├── app ├── Lib │ ├── empty │ └── Event │ │ └── UsersEventListener.php ├── tmp │ ├── logs │ │ └── empty │ └── cache │ │ ├── models │ │ └── empty │ │ ├── sessions │ │ └── empty │ │ ├── views │ │ └── empty │ │ └── persistent │ │ └── empty ├── Test │ ├── Fixture │ │ └── empty │ └── Case │ │ ├── View │ │ └── Helper │ │ │ └── empty │ │ ├── Model │ │ └── Behavior │ │ │ └── empty │ │ └── Controller │ │ └── Component │ │ └── empty ├── webroot │ ├── files │ │ └── empty │ ├── favicon.ico │ ├── img │ │ ├── no-cover.png │ │ ├── icon-search.png │ │ ├── btn_donate_SM.gif │ │ └── flattr-badge-large.png │ ├── fonts │ │ ├── OpenSans-Bold.woff │ │ ├── OpenSans-Regular.woff │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── .htaccess │ ├── css │ │ ├── slider.css │ │ ├── notify.css │ │ ├── pace.css │ │ └── login.css │ └── js │ │ ├── albums.js │ │ ├── jquery.scroll.js │ │ ├── settings.js │ │ ├── lazyload.min.js │ │ └── jquery.list.js ├── Console │ ├── Templates │ │ └── empty │ ├── Command │ │ ├── Task │ │ │ └── empty │ │ └── AppShell.php │ ├── cake.bat │ ├── cake │ └── cake.php ├── Model │ ├── Behavior │ │ └── empty │ ├── Datasource │ │ └── empty │ ├── Song.php │ ├── Playlist.php │ ├── PlaylistMembership.php │ ├── AppModel.php │ ├── Rootpath.php │ └── Setting.php ├── View │ ├── Scaffolds │ │ └── empty │ ├── Layouts │ │ ├── xml │ │ │ └── default.ctp │ │ ├── js │ │ │ └── default.ctp │ │ ├── rss │ │ │ └── default.ctp │ │ ├── Emails │ │ │ ├── text │ │ │ │ └── default.ctp │ │ │ └── html │ │ │ │ └── default.ctp │ │ ├── installer.ctp │ │ ├── ajax.ctp │ │ ├── login.ctp │ │ ├── flash.ctp │ │ └── error.ctp │ ├── Elements │ │ ├── redirect.ctp │ │ ├── Flash │ │ │ ├── info.ctp │ │ │ ├── error.ctp │ │ │ └── success.ctp │ │ ├── pagination.ctp │ │ ├── add_menu.ctp │ │ ├── default_navbar.ctp │ │ └── admin_navbar.ctp │ ├── Songs │ │ ├── artists.ctp │ │ ├── search.ctp │ │ └── index.ctp │ ├── Helper │ │ ├── FileSizeHelper.php │ │ ├── AjaxHtmlHelper.php │ │ ├── AppHelper.php │ │ ├── ImageHelper.php │ │ └── BootstrapFormHelper.php │ ├── Emails │ │ ├── html │ │ │ ├── user_add.ctp │ │ │ ├── default.ctp │ │ │ └── send_token.ctp │ │ └── text │ │ │ └── default.ctp │ ├── PlaylistMemberships │ │ └── add.ctp │ ├── Errors │ │ ├── error500.ctp │ │ └── error400.ctp │ ├── Users │ │ ├── reset_password.ctp │ │ └── login.ctp │ └── Installers │ │ └── docker.ctp ├── Locale │ └── eng │ │ └── LC_MESSAGES │ │ └── empty ├── Plugin │ └── DebugKit │ │ ├── VERSION.txt │ │ ├── webroot │ │ └── img │ │ │ └── cake.icon.png │ │ ├── .gitignore │ │ ├── View │ │ ├── ToolbarAccess │ │ │ ├── sql_explain.ctp │ │ │ └── history_state.ctp │ │ ├── Elements │ │ │ ├── session_panel.ctp │ │ │ ├── variables_panel.ctp │ │ │ ├── include_panel.ctp │ │ │ ├── history_panel.ctp │ │ │ ├── log_panel.ctp │ │ │ ├── request_panel.ctp │ │ │ ├── sql_log_panel.ctp │ │ │ ├── debug_toolbar.ctp │ │ │ └── environment_panel.ctp │ │ └── Helper │ │ │ ├── SimpleGraphHelper.php │ │ │ ├── DebugTimerHelper.php │ │ │ └── FirePhpToolbarHelper.php │ │ ├── Test │ │ ├── test_app │ │ │ ├── View │ │ │ │ └── DebugKitTest │ │ │ │ │ └── request_action_render.ctp │ │ │ ├── Plugin │ │ │ │ └── DebugkitTestPlugin │ │ │ │ │ └── Lib │ │ │ │ │ └── Panel │ │ │ │ │ └── PluginTestPanel.php │ │ │ ├── Lib │ │ │ │ └── Panel │ │ │ │ │ └── TestPanel.php │ │ │ └── Controller │ │ │ │ └── DebugKitTestController.php │ │ └── Case │ │ │ ├── AllTestsTest.php │ │ │ ├── AllDebugKitTest.php │ │ │ ├── AllDebugKitViewTest.php │ │ │ ├── AllDebugKitWithoutViewTest.php │ │ │ ├── Lib │ │ │ ├── Panel │ │ │ │ ├── SqlLogPanelTest.php │ │ │ │ └── LogPanelTest.php │ │ │ ├── DebugMemoryTest.php │ │ │ └── DebugKitDebuggerTest.php │ │ │ ├── TestFireCake.php │ │ │ ├── Model │ │ │ ├── ToolbarAccessTest.php │ │ │ └── Behavior │ │ │ │ └── TimedBehaviorTest.php │ │ │ └── DebugkitGroupTestCase.php │ │ ├── Model │ │ ├── DebugKitAppModel.php │ │ ├── ToolbarAccess.php │ │ └── Behavior │ │ │ └── TimedBehavior.php │ │ ├── Controller │ │ └── DebugKitAppController.php │ │ ├── Lib │ │ ├── Panel │ │ │ ├── SessionPanel.php │ │ │ ├── VariablesPanel.php │ │ │ ├── TimerPanel.php │ │ │ ├── RequestPanel.php │ │ │ ├── LogPanel.php │ │ │ ├── SqlLogPanel.php │ │ │ ├── EnvironmentPanel.php │ │ │ └── HistoryPanel.php │ │ ├── Log │ │ │ └── Engine │ │ │ │ └── DebugKitLog.php │ │ ├── DebugPanel.php │ │ └── DebugMemory.php │ │ ├── composer.json │ │ ├── .travis.yml │ │ └── Console │ │ └── Command │ │ └── WhitespaceShell.php ├── .htaccess ├── Config │ ├── ciphers.php.default │ ├── Schema │ │ ├── sessions.sql │ │ ├── i18n.sql │ │ ├── db_acl.sql │ │ ├── sessions.php │ │ └── i18n.php │ ├── acl.ini.php │ ├── routes.php │ ├── email.php.default │ └── database.php.default ├── index.php └── Controller │ ├── ApiController.php │ ├── Component │ ├── UrlComponent.php │ ├── DateComponent.php │ └── SortComponent.php │ └── ImgController.php ├── docs ├── downloads │ └── empty ├── img │ └── install.png ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── about │ └── license.md ├── css │ └── style.css ├── user-guides │ ├── apache-virtual-host.md │ ├── how-to-upgrade.md │ ├── cli-tool.md │ ├── settings.md │ └── nginx-server-block.md ├── README.md └── index.md ├── .htaccess ├── .editorconfig ├── composer.json ├── .gitattributes ├── .gitignore ├── mkdocs.yml ├── index.php ├── CONTRIBUTING.md └── README.md /app/Lib/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/tmp/logs/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Test/Fixture/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/webroot/files/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/downloads/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Console/Templates/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Model/Behavior/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Model/Datasource/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/View/Scaffolds/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/tmp/cache/models/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/tmp/cache/sessions/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/tmp/cache/views/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Console/Command/Task/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Locale/eng/LC_MESSAGES/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Test/Case/View/Helper/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/tmp/cache/persistent/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Test/Case/Model/Behavior/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/VERSION.txt: -------------------------------------------------------------------------------- 1 | 2.2.3 2 | -------------------------------------------------------------------------------- /app/Test/Case/Controller/Component/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/View/Layouts/xml/default.ctp: -------------------------------------------------------------------------------- 1 | fetch('content'); ?> 2 | -------------------------------------------------------------------------------- /docs/img/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/docs/img/install.png -------------------------------------------------------------------------------- /app/webroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/favicon.ico -------------------------------------------------------------------------------- /app/webroot/img/no-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/img/no-cover.png -------------------------------------------------------------------------------- /app/webroot/img/icon-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/img/icon-search.png -------------------------------------------------------------------------------- /app/webroot/img/btn_donate_SM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/img/btn_donate_SM.gif -------------------------------------------------------------------------------- /app/webroot/fonts/OpenSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/fonts/OpenSans-Bold.woff -------------------------------------------------------------------------------- /app/webroot/fonts/OpenSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/fonts/OpenSans-Regular.woff -------------------------------------------------------------------------------- /app/webroot/img/flattr-badge-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/img/flattr-badge-large.png -------------------------------------------------------------------------------- /app/Plugin/DebugKit/webroot/img/cake.icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/Plugin/DebugKit/webroot/img/cake.icon.png -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/docs/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /app/View/Layouts/js/default.ctp: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/webroot/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/webroot/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/webroot/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/webroot/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sonerezh/sonerezh/HEAD/app/webroot/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteRule ^$ app/webroot/ [L] 4 | RewriteRule (.*) app/webroot/$1 [L] 5 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteRule ^$ webroot/ [L] 4 | RewriteRule (.*) webroot/$1 [L] 5 | -------------------------------------------------------------------------------- /app/View/Elements/redirect.ctp: -------------------------------------------------------------------------------- 1 | start('script'); ?> 2 | 5 | end(); ?> -------------------------------------------------------------------------------- /app/View/Elements/Flash/info.ctp: -------------------------------------------------------------------------------- 1 |
2 | 7 |
-------------------------------------------------------------------------------- /app/webroot/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | RewriteCond %{REQUEST_FILENAME} !-d 4 | RewriteCond %{REQUEST_FILENAME} !-f 5 | RewriteRule ^ index.php [L] 6 | 7 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/.gitignore: -------------------------------------------------------------------------------- 1 | *.diff 2 | *.err 3 | *.orig 4 | *.rej 5 | *.swo 6 | *.swp 7 | *.vi 8 | *~ 9 | .DS_Store 10 | .cache 11 | .project 12 | .settings 13 | .svn 14 | errors.err 15 | tags 16 | /nbproject/ -------------------------------------------------------------------------------- /app/View/Elements/Flash/error.ctp: -------------------------------------------------------------------------------- 1 |
2 | 7 |
-------------------------------------------------------------------------------- /app/View/Elements/Flash/success.ctp: -------------------------------------------------------------------------------- 1 |
2 | 7 |
-------------------------------------------------------------------------------- /app/View/Songs/artists.ctp: -------------------------------------------------------------------------------- 1 |
2 | element('artists_view'); ?> 3 | element('add_to_playlist'); ?> 4 | element('pagination'); ?> 5 |
-------------------------------------------------------------------------------- /app/Model/Song.php: -------------------------------------------------------------------------------- 1 | Rss->document( 10 | $this->Rss->channel( 11 | array(), $channel, $this->fetch('content') 12 | ) 13 | ); 14 | ?> 15 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/ToolbarAccess/sql_explain.ctp: -------------------------------------------------------------------------------- 1 | 2 | Html->tableHeaders($headers); 6 | echo $this->Html->tableCells($result); 7 | ?> 8 |
9 | units[$factor]; 13 | } 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /app/View/Helper/AjaxHtmlHelper.php: -------------------------------------------------------------------------------- 1 | 2 |
  • Paginator->hasPrev()) { echo 'class="disabled"'; } ?>>Paginator->prev('«',array('tag' => false)); ?>
  • 3 | Paginator->numbers(array( 4 | 'tag' => 'li', 5 | 'separator' => false, 6 | 'currentTag' => 'a', 7 | 'currentClass' => 'active' 8 | )); ?> 9 |
  • Paginator->hasNext()) { echo 'class="disabled"'; } ?>>Paginator->next('»',array('tag' => false)); ?>
  • 10 | -------------------------------------------------------------------------------- /app/Config/Schema/sessions.sql: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 4 | # 1785 E. Sahara Avenue, Suite 490-204 5 | # Las Vegas, Nevada 89104 6 | # 7 | # Licensed under The MIT License 8 | # For full copyright and license information, please see the LICENSE.txt 9 | # Redistributions of files must retain the above copyright notice. 10 | # MIT License (http://www.opensource.org/licenses/mit-license.php) 11 | 12 | CREATE TABLE cake_sessions ( 13 | id varchar(255) NOT NULL default '', 14 | data text, 15 | expires int(11) default NULL, 16 | PRIMARY KEY (id) 17 | ); -------------------------------------------------------------------------------- /app/View/Emails/html/user_add.ctp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 13 | 14 | 15 |
    5 | 6 |
    10 | 11 | Html->link(__('Log me in'), array('controller' => 'users', 'action' => 'login', 'full_base' => true)); ?> 12 |
    -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/test_app/View/DebugKitTest/request_action_render.ctp: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/View/PlaylistMemberships/add.ctp: -------------------------------------------------------------------------------- 1 | start('script'); ?> 2 | 16 | end(); ?> 17 | start('playlist_form');?> 18 | element('add_to_playlist');?> 19 | end();?> -------------------------------------------------------------------------------- /app/View/Emails/text/default.ctp: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /app/index.php: -------------------------------------------------------------------------------- 1 | array( 14 | 'dependent' => true 15 | ) 16 | ); 17 | 18 | public $validate = array( 19 | 'title' => array( 20 | 'notBlank' => array( 21 | 'rule' => 'notBlank', 22 | 'message' => 'The playlist must have a title') 23 | ) 24 | ); 25 | 26 | public function beforeSave($options = array()) { 27 | $this->data[$this->alias]['user_id'] = AuthComponent::user('id'); 28 | return true; 29 | } 30 | } -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/test_app/Plugin/DebugkitTestPlugin/Lib/Panel/PluginTestPanel.php: -------------------------------------------------------------------------------- 1 | 19 |

    20 | Toolbar->makeNeatArray($content); 21 | -------------------------------------------------------------------------------- /app/Model/PlaylistMembership.php: -------------------------------------------------------------------------------- 1 | data)) { 16 | $this->updateAll( 17 | array('PlaylistMembership.sort' => 'PlaylistMembership.sort - 1'), 18 | array( 19 | 'PlaylistMembership.playlist_id' => $this->data['PlaylistMembership']['playlist_id'], 20 | 'PlaylistMembership.sort >' => $this->data['PlaylistMembership']['sort'] 21 | ) 22 | ); 23 | } 24 | } 25 | 26 | 27 | } -------------------------------------------------------------------------------- /app/webroot/css/slider.css: -------------------------------------------------------------------------------- 1 | .slider{ 2 | display: block; 3 | position: relative; 4 | height: 7px; 5 | width: 100%; 6 | background-color: #DADADA; 7 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); 8 | } 9 | .slider .slidebar, .slider .bufbar{ 10 | position: absolute; 11 | height: 7px; 12 | width: 0; 13 | background-color: #428bca; 14 | } 15 | .slider .bufbar{ 16 | background-color: rgba(0, 0, 0, .15); 17 | } 18 | .slider, .slidebar, .bufbar{ 19 | border-radius: 7px; 20 | } 21 | .slider .handle{ 22 | cursor: pointer; 23 | position: absolute; 24 | height: 15px; 25 | width: 15px; 26 | border-radius: 15px; 27 | margin-left: -7px; 28 | top: -5px; 29 | left: 0; 30 | background-color: #f7f7f7; 31 | box-shadow: 0 1px 1px white inset, 0 1px 2px rgba(0, 0, 0, .3); 32 | } -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family:'Glyphicons Halflings'; 3 | src:url("../fonts/glyphicons-halflings-regular.eot"); 4 | src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"), url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"), url("../fonts/glyphicons-halflings-regular.woff") format("woff"), url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"), url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg"); 5 | } 6 | 7 | div.col-md-9 h1:first-of-type { 8 | text-align: center; 9 | } 10 | 11 | div.col-md-9>p:first-of-type { 12 | text-align: center; 13 | } 14 | 15 | .icon { 16 | font-family:'Glyphicons Halflings'; 17 | } 18 | 19 | .icon.heart::before { 20 | content:"\e005"; 21 | } 22 | 23 | footer .icon { 24 | color:red; 25 | } 26 | -------------------------------------------------------------------------------- /docs/user-guides/apache-virtual-host.md: -------------------------------------------------------------------------------- 1 | # Apache virtual host 2 | 3 | This is a minimalist configuration sample for Apache2. 4 | 5 | ```apacheconfig 6 | 7 | ServerName demo.sonerezh.bzh 8 | DocumentRoot /var/www/sonerezh 9 | 10 | 11 | Options -Indexes 12 | AllowOverride All 13 | 14 | # Apache 2.2.x 15 | 16 | Order Allow,Deny 17 | Allow from all 18 | 19 | 20 | # Apache 2.4.x 21 | 22 | Require all granted 23 | 24 | 25 | 26 | CustomLog /var/log/apache2/demo.sonerezh.bzh-access.log "Combined" 27 | ErrorLog /var/log/apache2/demo.sonerezh.bzh-error.log 28 | 29 | ``` -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Model/DebugKitAppModel.php: -------------------------------------------------------------------------------- 1 | 19 | fetch('content'); ?> 20 | 21 | This email was sent using the CakePHP Framework, http://cakephp.org. 22 | -------------------------------------------------------------------------------- /app/webroot/css/notify.css: -------------------------------------------------------------------------------- 1 | #notify{ 2 | position: fixed; 3 | width: 300px; 4 | background-color: #2299dd; 5 | color: white; 6 | left: 50%; 7 | top:-5px; 8 | text-align: center; 9 | padding: 7px 0 2px; 10 | z-index: 1100; 11 | margin-left: -150px; 12 | border-radius: 3px; 13 | -webkit-animation: slideDown .3s; 14 | animation: slideDown .3s; 15 | } 16 | #notify.slideUp{ 17 | top:-100px; 18 | -webkit-animation: slideUp .3s; 19 | animation: slideUp .3s; 20 | } 21 | 22 | @-webkit-keyframes slideDown 23 | { 24 | from {top: -20px;} 25 | to {top: -5px;} 26 | } 27 | @keyframes slideDown 28 | { 29 | from {top: -20px;} 30 | to {top: -5px;} 31 | } 32 | @-webkit-keyframes slideUp 33 | { 34 | from {top: -5px;} 35 | to {top: -100px;} 36 | } 37 | @keyframes slideUp 38 | { 39 | from {top: -5px;} 40 | to {top: -100px;} 41 | } -------------------------------------------------------------------------------- /app/Controller/ApiController.php: -------------------------------------------------------------------------------- 1 | modelClass = "Song"; 18 | $this->modelKey = Inflector::underscore($this->modelClass); 19 | } 20 | 21 | public function beforeFilter() { 22 | $this->viewClass = "Json"; 23 | } 24 | 25 | public function json($one, $two = null) { 26 | parent::set($one, $two); 27 | $this->jsonVars = $this->viewVars; 28 | } 29 | 30 | public function beforeRender() { 31 | $this->set('_serialize', array_keys($this->jsonVars)); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/View/Emails/html/default.ctp: -------------------------------------------------------------------------------- 1 | 19 | ' . $line . "

    \n"; 24 | endforeach; 25 | ?> -------------------------------------------------------------------------------- /app/Controller/Component/UrlComponent.php: -------------------------------------------------------------------------------- 1 | 19 |

    20 | validationErrors'] = $this->validationErrors; 22 | $content['Loaded Helpers'] = $this->Helpers->attached(); 23 | echo $this->Toolbar->makeNeatArray($content); 24 | -------------------------------------------------------------------------------- /app/Console/Command/AppShell.php: -------------------------------------------------------------------------------- 1 | testPanel = true; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/Config/Schema/i18n.sql: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 4 | # 5 | # Licensed under The MIT License 6 | # For full copyright and license information, please see the LICENSE.txt 7 | # Redistributions of files must retain the above copyright notice. 8 | # MIT License (http://www.opensource.org/licenses/mit-license.php) 9 | 10 | CREATE TABLE i18n ( 11 | id int(10) NOT NULL auto_increment, 12 | locale varchar(6) NOT NULL, 13 | model varchar(255) NOT NULL, 14 | foreign_key int(10) NOT NULL, 15 | field varchar(255) NOT NULL, 16 | content mediumtext, 17 | PRIMARY KEY (id), 18 | # UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), 19 | # INDEX I18N_LOCALE_ROW(locale, model, foreign_key), 20 | # INDEX I18N_LOCALE_MODEL(locale, model), 21 | # INDEX I18N_FIELD(model, foreign_key, field), 22 | # INDEX I18N_ROW(model, foreign_key), 23 | INDEX locale (locale), 24 | INDEX model (model), 25 | INDEX row_id (foreign_key), 26 | INDEX field (field) 27 | ); -------------------------------------------------------------------------------- /app/View/Errors/error500.ctp: -------------------------------------------------------------------------------- 1 | 19 |

    20 |

    21 | : 22 | 23 |

    24 | 0): 26 | echo $this->element('exception_stack_trace'); 27 | endif; 28 | ?> 29 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/ToolbarAccess/history_state.ctp: -------------------------------------------------------------------------------- 1 | $panel) { 21 | if (!empty($panel) && !empty($panel['elementName'])) { 22 | $panels[$panelName] = $this->element($panel['elementName'], array( 23 | 'content' => $panel['content'] 24 | ), array( 25 | 'plugin' => Inflector::camelize($panel['plugin']) 26 | )); 27 | } 28 | } 29 | echo json_encode($panels); 30 | Configure::write('debug', 0); 31 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/SessionPanel.php: -------------------------------------------------------------------------------- 1 | Toolbar->Session->read(); 36 | return $sessions; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/View/Emails/html/send_token.ctp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 25 | 26 | 27 |
    5 | 6 |
    10 | 11 | Html->link(__('link'), array( 12 | 'controller' => 'users', 13 | 'action' => 'resetPassword', 14 | '?' => array('t' => $token), 15 | 'full_base' => true 16 | ) 17 | ).__(' or copy and paste the following URL in your browser: ').$this->Html->Url(array( 18 | 'controller' => 'users', 19 | 'action' => 'resetPassword', 20 | '?' => array('t' => $token) 21 | ), true 22 | ); 23 | ?> 24 |
    28 | -------------------------------------------------------------------------------- /app/View/Errors/error400.ctp: -------------------------------------------------------------------------------- 1 | 19 |

    20 |

    21 | : 22 | '{$url}'" 25 | ); ?> 26 |

    27 | 0): 29 | echo $this->element('exception_stack_trace'); 30 | endif; 31 | ?> 32 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/VariablesPanel.php: -------------------------------------------------------------------------------- 1 | viewVars, array('$request->data' => $controller->request->data)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/include_panel.ctp: -------------------------------------------------------------------------------- 1 | 19 |

    20 | 21 |

    Include Paths

    22 | $path) { 24 | if (strstr($path, CAKE)) { 25 | $content['paths'][$i] = '-> ' . $path; 26 | break; 27 | } 28 | } 29 | echo $this->Toolbar->makeNeatArray(array_filter($content['paths'])); 30 | unset($content['paths']); 31 | ?> 32 | 33 |

    Included Files

    34 | Toolbar->makeNeatArray($content); -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Sonerezh 2 | site_url: https://www.sonerezh.bzh 3 | site_description: The Sonerezh project documentation. 4 | site_author: The Sonerezh team. 5 | 6 | repo_url: https://github.com/Sonerezh/sonerezh 7 | 8 | nav: 9 | - Home: index.md 10 | - User Guides: 11 | - How-to upgrade: user-guides/how-to-upgrade.md 12 | - Command-line Tool: user-guides/cli-tool.md 13 | - Settings Reference: user-guides/settings.md 14 | - Nginx server-block: user-guides/nginx-server-block.md 15 | - Apache virtual host: user-guides/apache-virtual-host.md 16 | - About: 17 | - License: about/license.md 18 | - Download Sonerezh: downloads/latest.tar.gz 19 | - Demo: demo/ 20 | 21 | extra_css: 22 | - css/style.css 23 | 24 | theme: 25 | name: mkdocs 26 | custom_dir: docs/sonerezh_theme/ 27 | 28 | matomo_analytics: 1 29 | 30 | use_directory_urls: false 31 | 32 | markdown_extensions: 33 | - admonition 34 | 35 | copyright: 'Made in Brittany (FR) with by Guillaume & Thomas - hey [at] sonerezh [dot] bzh' 36 | -------------------------------------------------------------------------------- /app/Console/cake.bat: -------------------------------------------------------------------------------- 1 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 2 | :: 3 | :: Bake is a shell script for running CakePHP bake script 4 | :: 5 | :: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) 6 | :: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 7 | :: 8 | :: Licensed under The MIT License 9 | :: Redistributions of files must retain the above copyright notice. 10 | :: 11 | :: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 12 | :: @link http://cakephp.org CakePHP(tm) Project 13 | :: @package app.Console 14 | :: @since CakePHP(tm) v 2.0 15 | :: @license http://www.opensource.org/licenses/mit-license.php MIT License 16 | :: 17 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 18 | 19 | :: In order for this script to work as intended, the cake\console\ folder must be in your PATH 20 | 21 | @echo. 22 | @echo off 23 | 24 | SET app=%0 25 | SET lib=%~dp0 26 | 27 | php -q "%lib%cake.php" -working "%CD% " %* 28 | 29 | echo. 30 | 31 | exit /B %ERRORLEVEL% 32 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cakephp/debug_kit", 3 | "description": "CakePHP Debug Kit", 4 | "type": "cakephp-plugin", 5 | "keywords": ["cakephp", "debug", "kit"], 6 | "homepage": "https://github.com/cakephp/debug_kit", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Mark Story", 11 | "homepage": "http://mark-story.com", 12 | "role": "Author" 13 | }, 14 | { 15 | "name": "CakePHP Community", 16 | "homepage": "https://github.com/cakephp/debug_kit/graphs/contributors" 17 | } 18 | ], 19 | "support": { 20 | "issues": "https://github.com/cakephp/debug_kit/issues", 21 | "forum": "http://stackoverflow.com/tags/cakephp", 22 | "irc": "irc://irc.freenode.org/cakephp", 23 | "source": "https://github.com/cakephp/debug_kit" 24 | }, 25 | "require": { 26 | "php": ">=5.3.0", 27 | "composer/installers": "*" 28 | }, 29 | "extra": { 30 | "branch-alias": { 31 | "dev-master": "2.2.x-dev" 32 | }, 33 | "installer-name": "DebugKit" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/View/Helper/AppHelper.php: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 |
    19 | element('artists_view'); ?> 20 | element('add_to_playlist'); ?> 21 | element('pagination'); ?> 22 |
    23 | 24 | -------------------------------------------------------------------------------- /app/View/Helper/ImageHelper.php: -------------------------------------------------------------------------------- 1 | Html->image($path, $options); 19 | return str_replace('src="', 'src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" onload="lzldhd(this)" data-src="', $image); 20 | } 21 | 22 | public function avatar($auth, $size = null) { 23 | $image = $auth['avatar']; 24 | if (empty($image)) { 25 | $image = 'https://secure.gravatar.com/avatar/'.md5($auth['email']).'.png?r=x&s='.$size; 26 | } else { 27 | $image = $this->resizedPath("avatars".DS.$auth['avatar'], $size, $size); 28 | } 29 | return $image; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/View/Users/reset_password.ctp: -------------------------------------------------------------------------------- 1 |
    2 | Form->create('User'); ?> 3 |
    4 |

    Sonerezh -

    5 |
    6 |

    7 | '.$user['User']['email'].'.'; ?> 8 | 9 |

    10 | Form->input('password', array('placeholder' => __('Email Address'), 'required', 'label' => __('New Password'))); ?> 11 | Form->input('confirm_password', array('placeholder' => __('Password'), 'required', 'type' => 'password', 'label' => __('Confirm the new password'))); ?> 12 |
    13 |
    14 |
    15 | Form->submit(__('Reset my password'), array('class' => 'btn btn-lg btn-success btn-block')); ?> 16 |
    17 |
    18 |
    19 | Form->end(); ?> 20 |
    -------------------------------------------------------------------------------- /app/View/Layouts/installer.ctp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Html->charset(); ?> 5 | 6 | 7 | 8 | <?php echo $title_for_layout; ?> 9 | 10 | Html->meta('icon'); 12 | echo $this->Html->css(array('bootstrap.min', 'jquery.fs.selecter.min')); 13 | echo $this->fetch('meta'); 14 | echo $this->fetch('css'); 15 | ?> 16 | 21 | 22 | 23 | 24 |
    25 |
    26 | Flash->render(); ?> 27 |
    28 |
    29 | fetch('content'); ?> 30 |
    31 |
    32 | 35 | Html->script(array('jquery-2.1.0.min', 'bootstrap.min', 'jquery.fs.selecter.min')); 37 | echo $this->fetch('script'); 38 | ?> 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/AllTestsTest.php: -------------------------------------------------------------------------------- 1 | getTestFiles(); 36 | $suite->addTestFiles($files); 37 | 38 | return $suite; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/AllDebugKitTest.php: -------------------------------------------------------------------------------- 1 | getTestFiles(); 36 | $suite->addTestFiles($files); 37 | 38 | return $suite; 39 | } 40 | } -------------------------------------------------------------------------------- /app/Model/AppModel.php: -------------------------------------------------------------------------------- 1 | 19 | [{ 20 | "css": fetch('css')); ?>, 21 | "js": fetch('script')); ?> 22 | }, 23 | { 24 | "title": "", 25 | "url": "request->here(); ?>" 26 | }, 27 | { 28 | "flash": Flash->render()); ?>, 29 | "html": fetch('content')); ?>, 30 | "playlist_form": fetch('playlist_form'));?> 31 | }] 32 | -------------------------------------------------------------------------------- /app/Model/Rootpath.php: -------------------------------------------------------------------------------- 1 | array( 9 | 'pathExists' => array( 10 | 'rule' => 'pathExists', 11 | 'message' => "The music library's directory does not exist." 12 | ), 13 | 'isReadable' => array( 14 | 'rule' => 'isReadable', 15 | 'message' => "The music library's directory is not readable." 16 | ), 17 | ) 18 | ); 19 | 20 | public function pathExists($options = array()) { 21 | if (!file_exists($this->data[$this->alias]['rootpath'])) { 22 | return false; 23 | } 24 | return true; 25 | } 26 | 27 | public function isReadable($options = array()) { 28 | if (!is_readable($this->data[$this->alias]['rootpath'])) { 29 | return false; 30 | } 31 | 32 | if (preg_match('/^\s/', $this->data[$this->alias]['rootpath']) || preg_match('/\s$/', $this->data[$this->alias]['rootpath'])) { 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/AllDebugKitViewTest.php: -------------------------------------------------------------------------------- 1 | getTestFiles('View'); 36 | $suite->addTestFiles($files); 37 | 38 | return $suite; 39 | } 40 | } -------------------------------------------------------------------------------- /app/View/Layouts/login.ctp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Html->charset(); ?> 5 | 6 | 7 | 8 | <?php echo $title_for_layout; ?> 9 | 10 | Html->meta('icon'); ?> 11 | Html->css(array('bootstrap.min', 'login')); ?> 12 | fetch('meta'); 15 | echo $this->fetch('css'); 16 | ?> 17 | 18 | 19 |
    20 |
    21 |
    22 | Flash->render(); ?> 23 |
    24 |
    25 |
    26 | fetch('content'); ?> 27 |
    28 |
    29 | Html->script(array( 30 | "jquery-2.1.0.min", 31 | "bootstrap.min")); ?> 32 | fetch('script'); ?> 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/AllDebugKitWithoutViewTest.php: -------------------------------------------------------------------------------- 1 | getTestFiles(null, 'View'); 36 | $suite->addTestFiles($files); 37 | 38 | return $suite; 39 | } 40 | } -------------------------------------------------------------------------------- /app/View/Layouts/flash.ctp: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | Html->charset(); ?> 23 | <?php echo $page_title; ?> 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 |

    36 | 37 | 38 | -------------------------------------------------------------------------------- /app/Controller/Component/DateComponent.php: -------------------------------------------------------------------------------- 1 | > 9) & 0b01111111; 36 | $month = ($date >> 5) & 0b00001111; 37 | $day = ($date) & 0b00011111; 38 | 39 | return [$year, $month, $day]; 40 | } 41 | } -------------------------------------------------------------------------------- /app/View/Elements/add_menu.ctp: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/TimerPanel.php: -------------------------------------------------------------------------------- 1 | helpers)))) { 36 | $controller->helpers[] = 'Number'; 37 | } 38 | if (!in_array('SimpleGraph', array_keys(HelperCollection::normalizeObjectArray($controller->helpers)))) { 39 | $controller->helpers[] = 'DebugKit.SimpleGraph'; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/webroot/css/pace.css: -------------------------------------------------------------------------------- 1 | .pace .pace-progress { 2 | background-image: -webkit-linear-gradient(left, #c4e17f, #c4e17f 12.5%, #f7fdca 12.5%, #f7fdca 25%, #fecf71 25%, #fecf71 37.5%, #f0776c 37.5%, #f0776c 50%, #db9dbe 50%, #db9dbe 62.5%, #c49cde 62.5%, #c49cde 75%, #669ae1 75%, #669ae1 87.5%, #62c2e4 87.5%, #62c2e4); 3 | background-image: -moz-linear-gradient(left, #c4e17f, #c4e17f 12.5%, #f7fdca 12.5%, #f7fdca 25%, #fecf71 25%, #fecf71 37.5%, #f0776c 37.5%, #f0776c 50%, #db9dbe 50%, #db9dbe 62.5%, #c49cde 62.5%, #c49cde 75%, #669ae1 75%, #669ae1 87.5%, #62c2e4 87.5%, #62c2e4); 4 | background-image: -o-linear-gradient(left, #c4e17f, #c4e17f 12.5%, #f7fdca 12.5%, #f7fdca 25%, #fecf71 25%, #fecf71 37.5%, #f0776c 37.5%, #f0776c 50%, #db9dbe 50%, #db9dbe 62.5%, #c49cde 62.5%, #c49cde 75%, #669ae1 75%, #669ae1 87.5%, #62c2e4 87.5%, #62c2e4); 5 | background-image: linear-gradient(to right, #c4e17f, #c4e17f 12.5%, #f7fdca 12.5%, #f7fdca 25%, #fecf71 25%, #fecf71 37.5%, #f0776c 37.5%, #f0776c 50%, #db9dbe 50%, #db9dbe 62.5%, #c49cde 62.5%, #c49cde 75%, #669ae1 75%, #669ae1 87.5%, #62c2e4 87.5%, #62c2e4); 6 | position: fixed; 7 | z-index: 2000; 8 | top: 0; 9 | left: 0; 10 | height: 3px; 11 | 12 | -webkit-transition: width 1s; 13 | -moz-transition: width 1s; 14 | -o-transition: width 1s; 15 | transition: width 1s; 16 | } 17 | 18 | .pace-inactive { 19 | display: none; 20 | } -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/history_panel.ctp: -------------------------------------------------------------------------------- 1 | 19 |

    20 | 21 |

    22 | 23 | 24 | 32 | logger = $this; 39 | } 40 | 41 | /** 42 | * Captures log messages in memory 43 | * 44 | * @param $type 45 | * @param $message 46 | * @return void 47 | */ 48 | public function write($type, $message) { 49 | if (!isset($this->logs[$type])) { 50 | $this->logs[$type] = array(); 51 | } 52 | $this->logs[$type][] = array(date('Y-m-d H:i:s'), $message); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/RequestPanel.php: -------------------------------------------------------------------------------- 1 | request->params; 37 | $out['url'] = $controller->request->url; 38 | $out['query'] = $controller->request->query; 39 | $out['data'] = $controller->request->data; 40 | if (isset($controller->Cookie)) { 41 | $out['cookie'] = $controller->Cookie->read(); 42 | } 43 | $out['get'] = $_GET; 44 | $out['currentRoute'] = Router::currentRoute(); 45 | return $out; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/LogPanel.php: -------------------------------------------------------------------------------- 1 | 'FileLog' 37 | )); 38 | } 39 | CakeLog::config('debug_kit_log_panel', array( 40 | 'engine' => 'DebugKit.DebugKitLog', 41 | 'panel' => $this 42 | )); 43 | } 44 | 45 | /** 46 | * beforeRender Callback 47 | * 48 | * @param Controller $controller 49 | * @return array 50 | */ 51 | public function beforeRender(Controller $controller) { 52 | $logger = $this->logger; 53 | return $logger; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docs/user-guides/how-to-upgrade.md: -------------------------------------------------------------------------------- 1 | # Upgrading 2 | 3 | Just install it again… 4 | 5 | --- 6 | 7 | We do not provide any automation tool to upgrade Sonerezh. The most efficient 8 | way is to move your existent installation to another directory 9 | (``sonerezh.old`` for instance), make a fresh installation and copy some 10 | configuration data from the old directory. 11 | 12 | Let's suppose our current installation is located into ``/srv/sonerezh``. First, 13 | we rename it. 14 | 15 | ```text 16 | $ mv /src/sonerezh /srv/sonerezh.old 17 | ``` 18 | 19 | Then we can download the latest release and _untar_ it. 20 | 21 | ```text 22 | $ wget https://www.sonerezh.bzh/downloads/latest.tar.gz 23 | $ tar -zxf latest.tar.gz 24 | ``` 25 | 26 | We need to copy: 27 | 28 | - The database configuration file (``database.php``) 29 | - The album artworks, and the user's avatars 30 | - If you used the email function, the email configuration files (``email.php``) 31 | 32 | ```text 33 | $ cp -a /srv/sonerezh.old/app/Config/database.php /srv/sonerezh/app/Config/database.php 34 | $ cp -a /srv/sonerezh.old/webroot/img/thumbnails /srv/sonerezh.old/webroot/img/ 35 | $ cp -a /srv/sonerezh.old/webroot/img/resized /srv/sonerezh.old/webroot/img/ 36 | $ cp -a /srv/sonerezh.old/webroot/img/avatars /srv/sonerezh.old/webroot/img/ 37 | $ cp -a /srv/sonerezh.old/app/Config/email.php /srv/sonerezh/app/Config/email.php 38 | ``` 39 | 40 | Don't forget to check the permissions on the new folders. 41 | 42 | ```text 43 | $ chown -R www-data: /srv/sonerezh 44 | ``` -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/log_panel.ctp: -------------------------------------------------------------------------------- 1 | 19 |

    20 |
    21 | 22 | logs as $logName => $logs): ?> 23 |

    24 | 0): 27 | $headers = array(__d('debug_kit', 'Time'), __d('debug_kit', 'Message')); 28 | $rows = array(); 29 | for ($i = 0; $i < $len; $i++): 30 | $rows[] = array( 31 | $logs[$i][0], h($logs[$i][1]) 32 | ); 33 | endfor; 34 | echo $this->Toolbar->table($rows, $headers, array('title' => $logName)); 35 | endif; ?> 36 | 37 | logs)): ?> 38 |

    39 | 40 | 41 |
    42 | -------------------------------------------------------------------------------- /app/Config/Schema/sessions.php: -------------------------------------------------------------------------------- 1 | array('type' => 'string', 'null' => false, 'key' => 'primary'), 40 | 'data' => array('type' => 'text', 'null' => true, 'default' => null), 41 | 'expires' => array('type' => 'integer', 'null' => true, 'default' => null), 42 | 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) 43 | ); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/Console/cake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ################################################################################ 3 | # 4 | # Bake is a shell script for running CakePHP bake script 5 | # 6 | # CakePHP(tm) : Rapid Development Framework (http://cakephp.org) 7 | # Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 8 | # 9 | # Licensed under The MIT License 10 | # For full copyright and license information, please see the LICENSE.txt 11 | # Redistributions of files must retain the above copyright notice. 12 | # 13 | # @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 14 | # @link http://cakephp.org CakePHP(tm) Project 15 | # @package app.Console 16 | # @since CakePHP(tm) v 1.2.0.5012 17 | # @license http://www.opensource.org/licenses/mit-license.php MIT License 18 | # 19 | ################################################################################ 20 | 21 | # Canonicalize by following every symlink of the given name recursively 22 | canonicalize() { 23 | NAME="$1" 24 | if [ -f "$NAME" ] 25 | then 26 | DIR=$(dirname -- "$NAME") 27 | NAME=$(cd -P "$DIR" && pwd -P)/$(basename -- "$NAME") 28 | fi 29 | while [ -h "$NAME" ]; do 30 | DIR=$(dirname -- "$NAME") 31 | SYM=$(readlink "$NAME") 32 | NAME=$(cd "$DIR" && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM") 33 | done 34 | echo "$NAME" 35 | } 36 | 37 | CONSOLE=$(dirname -- "$(canonicalize "$0")") 38 | APP=$(dirname "$CONSOLE") 39 | 40 | exec php -q "$CONSOLE"/cake.php -working "$APP" "$@" 41 | exit 42 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/test_app/Controller/DebugKitTestController.php: -------------------------------------------------------------------------------- 1 | autoRender = false; 54 | return 'I am some value from requestAction.'; 55 | } 56 | 57 | /** 58 | * Render Request Action 59 | */ 60 | public function request_action_render() { 61 | $this->set('test', 'I have been rendered.'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Controller/ImgController.php: -------------------------------------------------------------------------------- 1 | Image->resize($path, $resized, $dimensions[0], $dimensions[1]); 43 | } 44 | 45 | $this->response->file($resized); 46 | return $this->response; 47 | } 48 | } -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/Lib/Panel/SqlLogPanelTest.php: -------------------------------------------------------------------------------- 1 | panel = new SqlLogPanel(); 42 | } 43 | 44 | /** 45 | * test the parsing of source list. 46 | * 47 | * @return void 48 | */ 49 | public function testBeforeRender() { 50 | $Article = ClassRegistry::init('Article'); 51 | $Article->find('first', array('conditions' => array('Article.id' => 1))); 52 | 53 | $controller = new Controller(); 54 | $result = $this->panel->beforeRender($controller); 55 | 56 | $this->assertTrue(isset($result['connections'][$Article->useDbConfig])); 57 | $this->assertTrue(isset($result['threshold'])); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 23 | > A paragaph describing what changed and its impact." 24 | 25 | ## Coding convention 26 | 27 | Start reading our code and you'll get the hang of it. We optimize for 28 | readability: 29 | 30 | * We indent using four spaces (no tabs) 31 | * As Sonerezh is built with CakePHP, we follow [their conventions] 32 | * This is open source software. Consider the people who will read your code, and 33 | make it look nice for them. It's sort of like driving a car: Perhaps you love 34 | doing donuts when you're alone, but with passengers the goal is to make the 35 | ride as smooth as possible. 36 | 37 | Thanks, 38 | Guillaume and Thomas 39 | 40 | [The official documentation]: (https://www.sonerezh.bzh) 41 | [GitHub Pull Request to Sonerezh]: (https://github.com/Sonerezh/sonerezh/pulls) 42 | [pull requests]: (http://help.github.com/pull-requests/) 43 | [their conventions]: (http://book.cakephp.org/2.0/en/getting-started/cakephp-conventions.html) -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/TestFireCake.php: -------------------------------------------------------------------------------- 1 | sentHeaders[$name] = $value; 44 | } 45 | 46 | /** 47 | * Skip client detection as headers are not being sent. 48 | * 49 | * @return boolean Always true 50 | */ 51 | public static function detectClientExtension() { 52 | return true; 53 | } 54 | 55 | /** 56 | * Reset FireCake 57 | * 58 | * @return void 59 | **/ 60 | public static function reset() { 61 | $_this = FireCake::getInstance(); 62 | $_this->sentHeaders = array(); 63 | $_this->_messageIndex = 1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/request_panel.ctp: -------------------------------------------------------------------------------- 1 | 19 |

    20 | 21 |

    Cake Params

    22 | Toolbar->makeNeatArray($content['params']); ?> 23 | 24 |

    Post data

    25 | ' . __d('debug_kit', 'No post data.') . '

    '; 28 | else: 29 | echo $this->Toolbar->makeNeatArray($content['data']); 30 | endif; 31 | ?> 32 | 33 |

    Query string

    34 | ' . __d('debug_kit', 'No querystring data.') . '

    '; 37 | else: 38 | echo $this->Toolbar->makeNeatArray($content['query']); 39 | endif; 40 | ?> 41 | 42 |

    Cookie

    43 | 44 | Toolbar->makeNeatArray($content['cookie']); ?> 45 | 46 |

    To view Cookies, add CookieComponent to Controller

    47 | 48 | 49 |

    50 | Toolbar->makeNeatArray($content['currentRoute']); 51 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/Lib/Panel/LogPanelTest.php: -------------------------------------------------------------------------------- 1 | panel = new LogPanel(); 35 | } 36 | 37 | /** 38 | * Test that logging configs are created. 39 | * 40 | * @return void 41 | */ 42 | public function testConstructor() { 43 | $result = CakeLog::configured(); 44 | $this->assertContains('debug_kit_log_panel', $result); 45 | $this->assertTrue(count($result) > 1, 'Default loggers were not added.'); 46 | } 47 | 48 | /** 49 | * testBeforeRender 50 | * 51 | * @return void 52 | */ 53 | public function testBeforeRender() { 54 | $controller = new Controller(); 55 | 56 | CakeLog::write('error', 'Test'); 57 | 58 | $result = $this->panel->beforeRender($controller); 59 | $this->assertInstanceOf('DebugKitLog', $result); 60 | $this->assertTrue(isset($result->logs)); 61 | $this->assertCount(1, $result->logs['error']); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sonerezh 2 | 3 | Sonerezh is a self-hosted web application which allows you to listen to your 4 | music, from anywhere. 5 | 6 | All you have to do is to specify where your music is stored, and Sonerezh will 7 | build its database based on the audio file's metadata. Then you can browse your 8 | music library through a simple and intuitive Web UI. 9 | 10 | :arrow_right: Let's try the latest version on [sonerezh.bzh/demo]! 11 | 12 | Follow us on [Twitter] if you like the project, and don't forget to [support it 13 | making a donation]. 14 | 15 | The **standard installation instruction are available in the documentation** on 16 | [sonerezh.bzh]. If you want to contribute to the project or if you prefer to use 17 | Git and Composer you can follow the steps below. 18 | 19 | ## Installation using Git and Composer (for developers) 20 | 21 | You must have [Composer] installed and ready to download the Sonerezh's 22 | dependencies. You will also need PHP (obviously) and at least ``php-mysql`` or 23 | ``php-pgsql`` and ``php-gd``. 24 | 25 | 1. Download the sources: 26 | 27 | ```sh 28 | $ git clone https://github.com/Sonerezh/sonerezh.git 29 | ``` 30 | 31 | 2. Download the dependencies: 32 | 33 | ```sh 34 | $ cd sonerezh 35 | $ composer install 36 | ``` 37 | 38 | 3. You should be good to run Sonerezh using: 39 | 40 | ```sh 41 | $ cd app/webroot 42 | $ CAKEPHP_DEBUG=1 php -S localhost:8080 43 | ``` 44 | 45 | _Note: you may have some issues to display the cover arts using the built-in PHP 46 | server._ 47 | 48 | [sonerezh.bzh/demo]: https://www.sonerezh.bzh/demo/login 49 | [Twitter]: https://twitter.com/snrzh 50 | [support it making a donation]: https://www.sonerezh.bzh/donate 51 | [sonerezh.bzh]: https://www.sonerezh.bzh 52 | [Composer]: https://getcomposer.org/ -------------------------------------------------------------------------------- /app/webroot/js/albums.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | $('#content').off('click', '.action-expend'); 4 | $('#content').on('click', '.action-expend', function(){ 5 | var band = $(this).attr('data-band'); 6 | var album = $(this).attr('data-album'); 7 | var $this = $(this); 8 | 9 | $('#album-expended').remove(); 10 | if($this.hasClass('active')){ 11 | $('.action-expend').css('margin-bottom', 0); 12 | setTimeout(function(){ 13 | $this.removeClass('active'); 14 | }, 200); 15 | return; 16 | } 17 | $('.action-expend').removeClass('active').css('margin-bottom', 0); 18 | $(this).addClass('loading'); 19 | $(this).prepend('
    '); 20 | 21 | $.ajax({ 22 | url: baseurl + "/album", 23 | data: "band=" + encodeURIComponent(band) + "&album=" + encodeURIComponent(album), 24 | success: function(response){ 25 | var $html = $(response[2].html); 26 | $html.css('top', $this.offset().top+$this.height()).addClass('animated flipInX'); 27 | $html.find('.close-album').click(function(){ 28 | $('#album-expended').remove(); 29 | $('.action-expend').css('margin-bottom', 0); 30 | setTimeout(function(){ 31 | $this.removeClass('active'); 32 | }, 200); 33 | }); 34 | $("#content").append($html); 35 | updateSelectedSong(); 36 | $this.toggleClass('active loading').css('margin-bottom', $html.height()+20); 37 | $this.find('.loader').remove(); 38 | } 39 | }); 40 | }); 41 | }); -------------------------------------------------------------------------------- /app/Console/cake.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -q 2 | 2 |
    3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 |
    #
    17 | 18 | 23 | 24 | element('add_menu', array('song_id' => h($song['Song']['id']), 'song_title' => h($song['Song']['title']))); ?> 25 |
    30 |
    31 | element('add_to_playlist'); ?> 32 | element('pagination'); ?> 33 | 34 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/Model/ToolbarAccessTest.php: -------------------------------------------------------------------------------- 1 | Model = new ToolbarAccess(); 43 | } 44 | 45 | /** 46 | * tearDown 47 | * 48 | * @return void 49 | */ 50 | public function tearDown() { 51 | parent::tearDown(); 52 | unset($this->Model); 53 | } 54 | 55 | /** 56 | * test that explain query returns arrays of query information. 57 | * 58 | * @return void 59 | */ 60 | public function testExplainQuery() { 61 | $Post = new CakeTestModel(array('table' => 'posts', 'alias' => 'Post')); 62 | $db = $Post->getDataSource(); 63 | $sql = 'SELECT * FROM ' . $db->fullTableName('posts') . ';'; 64 | $result = $this->Model->explainQuery($Post->useDbConfig, $sql); 65 | 66 | $this->assertTrue(is_array($result)); 67 | $this->assertFalse(empty($result)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/View/Layouts/error.ctp: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 24 | Html->charset(); ?> 25 | 26 | <?php echo $cakeDescription ?>: 27 | <?php echo $title_for_layout; ?> 28 | 29 | Html->meta('icon'); 31 | 32 | echo $this->Html->css('cake.generic'); 33 | 34 | echo $this->fetch('meta'); 35 | echo $this->fetch('css'); 36 | echo $this->fetch('script'); 37 | ?> 38 | 39 | 40 |
    41 | 44 |
    45 | 46 | Flash->render(); ?> 47 | 48 | fetch('content'); ?> 49 |
    50 | 58 |
    59 | element('sql_dump'); ?> 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/View/Installers/docker.ctp: -------------------------------------------------------------------------------- 1 |
    2 | 5 |

    6 | 7 |

    8 | 9 |

    10 |
    11 | 12 |

    13 | 14 |

    15 | 16 |
    17 | 18 | Form->create(null, array( 20 | 'class' => 'form-horizontal', 21 | 'inputDefaults' => array( 22 | 'label' => array('class' => 'col-sm-3 control-label'), 23 | 'div' => 'form-group', 24 | 'between' => '
    ', 25 | 'after' => '
    ', 26 | 'class' => 'form-control', 27 | 'error' => array('attributes' => array('wrap' => 'div', 'class' => 'text-danger col-sm-offset-3 col-sm-9')) 28 | ) 29 | )); 30 | echo $this->Form->input('User.email', array('placeholder' => 'john.doe@sonerezh.bzh', 'required')); 31 | echo $this->Form->input('User.password', array('placeholder' => __('Password'), 'label' => array('text' => __('Password (twice)'), 'class' => 'col-sm-3 control-label'), 'required')); 32 | echo $this->Form->input('User.confirm_password', array('placeholder' => __('Confirm your password'), 'type' => 'password', 'label' => array('text' => '', 'class' => 'col-sm-3 control-label'), 'required')); 33 | echo $this->Form->submit('Run!', array('class' => 'btn btn-success pull-right')); 34 | ?> 35 |
    36 | 37 | Form->end(); ?> 38 |
    -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/Lib/DebugMemoryTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(is_int($result)); 34 | 35 | $result = DebugMemory::getPeak(); 36 | $this->assertTrue(is_int($result)); 37 | } 38 | 39 | /** 40 | * test making memory use markers. 41 | * 42 | * @return void 43 | */ 44 | public function testMemorySettingAndGetting() { 45 | DebugMemory::clear(); 46 | $result = DebugMemory::record('test marker'); 47 | $this->assertTrue($result); 48 | 49 | $result = DebugMemory::getAll(true); 50 | $this->assertEquals(count($result), 1); 51 | $this->assertTrue(isset($result['test marker'])); 52 | $this->assertTrue(is_numeric($result['test marker'])); 53 | 54 | $result = DebugMemory::getAll(); 55 | $this->assertTrue(empty($result)); 56 | 57 | DebugMemory::record('test marker'); 58 | DebugMemory::record('test marker'); 59 | $result = DebugMemory::getAll(); 60 | 61 | $this->assertEquals(count($result), 2); 62 | $this->assertTrue(isset($result['test marker'])); 63 | $this->assertTrue(isset($result['test marker #2'])); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/webroot/js/jquery.scroll.js: -------------------------------------------------------------------------------- 1 | (function ($){ 2 | $.fn.infinitescroll = function(options){ 3 | 4 | var settings = $.extend({ 5 | nextSelector : '.next', 6 | nav : '.pagination', 7 | loadBefore: '100', 8 | callback: function(){} 9 | }, options ); 10 | 11 | var loading = false; 12 | 13 | $(window).scroll(function(){ 14 | $(settings.nav).hide(); 15 | if($('#scroll-loader').length == 0){ 16 | $('[data-scroll-container="true"]').after(''); 17 | } 18 | if(loading) 19 | return; 20 | if($(document).height()-settings.loadBefore <= ($(document).scrollTop()+$(window).height())){ 21 | var link = $(settings.nextSelector).attr('href'); 22 | if(link === undefined){ 23 | return; 24 | } 25 | loading = true; 26 | $('#scroll-loader').show(); 27 | $.ajax({ 28 | url: link + (link.indexOf("?ajax=true") == -1 ? (link.indexOf("?") == -1) ? "?ajax=true" : "&ajax=true" : ""), 29 | dataType : 'json', 30 | success: function(data){ 31 | var html = data[2].html; 32 | var nextLink = $(html).find(settings.nextSelector).attr('href'); 33 | $(settings.nextSelector).attr('href', nextLink ? nextLink : null); 34 | $('[data-scroll-container="true"]').append($(html).find('[data-scroll-content="true"]')); 35 | loading = false; 36 | $('#scroll-loader').hide(); 37 | settings.callback(); 38 | } 39 | }); 40 | } 41 | }); 42 | 43 | return this; 44 | } 45 | }(jQuery)); -------------------------------------------------------------------------------- /app/Config/Schema/i18n.php: -------------------------------------------------------------------------------- 1 | array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'key' => 'primary'), 42 | 'locale' => array('type' => 'string', 'null' => false, 'length' => 6, 'key' => 'index'), 43 | 'model' => array('type' => 'string', 'null' => false, 'key' => 'index'), 44 | 'foreign_key' => array('type' => 'integer', 'null' => false, 'length' => 10, 'key' => 'index'), 45 | 'field' => array('type' => 'string', 'null' => false, 'key' => 'index'), 46 | 'content' => array('type' => 'text', 'null' => true, 'default' => null), 47 | 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'locale' => array('column' => 'locale', 'unique' => 0), 'model' => array('column' => 'model', 'unique' => 0), 'row_id' => array('column' => 'foreign_key', 'unique' => 0), 'field' => array('column' => 'field', 'unique' => 0)) 48 | ); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/View/Elements/default_navbar.ctp: -------------------------------------------------------------------------------- 1 | 6 | 11 | -------------------------------------------------------------------------------- /app/Controller/Component/SortComponent.php: -------------------------------------------------------------------------------- 1 | $row) { 22 | $s_discs = explode('/', $row['Song']['disc']); 23 | $s_band[$key] = $row['Song']['band']; 24 | $s_album[$key] = $row['Song']['album']; 25 | $s_track_number[$key] = $row['Song']['track_number']; 26 | $s_disc[$key] = $s_discs[0]; 27 | } 28 | 29 | if (!empty($s_band) && !empty($s_album) && !empty($s_track_number) && !empty($s_disc)) { 30 | array_multisort($s_band, $s_album, $s_disc, $s_track_number, $songs); 31 | } 32 | 33 | return $songs; 34 | } 35 | 36 | /** 37 | * Sort an array of songs by disc and track number. 38 | * See /albums to visualize it 39 | * @see https://php.net/manual/en/function.array-multisort.php 40 | * 41 | * @param array $songs Array of songs. 42 | * @return array An array of sorted songs. 43 | */ 44 | public function sortByDisc($songs) { 45 | foreach ($songs as $key => $row) { 46 | $s_discs = explode('/', $row['Song']['disc']); 47 | $s_track_number[$key] = $row['Song']['track_number']; 48 | $s_disc[$key] = $s_discs[0]; 49 | } 50 | 51 | if (!empty($s_discs) && !empty($s_track_number)) { 52 | array_multisort($s_disc, $s_track_number, $songs); 53 | } 54 | 55 | return $songs; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/Lib/Event/UsersEventListener.php: -------------------------------------------------------------------------------- 1 | 'sendUserCreationEmail', 11 | 'Controller.User.resetPassword' => 'sendResetPasswordEmail' 12 | ); 13 | } 14 | 15 | public function sendUserCreationEmail(CakeEvent $event) { 16 | $setting = ClassRegistry::init('Setting'); 17 | $settings = $setting->find('first', array('fields' => array('Setting.enable_mail_notification'))); 18 | $mail_notifications_enabled = $settings['Setting']['enable_mail_notification']; 19 | 20 | if ($mail_notifications_enabled) { 21 | $user_email = $event->subject()->data['User']['email']; 22 | $email = new CakeEmail('default'); 23 | $email->to($user_email) 24 | ->subject(__('Welcome on Sonerezh!')) 25 | ->emailFormat('html') 26 | ->template('userAdd') 27 | ->viewVars(compact('user_email')) 28 | ->send(); 29 | } 30 | } 31 | 32 | public function sendResetPasswordEmail(CakeEvent $event) { 33 | 34 | $setting = ClassRegistry::init('Setting'); 35 | $settings = $setting->find('first', array('fields' => array('Setting.enable_mail_notification'))); 36 | $mail_notifications_enabled = $settings['Setting']['enable_mail_notification']; 37 | 38 | if ($mail_notifications_enabled) { 39 | $event_data = $event->subject(); 40 | $user_email = $event_data['email']; 41 | $token = $event_data['token']; 42 | 43 | $email = new CakeEmail('default'); 44 | $email->to($user_email) 45 | ->subject(__('Forgot your password?')) 46 | ->emailFormat('html') 47 | ->template('sendToken') 48 | ->viewVars(compact('token')) 49 | ->send(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Model/ToolbarAccess.php: -------------------------------------------------------------------------------- 1 | config['datasource']; 41 | 42 | $return = array(); 43 | if (preg_match('/(Mysql|Postgres)$/', $datasource)) { 44 | $explained = $db->query('EXPLAIN ' . $query); 45 | if (preg_match('/Postgres$/', $datasource)) { 46 | $queryPlan = array(); 47 | foreach ($explained as $postgreValue) { 48 | $queryPlan[] = array($postgreValue[0]['QUERY PLAN']); 49 | } 50 | $return = array_merge(array(array('')), $queryPlan); 51 | } else { 52 | $keys = array_keys($explained[0][0]); 53 | foreach ($explained as $mysqlValue) { 54 | $queryPlan[] = array_values($mysqlValue[0]); 55 | } 56 | $return = array_merge(array($keys), $queryPlan); 57 | } 58 | } 59 | return $return; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/SqlLogPanel.php: -------------------------------------------------------------------------------- 1 | config['driver']) && empty($db->config['datasource'])) || 54 | !method_exists($db, 'getLog') 55 | ) { 56 | continue; 57 | } 58 | if (isset($db->config['datasource'])) { 59 | $driver = $db->config['datasource']; 60 | } 61 | $explain = false; 62 | $isExplainable = (preg_match('/(Mysql|Postgres)$/', $driver)); 63 | if ($isExplainable) { 64 | $explain = true; 65 | } 66 | $connections[$configName] = $explain; 67 | } 68 | return array('connections' => $connections, 'threshold' => $this->slowRate); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/View/Helper/BootstrapFormHelper.php: -------------------------------------------------------------------------------- 1 | array( 11 | 'div' => array('class' => 'form-group'), 12 | 'class' => 'form-control', 13 | 'error' => array('attributes' => array('wrap' => 'p', 'class' => 'text-danger')) 14 | ) 15 | ); 16 | 17 | $options = array_merge($defaultOptions, $options); 18 | return parent::create($model, $options); 19 | } 20 | 21 | public function input($fieldName, $options = array()) { 22 | 23 | $this->setEntity($fieldName); 24 | $defaultOptions = $this->_parseOptions($options); 25 | 26 | if ($defaultOptions['type'] == 'checkbox') { 27 | $newDefaultOptions = array( 28 | 'div' => array('class' => 'checkbox'), 29 | 'class' => false, 30 | 'type' => 'checkbox' 31 | ); 32 | 33 | if (isset($defaultOptions["disabled"]) && $defaultOptions['disabled'] == 'disabled') { 34 | $newDefaultOptions['div']['class'] .= ' disabled'; 35 | } 36 | $options = array_merge($newDefaultOptions, $options); 37 | } 38 | 39 | if ($defaultOptions['type'] == 'radio') { 40 | $defaultOptions = array( 41 | 'div' => array('class' => 'radio'), 42 | 'separator' => '
    ', 43 | 'class' => false, 44 | 'legend' => false 45 | ); 46 | $options = array_merge($defaultOptions, $options); 47 | } 48 | return parent::input($fieldName, $options); 49 | } 50 | 51 | 52 | protected function _confirm($message, $okCode, $cancelCode = '', $options = array()) { 53 | $ok = explode('.', $okCode); 54 | $okCode = "$('#".$ok[1]."').submit();"; 55 | return parent::_confirm($message, $okCode, $cancelCode, $options); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/sql_log_panel.ctp: -------------------------------------------------------------------------------- 1 | Toolbar->readCache('sql_log', $this->request->params['pass'][0]); 22 | } 23 | ?> 24 |

    25 | 26 | $explain): ?> 27 |
    28 |

    29 | Toolbar->getQueryLogs($dbName, array( 32 | 'explain' => $explain, 'threshold' => $content['threshold'] 33 | )); 34 | else: 35 | $queryLog = $content[$dbName]; 36 | endif; 37 | echo '
    '; 38 | echo __d( 39 | 'debug_kit', 40 | 'Total Time: %s ms
    Total Queries: %s queries', 41 | $queryLog['time'], 42 | $queryLog['count'] 43 | ); 44 | echo '
    '; 45 | echo $this->Toolbar->table($queryLog['queries'], $headers, array('title' => 'SQL Log ' . $dbName)); 46 | ?> 47 |

    48 |
    49 | 50 |

    51 |
    52 |
    53 | 54 | Toolbar->message('Warning', __d('debug_kit', 'No active database connections')); 56 | endif; 57 | -------------------------------------------------------------------------------- /docs/user-guides/cli-tool.md: -------------------------------------------------------------------------------- 1 | # Command-line tool 2 | 3 | A simple tool available for automation. 4 | 5 | --- 6 | 7 | Since version 1.1.0, a command-line tool is available to process big music 8 | libraries or add some automation. This tool is built on the [CakePHP Shell] and 9 | can be used as below. 10 | 11 | ## Import songs with the CLI 12 | 13 | The CLI can be used to import a single audio file: 14 | 15 | ```text 16 | sonerezh/app $ Console/cake sonerezh import /home/user/Music/file.mp3 17 | Welcome to CakePHP v2.8.1 Console 18 | --------------------------------------------------------------- 19 | App : app 20 | Path: /var/www/sonerezh/app/ 21 | --------------------------------------------------------------- 22 | [INFO] You asked to import /home/user/Music/file.mp3. Continue? (yes/no) 23 | [yes] > 24 | [INFO] Run import: [100%] [#############################################] 25 | ``` 26 | 27 | It can also scan a directory: 28 | 29 | ```text 30 | sonerezh/app $ Console/cake sonerezh import /home/user/Music/an-album 31 | Welcome to CakePHP v2.8.1 Console 32 | --------------------------------------------------------------- 33 | App : app 34 | Path: /var/www/sonerezh/app/ 35 | --------------------------------------------------------------- 36 | [INFO] Scan /home/user/Music/an-album... 37 | [INFO] Found 13 audio files (0 already in the database). Continue? (yes/no) 38 | [yes] > 39 | [INFO] Run import: [100%] [#############################################] 40 | ``` 41 | 42 | Or you can scan a complete folder tree using the ``--recursive`` option: 43 | 44 | ```text 45 | sonerezh/app $ Console/cake sonerezh import -r /home/user/Music 46 | Welcome to CakePHP v2.8.1 Console 47 | --------------------------------------------------------------- 48 | App : app 49 | Path: /var/www/sonerezh/app/ 50 | --------------------------------------------------------------- 51 | [INFO] Scan /home/user/Music... 52 | [INFO] Found 614 audio files (13 already in the database). Continue? (yes/no) 53 | [yes] > 54 | [INFO] Run import: [100%] [#############################################] 55 | ``` 56 | 57 | The imported files are immediately available on the web interface. 58 | 59 | [CakePHP Shell]: http://book.cakephp.org/2.0/en/console-and-shells.html -------------------------------------------------------------------------------- /app/View/Layouts/Emails/html/default.ctp: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | <?php echo $title_for_layout; ?> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
    39 | fetch('content'); ?> 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
    54 | 55 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/DebugPanel.php: -------------------------------------------------------------------------------- 1 | firecake = FireCake::getInstance('TestFireCake'); 38 | TestFireCake::reset(); 39 | } 40 | 41 | /** 42 | * tearDown method 43 | * 44 | * @return void 45 | */ 46 | public function tearDown() { 47 | parent::tearDown(); 48 | Configure::write('log', true); 49 | DebugKitDebugger::clearTimers(); 50 | TestFireCake::reset(); 51 | } 52 | 53 | /** 54 | * test output switch to firePHP 55 | * 56 | * @return void 57 | */ 58 | public function testOutput() { 59 | Debugger::getInstance('DebugKitDebugger'); 60 | Debugger::addFormat('fb', array('callback' => 'DebugKitDebugger::fireError')); 61 | Debugger::outputAs('fb'); 62 | 63 | set_error_handler('ErrorHandler::handleError'); 64 | $foo .= ''; 65 | restore_error_handler(); 66 | 67 | $result = $this->firecake->sentHeaders; 68 | 69 | $this->assertRegExp('/GROUP_START/', $result['X-Wf-1-1-1-1']); 70 | $this->assertRegExp('/ERROR/', $result['X-Wf-1-1-1-2']); 71 | $this->assertRegExp('/GROUP_END/', $result['X-Wf-1-1-1-5']); 72 | 73 | Debugger::getInstance('Debugger'); 74 | Debugger::outputAs('html'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/Config/acl.ini.php: -------------------------------------------------------------------------------- 1 | ; 2 | ;/** 3 | ; * ACL Configuration 4 | ; * 5 | ; * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) 6 | ; * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 7 | ; * 8 | ; * Licensed under The MIT License 9 | ; * Redistributions of files must retain the above copyright notice. 10 | ; * 11 | ; * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) 12 | ; * @link http://cakephp.org CakePHP(tm) Project 13 | ; * @package app.Config 14 | ; * @since CakePHP(tm) v 0.10.0.1076 15 | ; * @license http://www.opensource.org/licenses/mit-license.php MIT License 16 | ; */ 17 | 18 | ; acl.ini.php - Cake ACL Configuration 19 | ; --------------------------------------------------------------------- 20 | ; Use this file to specify user permissions. 21 | ; aco = access control object (something in your application) 22 | ; aro = access request object (something requesting access) 23 | ; 24 | ; User records are added as follows: 25 | ; 26 | ; [uid] 27 | ; groups = group1, group2, group3 28 | ; allow = aco1, aco2, aco3 29 | ; deny = aco4, aco5, aco6 30 | ; 31 | ; Group records are added in a similar manner: 32 | ; 33 | ; [gid] 34 | ; allow = aco1, aco2, aco3 35 | ; deny = aco4, aco5, aco6 36 | ; 37 | ; The allow, deny, and groups sections are all optional. 38 | ; NOTE: groups names *cannot* ever be the same as usernames! 39 | ; 40 | ; ACL permissions are checked in the following order: 41 | ; 1. Check for user denies (and DENY if specified) 42 | ; 2. Check for user allows (and ALLOW if specified) 43 | ; 3. Gather user's groups 44 | ; 4. Check group denies (and DENY if specified) 45 | ; 5. Check group allows (and ALLOW if specified) 46 | ; 6. If no aro, aco, or group information is found, DENY 47 | ; 48 | ; --------------------------------------------------------------------- 49 | 50 | ;------------------------------------- 51 | ;Users 52 | ;------------------------------------- 53 | 54 | [username-goes-here] 55 | groups = group1, group2 56 | deny = aco1, aco2 57 | allow = aco3, aco4 58 | 59 | ;------------------------------------- 60 | ;Groups 61 | ;------------------------------------- 62 | 63 | [groupname-goes-here] 64 | deny = aco5, aco6 65 | allow = aco7, aco8 66 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/DebugkitGroupTestCase.php: -------------------------------------------------------------------------------- 1 | valid()) { 60 | 61 | if (!$it->isDot()) { 62 | $file = $it->key(); 63 | 64 | if ( 65 | preg_match('|Test\.php$|', $file) && 66 | $file !== __FILE__ && 67 | !preg_match('|^All.+?\.php$|', basename($file)) && 68 | ($excludes === null || !in_array($file, $excludes)) 69 | ) { 70 | $files[] = $file; 71 | } 72 | } 73 | 74 | $it->next(); 75 | } 76 | 77 | return $files; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/debug_toolbar.ctp: -------------------------------------------------------------------------------- 1 | 21 |
    22 | 23 |

    24 | 25 | 58 | 59 |
    60 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Test/Case/Model/Behavior/TimedBehaviorTest.php: -------------------------------------------------------------------------------- 1 | Article = ClassRegistry::init('Article'); 43 | $this->Article->Behaviors->attach('DebugKit.Timed'); 44 | } 45 | 46 | /** 47 | * End a test 48 | * 49 | * @return void 50 | */ 51 | public function tearDown() { 52 | parent::tearDown(); 53 | unset($this->Article); 54 | ClassRegistry::flush(); 55 | DebugKitDebugger::clearTimers(); 56 | } 57 | 58 | /** 59 | * Test find timers 60 | * 61 | * @return void 62 | */ 63 | public function testFindTimers() { 64 | $timers = DebugKitDebugger::getTimers(false); 65 | $this->assertEquals(count($timers), 1); 66 | 67 | $this->Article->find('all'); 68 | $result = DebugKitDebugger::getTimers(false); 69 | $this->assertEquals(count($result), 2); 70 | 71 | $this->Article->find('all'); 72 | $result = DebugKitDebugger::getTimers(false); 73 | $this->assertEquals(count($result), 3); 74 | } 75 | 76 | /** 77 | * Test save timers 78 | * 79 | * @return void 80 | */ 81 | public function testSaveTimers() { 82 | $timers = DebugKitDebugger::getTimers(false); 83 | $this->assertEquals(count($timers), 1); 84 | 85 | $this->Article->save(array('user_id' => 1, 'title' => 'test', 'body' => 'test')); 86 | $result = DebugKitDebugger::getTimers(false); 87 | $this->assertEquals(count($result), 2); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/webroot/js/settings.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var mp3Quality = {min: 56, max: 320, steps:[128, 192, 256]}; 3 | var oggQuality = {min: 0, max: 10, steps:[1, 2, 3, 4, 5, 6, 7, 8, 9]}; 4 | var mp3Val = 256; 5 | var oggVal = 8; 6 | var init = true; 7 | 8 | $('#quality-slider').slider({change: function(val) { 9 | $('#SettingQuality').val(val); 10 | updateSlider(); 11 | }}); 12 | 13 | $('[name="data[Setting][convert_to]"]').change(function(){ 14 | var $checked = $('[name="data[Setting][convert_to]"]:checked'); 15 | if($checked.val() == "mp3") { 16 | $('#quality-slider').slider(mp3Quality); 17 | if(!init) { 18 | $('#SettingQuality').val(mp3Val); 19 | } 20 | }else { 21 | $('#quality-slider').slider(oggQuality); 22 | if(!init) { 23 | $('#SettingQuality').val(oggVal); 24 | } 25 | } 26 | init = false; 27 | $('#quality-slider').slider('value', $('#SettingQuality').val()); 28 | updateSlider(); 29 | }).change(); 30 | 31 | function updateSlider() { 32 | var val = $('#SettingQuality').val(); 33 | if($('[name="data[Setting][convert_to]"]:checked').val() == "mp3") { 34 | mp3Val = val; 35 | val += 'kb/s'; 36 | }else { 37 | oggVal = val; 38 | } 39 | $('.quality div:last').text(val); 40 | } 41 | 42 | $('#SettingIndexForm').on('click', '.remove-dir', function(){ 43 | $(this).parents('.rootpath').remove(); 44 | updateRootpathIndex(); 45 | }); 46 | 47 | $('#add-root-path-field').click(function(){ 48 | var $field = $(this).parents('.rootpath').clone(); 49 | $field.find('input[type=hidden]').remove(); 50 | $field.find('input').val(""); 51 | $field.removeAttr('id').find('button').removeAttr('id').toggleClass('btn-primary btn-danger remove-dir').find('i').toggleClass('glyphicon-plus glyphicon-minus'); 52 | $(this).parents('.rootpath').after($field); 53 | updateRootpathIndex(); 54 | }); 55 | 56 | function updateRootpathIndex(){ 57 | $('.rootpath').each(function(i, e){ 58 | $(e).find('input').each(function(index, element){ 59 | var name = $(element).attr('name').replace(/\]\[([0-9]+)\]\[/, "]["+i+"]["); 60 | $(element).attr('name', name); 61 | }); 62 | }); 63 | } 64 | 65 | }); -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Helper/SimpleGraphHelper.php: -------------------------------------------------------------------------------- 1 | (int) Maximum value in the graphs 40 | * - width => (int) 41 | * - valueType => string (value, percentage) 42 | * - style => array 43 | * 44 | * @var array 45 | */ 46 | protected $_defaultSettings = array( 47 | 'max' => 100, 48 | 'width' => 350, 49 | 'valueType' => 'value', 50 | ); 51 | 52 | /** 53 | * bar method 54 | * 55 | * @param $value Value to be graphed 56 | * @param $offset how much indentation 57 | * @param array|\Graph $options Graph options 58 | * @return string Html graph 59 | */ 60 | public function bar($value, $offset, $options = array()) { 61 | $settings = array_merge($this->_defaultSettings, $options); 62 | extract($settings); 63 | 64 | $graphValue = ($value / $max) * $width; 65 | $graphValue = max(round($graphValue), 1); 66 | 67 | if ($valueType === 'percentage') { 68 | $graphOffset = 0; 69 | } else { 70 | $graphOffset = ($offset / $max) * $width; 71 | $graphOffset = round($graphOffset); 72 | } 73 | return $this->Html->div( 74 | 'debug-kit-graph-bar', 75 | $this->Html->div( 76 | 'debug-kit-graph-bar-value', 77 | ' ', 78 | array( 79 | 'style' => "margin-left: {$graphOffset}px; width: {$graphValue}px", 80 | 'title' => __d('debug_kit', "Starting %sms into the request, taking %sms", $offset, $value), 81 | ) 82 | ), 83 | array('style' => "width: {$width}px;"), 84 | false 85 | ); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /app/View/Elements/admin_navbar.ctp: -------------------------------------------------------------------------------- 1 | 6 | 11 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Helper/DebugTimerHelper.php: -------------------------------------------------------------------------------- 1 | _renderComplete) { 56 | return; 57 | } 58 | DebugTimer::start( 59 | 'render_' . basename($viewFile), 60 | __d('debug_kit', 'Rendering %s', 61 | Debugger::trimPath($viewFile)) 62 | ); 63 | } 64 | 65 | /** 66 | * Stops the timer point before rendering a file. 67 | * 68 | * @param string $viewFile The view being rendered 69 | * @param string $content The contents of the view. 70 | */ 71 | public function afterRenderFile($viewFile, $content) { 72 | if ($this->_renderComplete) { 73 | return; 74 | } 75 | DebugTimer::stop('render_' . basename($viewFile)); 76 | } 77 | 78 | /** 79 | * Stop timers for rendering. 80 | * 81 | * @param string $layoutFile 82 | */ 83 | public function afterLayout($layoutFile) { 84 | DebugTimer::stop('viewRender'); 85 | DebugTimer::stop('controllerRender'); 86 | DebugMemory::record(__d('debug_kit', 'View render complete')); 87 | $this->_renderComplete = true; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | env: 9 | - CAKE_VERSION=2.2.7 DB=mysql 10 | - CAKE_VERSION=2.2.7 DB=pgsql 11 | - CAKE_VERSION=master DB=mysql 12 | - CAKE_VERSION=master DB=pgsql 13 | - CAKE_VERSION=2.4.1 DB=mysql 14 | - CAKE_VERSION=2.4.1 DB=pgsql 15 | 16 | before_script: 17 | - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" 18 | - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" 19 | - git clone git://github.com/cakephp/cakephp ../cakephp && cd ../cakephp && git checkout $CAKE_VERSION 20 | - cp -R ../debug_kit plugins/DebugKit 21 | - chmod -R 777 ../cakephp/app/tmp 22 | - set +H 23 | - echo " array( 27 | 'datasource' => 'Database/Mysql', 28 | 'host' => '0.0.0.0', 29 | 'login' => 'travis' 30 | ), 31 | 'pgsql' => array( 32 | 'datasource' => 'Database/Postgres', 33 | 'host' => '127.0.0.1', 34 | 'login' => 'postgres', 35 | 'database' => 'cakephp_test', 36 | 'schema' => array( 37 | 'default' => 'public', 38 | 'test' => 'public' 39 | ) 40 | ) 41 | ); 42 | public \$default = array( 43 | 'persistent' => false, 44 | 'host' => '', 45 | 'login' => '', 46 | 'password' => '', 47 | 'database' => 'cakephp_test', 48 | 'prefix' => '' 49 | ); 50 | public \$test = array( 51 | 'persistent' => false, 52 | 'host' => '', 53 | 'login' => '', 54 | 'password' => '', 55 | 'database' => 'cakephp_test', 56 | 'prefix' => '' 57 | ); 58 | public function __construct() { 59 | \$db = 'mysql'; 60 | if (!empty(\$_SERVER['DB'])) { 61 | \$db = \$_SERVER['DB']; 62 | } 63 | foreach (array('default', 'test') as \$source) { 64 | \$config = array_merge(\$this->{\$source}, \$this->identities[\$db]); 65 | if (is_array(\$config['database'])) { 66 | \$config['database'] = \$config['database'][\$source]; 67 | } 68 | if (!empty(\$config['schema']) && is_array(\$config['schema'])) { 69 | \$config['schema'] = \$config['schema'][\$source]; 70 | } 71 | \$this->{\$source} = \$config; 72 | } 73 | } 74 | }" > ../cakephp/app/Config/database.php 75 | 76 | script: 77 | - ./lib/Cake/Console/cake test DebugKit AllDebugKit --stderr 78 | 79 | notifications: 80 | email: false 81 | -------------------------------------------------------------------------------- /docs/user-guides/settings.md: -------------------------------------------------------------------------------- 1 | # Settings Reference 2 | 3 | Guide to all available configuration settings. 4 | 5 | --- 6 | 7 | ## About the configuration 8 | 9 | The settings can be configured on the ``/settings`` page. You will find some 10 | statistics related to the current installation and buttons used to manage the 11 | database and the caches. 12 | 13 | ### Music root directory 14 | 15 | Here you can specify the absolute path of the folder in which Sonerezh must look 16 | for your music. 17 | 18 | !!! Note 19 | 20 | Make sure Sonerezh can read this folder recursively. 21 | 22 | !!! Warning 23 | 24 | We strongly recommend to **NOT** store any audio file into the Sonerezh 25 | application directory. 26 | 27 | ### Email notifications 28 | 29 | Sonerezh can send email to new users, or allow them to retrieve a forgotten 30 | password. Make sure PHP can send emails before enable this option. 31 | 32 | Just like the database configuration, email configuration can be centralized in 33 | a class called ``EmailConfig``, in ``app/Config/email.php``. The 34 | ``app/Config/email.php.default`` has an example for this file. 35 | 36 | You can follow the [official CakePHP documentation] to configure the different 37 | transport methods to send email. The following configuration should work in most 38 | cases (if you already have a MTA available). 39 | 40 | ```php 41 | class EmailConfig { 42 | public $default = array( 43 | 'transport' => 'Mail', 44 | 'from' => 'no-reply@sonerezh.bzh', 45 | 'charset' => 'utf-8', 46 | 'headerCharset' => 'utf-8', 47 | ); 48 | } 49 | ``` 50 | 51 | In this example, Sonerezh will send email using the PHP function ``mail()``. 52 | 53 | ### Automatic track conversion 54 | 55 | If your library contains tracks which can not be read by your browser, Sonerezh 56 | can convert them to OGG/Vorbis or MP3. 57 | 58 | !!! Note 59 | 60 | Sonerezh requires ``avconv`` or ``ffmpeg`` to convert the tracks. 61 | 62 | ### Database management 63 | 64 | The settings page allows you to make some maintenance operations on Sonerezh, 65 | its database and its caches: 66 | 67 | * Database update: run the import process. Useful if you have recently added new 68 | songs 69 | * Clear the cache : empty the cache (converted tracks and resized covers) 70 | * Reset the database : reset the database. All songs and playlists 71 | **WILL BE LOST**. Don't panic, Sonerezh will never modify or delete any file 72 | on the filesystem 73 | 74 | [official CakePHP documentation]: http://book.cakephp.org/2.0/en/core-utility-libraries/email.html -------------------------------------------------------------------------------- /app/View/Users/login.ctp: -------------------------------------------------------------------------------- 1 |
    2 | Form->create('User'); ?> 3 |
    4 |

    Sonerezh -

    5 |
    6 | Form->input('email', array('placeholder' => __('Email Address'), 'required')); ?> 7 | Form->input('password', array('placeholder' => __('Password'), 'required')); ?> 8 | 9 | Form->input('rememberme', array('type' => 'checkbox', 'label' => __('Remember me'), 'div' => array('class' => 'checkbox pull-left'))); ?> 10 | 11 | 12 | 13 |
    14 |
    15 |
    16 |
    17 | Form->submit(__('Sign In'), array('class' => 'btn btn-lg btn-success btn-block')); ?> 18 |
    19 |
    20 |
    21 | Form->end(); ?> 22 |
    23 | 24 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/EnvironmentPanel.php: -------------------------------------------------------------------------------- 1 | $phpVer), $_SERVER); 43 | unset($return['php']['argv']); 44 | 45 | // CakePHP Data 46 | $return['cake'] = array( 47 | 'APP' => APP, 48 | 'APP_DIR' => APP_DIR, 49 | 'APPLIBS' => APPLIBS, 50 | 'CACHE' => CACHE, 51 | 'CAKE' => CAKE, 52 | 'CAKE_CORE_INCLUDE_PATH' => CAKE_CORE_INCLUDE_PATH, 53 | 'CORE_PATH' => CORE_PATH, 54 | 'CAKE_VERSION' => Configure::version(), 55 | 'CSS' => CSS, 56 | 'CSS_URL' => CSS_URL, 57 | 'DS' => DS, 58 | 'FULL_BASE_URL' => FULL_BASE_URL, 59 | 'IMAGES' => IMAGES, 60 | 'IMAGES_URL' => IMAGES_URL, 61 | 'JS' => JS, 62 | 'JS_URL' => JS_URL, 63 | 'LOGS' => LOGS, 64 | 'ROOT' => ROOT, 65 | 'TESTS' => TESTS, 66 | 'TMP' => TMP, 67 | 'VENDORS' => VENDORS, 68 | 'WEBROOT_DIR' => WEBROOT_DIR, 69 | 'WWW_ROOT' => WWW_ROOT 70 | ); 71 | 72 | $cakeConstants = array_fill_keys( 73 | array( 74 | 'DS', 'ROOT', 'FULL_BASE_URL', 'TIME_START', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR', 75 | 'LOG_ERROR', 'FULL_BASE_URL' 76 | ), '' 77 | ); 78 | $var = get_defined_constants(true); 79 | $return['app'] = array_diff_key($var['user'], $return['cake'], $cakeConstants); 80 | 81 | if (isset($var['hidef'])) { 82 | $return['hidef'] = $var['hidef']; 83 | } 84 | 85 | return $return; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Lib/Panel/HistoryPanel.php: -------------------------------------------------------------------------------- 1 | history = $settings['history']; 44 | } 45 | } 46 | 47 | /** 48 | * beforeRender callback function 49 | * 50 | * @param Controller $controller 51 | * @return array contents for panel 52 | */ 53 | public function beforeRender(Controller $controller) { 54 | $cacheKey = $controller->Toolbar->cacheKey; 55 | $toolbarHistory = Cache::read($cacheKey, 'debug_kit'); 56 | $historyStates = array(); 57 | if (is_array($toolbarHistory) && !empty($toolbarHistory)) { 58 | $prefix = array(); 59 | if (!empty($controller->request->params['prefix'])) { 60 | $prefix[$controller->request->params['prefix']] = false; 61 | } 62 | foreach ($toolbarHistory as $i => $state) { 63 | if (!isset($state['request']['content']['url'])) { 64 | continue; 65 | } 66 | $title = $state['request']['content']['url']; 67 | $query = @$state['request']['content']['query']; 68 | if (isset($query['url'])) { 69 | unset($query['url']); 70 | } 71 | if (!empty($query)) { 72 | $title .= '?' . urldecode(http_build_query($query)); 73 | } 74 | $historyStates[] = array( 75 | 'title' => $title, 76 | 'url' => array_merge($prefix, array( 77 | 'plugin' => 'debug_kit', 78 | 'controller' => 'toolbar_access', 79 | 'action' => 'history_state', 80 | $i + 1)) 81 | ); 82 | } 83 | } 84 | if (count($historyStates) >= $this->history) { 85 | array_pop($historyStates); 86 | } 87 | return $historyStates; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Elements/environment_panel.ctp: -------------------------------------------------------------------------------- 1 | 20 |

    21 | $val) { 25 | $cakeRows[] = array( 26 | $key, 27 | $val 28 | ); 29 | } 30 | $headers = array('Constant', 'Value'); 31 | echo $this->Toolbar->table($cakeRows, $headers, array('title' => 'Application Environment Vars')); 32 | } else { 33 | echo "No application environment available."; 34 | } ?> 35 | 36 |

    37 | $val) { 41 | $cakeRows[] = array( 42 | $key, 43 | $val 44 | ); 45 | } 46 | $headers = array('Constant', 'Value'); 47 | echo $this->Toolbar->table($cakeRows, $headers, array('title' => 'CakePHP Environment Vars')); 48 | } else { 49 | echo "CakePHP environment unavailable."; 50 | } ?> 51 | 52 |

    53 | $val) { 59 | $phpRows[] = array( 60 | Inflector::humanize(strtolower($key)), 61 | $val 62 | ); 63 | } 64 | echo $this->Toolbar->table($phpRows, $headers, array('title' => 'CakePHP Environment Vars')); 65 | } else { 66 | echo "PHP environment unavailable."; 67 | } 68 | 69 | if (isset($content['hidef'])) { 70 | echo '

    ' . __d('debug_kit', 'Hidef Environment') . '

    '; 71 | if (!empty($content['hidef'])) { 72 | $cakeRows = array(); 73 | foreach ($content['hidef'] as $key => $val) { 74 | $cakeRows[] = array( 75 | $key, 76 | $val 77 | ); 78 | } 79 | $headers = array('Constant', 'Value'); 80 | echo $this->Toolbar->table($cakeRows, $headers, array('title' => 'Hidef Environment Vars')); 81 | } else { 82 | echo "No Hidef environment available."; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/Model/Setting.php: -------------------------------------------------------------------------------- 1 | array( 10 | 'boolean' => array( 11 | 'rule' => array('boolean'), 12 | 'message' => 'Something went wrong!' 13 | ) 14 | ), 15 | 'convert_to' => array( 16 | 'inList' => array( 17 | 'rule' => array('inList', array('mp3', 'ogg')), 18 | 'message' => 'Wrong conversion destination!' 19 | ), 20 | 'convConflicts' => array( 21 | 'rule' => array('convConflicts'), 22 | 'message' => "Wrong conversion destination! Make sure you are not trying to convert MP3 to MP3, or OGG to OGG." 23 | ) 24 | ), 25 | 'enable_mail_notification' => array( 26 | 'boolean' => array( 27 | 'rule' => array('boolean'), 28 | 'message' => 'Something went wrong!' 29 | ) 30 | ) 31 | ); 32 | 33 | public function beforeSave($options = array()) { 34 | // On place les fichiers à convertir dans ['convert_from'] 35 | $this->data[$this->alias]['convert_from'] = ''; 36 | if (isset($this->data[$this->alias]['from_mp3']) && $this->data[$this->alias]['from_mp3']) { 37 | $this->data[$this->alias]['convert_from'] .= 'mp3,'; 38 | } 39 | if (isset($this->data[$this->alias]['from_ogg']) && $this->data[$this->alias]['from_ogg']) { 40 | $this->data[$this->alias]['convert_from'] .= 'ogg,'; 41 | } 42 | if (isset($this->data[$this->alias]['from_flac']) && $this->data[$this->alias]['from_flac']) { 43 | $this->data[$this->alias]['convert_from'] .= 'flac,'; 44 | } 45 | // On force la conversion des fichiers AAC 46 | $this->data[$this->alias]['convert_from'] .= 'aac'; 47 | return true; 48 | } 49 | 50 | public function convConflicts($options = array()) { 51 | // Make sure Sonerezh will not try to convert MP3 to MP3 or OGG to OGG 52 | if (isset($this->data[$this->alias]['from_mp3'])) { 53 | if ($this->data[$this->alias]['from_mp3'] && $this->data[$this->alias]['convert_to'] == 'mp3') { 54 | return false; 55 | } 56 | } 57 | 58 | if (isset($this->data[$this->alias]['from_ogg'])) { 59 | if ($this->data[$this->alias]['from_ogg'] && $this->data[$this->alias]['convert_to'] == 'ogg') { 60 | return false; 61 | } 62 | } 63 | return true; 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /app/Config/routes.php: -------------------------------------------------------------------------------- 1 | 'songs', 'action' => 'index')); 28 | 29 | Router::connect('/install', array('controller' => 'installers', 'action' => 'index')); 30 | Router::connect('/docker-install', array('controller' => 'installers', 'action' => 'docker')); 31 | 32 | Router::connect('/login', array('controller' => 'users', 'action' => 'login')); 33 | Router::connect('/logout', array('controller' => 'users', 'action' => 'logout')); 34 | 35 | Router::connect('/api/*', array('controller' => 'api')); 36 | 37 | Router::connect('/playlists/:action/:id', array('controller' => 'playlists'), array('pass' => array('id'))); 38 | Router::connect('/playlists/add', array('controller' => 'playlists', 'action' => 'add')); 39 | Router::connect('/playlists/*', array('controller' => 'playlists', 'action' => 'index')); 40 | 41 | Router::connect('/users', array('controller' => 'users', 'action' => 'index')); 42 | 43 | Router::connect('/settings', array('controller' => 'settings', 'action' => 'index')); 44 | 45 | Router::connect('/img/**', array('controller' => 'img', 'action' => 'index')); 46 | 47 | Router::connect('/:action', array('controller' => 'songs')); 48 | 49 | /** 50 | * Load all plugin routes. See the CakePlugin documentation on 51 | * how to customize the loading of plugin routes. 52 | */ 53 | CakePlugin::routes(); 54 | 55 | /** 56 | * Load the CakePHP default routes. Only remove this if you do not want to use 57 | * the built-in default routes. 58 | */ 59 | require CAKE . 'Config' . DS . 'routes.php'; 60 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Sonerezh 2 | 3 | A self-hosted, web-based application to stream your music, everywhere. 4 | 5 | --- 6 | 7 | ## Overview 8 | 9 | Sonerezh is a self-hosted, web-based audio streaming application allowing you 10 | to access your music from anywhere, using your favorite web browser. 11 | 12 | **Automatic import** 13 | 14 | Tell Sonerezh where your music is stored and that's all. It will parse and 15 | extract the metadata of each files to built its own database. We currently 16 | support MP3 and MP4, OGG/Vorbis (.ogg and .flac) files. 17 | 18 | **Beautiful Web-UI** 19 | 20 | Browse you favorite music through several beautiful views, sort your collection 21 | by band, album, date, etc. 22 | 23 | **2-clicks installation** 24 | 25 | Sonerezh is easy to install thanks to its automatic deployment tool. No 26 | headaches with configuration files. 27 | 28 | --- 29 | 30 | ## Installation 31 | 32 | Sonerezh is quite easy to install. All you need is: 33 | 34 | - A web server (Apache2, Nginx...) 35 | - PHP, with the following modules enabled: ``php-mysql`` and ``php-gd`` 36 | (with ``exif.so``) 37 | - A database (MySQL or MariaDB) 38 | - Optionally ``avconv`` or ``ffmpeg`` if you plan to use the conversion tool 39 | 40 | !!! Note 41 | 42 | This documentation will **NOT** cover the installation of NGINX or PHP. A lot 43 | of tutorials are already available on the web. 44 | 45 | However, configuration samples are available for NGINX or Apache. 46 | 47 | ### Get the latest stable release 48 | 49 | The releases are published on [GitHub], but you should download the latest 50 | stable release from _sonerezh.bzh_. 51 | 52 | ```sh 53 | # Replace tar.gz with zip if needed. 54 | $ wget https://www.sonerezh.bzh/downloads/latest.tar.gz 55 | $ tar -zxf latest.tar.gz 56 | ``` 57 | 58 | ### Prepare the database 59 | 60 | The database must be created before you access Sonerezh for the first time. 61 | A simple setup could be: 62 | 63 | ```sql 64 | $ mysql -u root -p 65 | mysql> CREATE DATABASE sonerezh; 66 | mysql> GRANT ALL PRIVILEGES ON sonerezh.* TO 'sonerezh'@'localhost' IDENTIFIED BY ''; 67 | mysql> FLUSH PRIVILEGES; 68 | exit; 69 | ``` 70 | 71 | Where ```` is the password used by the MySQL user. 72 | 73 | --- 74 | 75 | ## Getting started 76 | 77 | You may want to allow the web server system account to read this directory. 78 | Usually its ``www-data``. 79 | 80 | ```sh 81 | $ sudo chown -R www-data: sonerezh 82 | ``` 83 | 84 | Now you can go to ``http://127.0.0.1/install`` (or whatever configured domain 85 | name) to set the database settings and create the first user. 86 | 87 | ![Screenshot](img/install.png) 88 | 89 | If all goes well you should be redirected to the login page. 90 | 91 | [GitHub]: https://github.com/Sonerezh/sonerezh/releases 92 | -------------------------------------------------------------------------------- /docs/user-guides/nginx-server-block.md: -------------------------------------------------------------------------------- 1 | # NGINX server-block 2 | 3 | This is a minimalist configuration sample for NGINX. 4 | 5 | ```nginx 6 | upstream php-fpm { 7 | server unix:/var/run/php-fpm.sock; 8 | } 9 | 10 | server { 11 | listen 80; 12 | server_name demo.sonerezh.bzh; 13 | root /var/www/sonerezh/app/webroot; 14 | 15 | index index.php; 16 | 17 | location / { 18 | try_files $uri $uri/ /index.php?$args; 19 | expires 14d; 20 | add_header Cache-Control 'public'; 21 | } 22 | 23 | # The section below handle the thumbnails cache, on the client (browser) 24 | # side (optional but recommended) 25 | location ~* /([^/]+_[0-9]+x[0-9]+(@[0-9]+x)?\.[a-z]+)$ { 26 | try_files /img/resized/$1 /index.php?$args; 27 | add_header Cache-Control 'public'; 28 | expires 14d; 29 | access_log off; 30 | } 31 | 32 | location ~ \.php$ { 33 | try_files $uri =404; 34 | fastcgi_index index.php; 35 | fastcgi_pass php-fpm; 36 | include fastcgi.conf; 37 | 38 | # If fastcgi.conf is not available on your platform you may want to 39 | # uncomment the following line 40 | #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 41 | } 42 | } 43 | ``` 44 | 45 | To run Sonerezh on a subfolder instead of a subdomain, here is an other example. 46 | 47 | ```nginx 48 | upstream php-fpm { 49 | server unix:/var/run/php-fpm.sock; 50 | } 51 | 52 | server { 53 | listen 80; 54 | server_name demo.sonerezh.bzh/sonerezh; 55 | 56 | index index.php; 57 | 58 | location /sonerezh/ { 59 | alias /var/www/sonerezh/app/webroot/; 60 | try_files $uri $uri/ /sonerezh//sonerezh/index.php?$args; 61 | 62 | # The section below handle the thumbnails cache, on the client (browser) 63 | # side (optional but recommended) 64 | location ~* /([^/]+_[0-9]+x[0-9]+(@[0-9]+x)?\.[a-z]+)$ { 65 | try_files /img/resized/$1 /index.php?$args; 66 | add_header Cache-Control 'public'; 67 | expires 14d; 68 | access_log off; 69 | } 70 | 71 | location ~ ^/sonerezh/(.+\.php)$ { 72 | alias /var/www/sonerezh/app/webroot/$1; 73 | fastcgi_pass php-fpm; 74 | include fastcgi.conf; 75 | 76 | # If fastcgi.conf is not available on your platform you may want to 77 | # uncomment the following line 78 | #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 79 | } 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /app/Config/email.php.default: -------------------------------------------------------------------------------- 1 | The name of a supported transport; valid options are as follows: 28 | * Mail - Send using PHP mail function 29 | * Smtp - Send using SMTP 30 | * Debug - Do not send the email, just return the result 31 | * 32 | * You can add custom transports (or override existing transports) by adding the 33 | * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', 34 | * where 'Your' is the name of the transport. 35 | * 36 | * from => 37 | * The origin email. See CakeEmail::from() about the valid values 38 | * 39 | */ 40 | class EmailConfig { 41 | 42 | public $default = array( 43 | 'transport' => 'Mail', 44 | 'from' => 'you@localhost', 45 | //'charset' => 'utf-8', 46 | //'headerCharset' => 'utf-8', 47 | ); 48 | 49 | public $smtp = array( 50 | 'transport' => 'Smtp', 51 | 'from' => array('site@localhost' => 'My Site'), 52 | 'host' => 'localhost', 53 | 'port' => 25, 54 | 'timeout' => 30, 55 | 'username' => 'user', 56 | 'password' => 'secret', 57 | 'client' => null, 58 | 'log' => false, 59 | //'charset' => 'utf-8', 60 | //'headerCharset' => 'utf-8', 61 | ); 62 | 63 | public $fast = array( 64 | 'from' => 'you@localhost', 65 | 'sender' => null, 66 | 'to' => null, 67 | 'cc' => null, 68 | 'bcc' => null, 69 | 'replyTo' => null, 70 | 'readReceipt' => null, 71 | 'returnPath' => null, 72 | 'messageId' => true, 73 | 'subject' => null, 74 | 'message' => null, 75 | 'headers' => null, 76 | 'viewRender' => null, 77 | 'template' => false, 78 | 'layout' => false, 79 | 'viewVars' => null, 80 | 'attachments' => null, 81 | 'emailFormat' => null, 82 | 'transport' => 'Smtp', 83 | 'host' => 'localhost', 84 | 'port' => 25, 85 | 'timeout' => 30, 86 | 'username' => 'user', 87 | 'password' => 'secret', 88 | 'client' => null, 89 | 'log' => true, 90 | //'charset' => 'utf-8', 91 | //'headerCharset' => 'utf-8', 92 | ); 93 | 94 | } 95 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/View/Helper/FirePhpToolbarHelper.php: -------------------------------------------------------------------------------- 1 | 'firePHP', 'forceEnable' => false); 32 | 33 | /** 34 | * send method 35 | * 36 | * @return void 37 | */ 38 | public function send() { 39 | $view = $this->_View; 40 | $view->element('debug_toolbar', array('disableTimer' => true), array('plugin' => 'DebugKit')); 41 | } 42 | 43 | /** 44 | * makeNeatArray. 45 | * 46 | * wraps FireCake::dump() allowing panel elements to continue functioning 47 | * 48 | * @param string $values 49 | * @return void 50 | */ 51 | public function makeNeatArray($values) { 52 | FireCake::info($values); 53 | } 54 | 55 | /** 56 | * Create a simple message 57 | * 58 | * @param string $label Label of message 59 | * @param string $message Message content 60 | * @return void 61 | */ 62 | public function message($label, $message) { 63 | FireCake::log($message, $label); 64 | } 65 | 66 | /** 67 | * Generate a table with FireCake 68 | * 69 | * @param array $rows Rows to print 70 | * @param array $headers Headers for table 71 | * @param array $options Additional options and params 72 | * @return void 73 | */ 74 | public function table($rows, $headers, $options = array()) { 75 | $title = $headers[0]; 76 | if (isset($options['title'])) { 77 | $title = $options['title']; 78 | } 79 | foreach ($rows as $i => $row) { 80 | $rows[$i] = array_values($row); 81 | } 82 | array_unshift($rows, $headers); 83 | FireCake::table($title, $rows); 84 | } 85 | 86 | /** 87 | * Start a panel which is a 'Group' in FirePHP 88 | * 89 | * @param $title 90 | * @param $anchor 91 | * @return void 92 | */ 93 | public function panelStart($title, $anchor) { 94 | FireCake::group($title); 95 | } 96 | 97 | /** 98 | * End a panel (Group) 99 | * 100 | * @return void 101 | */ 102 | public function panelEnd() { 103 | FireCake::groupEnd(); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /app/webroot/js/lazyload.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | lazyload v2.1.3 | github.com/vvo/lazyload#license 3 | */ 4 | (function(c,k){function t(a,b,g){a.attachEvent?a.attachEvent("on"+b,g):a.addEventListener(b,g,!1)}function u(a,b,g){var d;return function(){var f=this,c=arguments,e=g&&!d;clearTimeout(d);d=setTimeout(function(){d=null;g||a.apply(f,c)},b);e&&a.apply(f,c)}}function v(a){function b(b,l,e){if(!d(k.documentElement,b)||!d(k.documentElement,a))return e?setTimeout(g(b,l,e),0):!1;var c=b.getBoundingClientRect(),n=a.getBoundingClientRect(),m=c.left,h=c.top,p=l,q=l;a===k.body?(p+=k.documentElement.clientWidth, 5 | q+=k.documentElement.clientHeight,n={bottom:a.scrollHeight,top:0,left:0,right:a.scrollWidth}):(m-=n.left,h-=n.top,p+=a.clientWidth,q+=a.clientHeight);if(!(c.rightn.right||c.bottomn.bottom)&&h<=q&&m<=p)if(e)f.splice(r.call(f,b),1),e(b);else return!0;else if(e)setTimeout(g(b,l,e),0);else return!1}function g(a,g,d){-1===r.call(f,a)&&f.push(a);return function(){l.push(function(){b(a,g,d)})}}var l=[],f=[],e=a===k.body?c:a,m=u(function(){for(var a;a=l.shift();)a()},15);t(e, 6 | "scroll",m);e===c&&t(c,"resize",m);"function"===typeof window.MutationObserver&&h(f,a,m);return{container:a,inViewport:b}}function r(a){for(var b=this.length;b--&&this[b]!==a;);return b}function h(a,b,g){function d(b){return-1!==r.call(a,b)}function e(a){return 0settings[$Model->alias] = array_merge($this->_defaults, $settings); 48 | } else { 49 | $this->settings[$Model->alias] = $this->_defaults; 50 | } 51 | } 52 | 53 | /** 54 | * beforeFind, starts a timer for a find operation. 55 | * 56 | * @param Model $Model 57 | * @param array $queryData Array of query data (not modified) 58 | * @return boolean true 59 | */ 60 | public function beforeFind(Model $Model, $queryData) { 61 | DebugKitDebugger::startTimer($Model->alias . '_find', $Model->alias . '->find()'); 62 | return true; 63 | } 64 | 65 | /** 66 | * afterFind, stops a timer for a find operation. 67 | * 68 | * @param Model $Model 69 | * @param array $results Array of results 70 | * @param $primary 71 | * @return boolean true. 72 | */ 73 | public function afterFind(Model $Model, $results, $primary = false) { 74 | DebugKitDebugger::stopTimer($Model->alias . '_find'); 75 | return true; 76 | } 77 | 78 | /** 79 | * beforeSave, starts a time before a save is initiated. 80 | * 81 | * @param Model $Model 82 | * @param array $options 83 | * @return boolean true 84 | */ 85 | public function beforeSave(Model $Model, $options = array()) { 86 | DebugKitDebugger::startTimer($Model->alias . '_save', $Model->alias . '->save()'); 87 | return true; 88 | } 89 | 90 | /** 91 | * afterSave, stop the timer started from a save. 92 | * 93 | * @param \Model $Model 94 | * @param string $created 95 | * @return boolean Always true 96 | */ 97 | public function afterSave(Model $Model, $created, $options = array()) { 98 | DebugKitDebugger::stopTimer($Model->alias . '_save'); 99 | return true; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/Plugin/DebugKit/Console/Command/WhitespaceShell.php: -------------------------------------------------------------------------------- 1 | params['path']) && strpos($this->params['path'], '/') === 0) { 34 | $path = $this->params['path']; 35 | } elseif (!empty($this->params['path'])) { 36 | $path .= $this->params['path']; 37 | } 38 | $folder = new Folder($path); 39 | 40 | $r = $folder->findRecursive('.*\.php'); 41 | $this->out("Checking *.php in " . $path); 42 | foreach ($r as $file) { 43 | $c = file_get_contents($file); 44 | if (preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', $c)) { 45 | $this->out('!!!contains leading whitespaces: ' . $this->shortPath($file)); 46 | } 47 | if (preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', $c)) { 48 | $this->out('!!!contains trailing whitespaces: ' . $this->shortPath($file)); 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * Much like main() except files are modified. Be sure to have 55 | * backups or use version control. 56 | * 57 | * @return void 58 | */ 59 | public function trim() { 60 | $path = APP; 61 | if (!empty($this->params['path']) && strpos($this->params['path'], '/') === 0) { 62 | $path = $this->params['path']; 63 | } elseif (!empty($this->params['path'])) { 64 | $path .= $this->params['path']; 65 | } 66 | $folder = new Folder($path); 67 | 68 | $r = $folder->findRecursive('.*\.php'); 69 | $this->out("Checking *.php in " . $path); 70 | foreach ($r as $file) { 71 | $c = file_get_contents($file); 72 | if (preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', $c) || preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', $c)) { 73 | $this->out('trimming' . $this->shortPath($file)); 74 | $c = preg_replace('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', '[\n\r|\n\r|\n|\r|\s]+$/', '?>', $c); 76 | file_put_contents($file, $c); 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * get the option parser 83 | * 84 | * @return ConsoleOptionParser 85 | */ 86 | public function getOptionParser() { 87 | $parser = parent::getOptionParser(); 88 | return $parser->addOption('path', array( 89 | 'short' => 'p', 90 | 'help' => __d('cake_console', 'Absolute path or relative to APP.') 91 | )); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/webroot/js/jquery.list.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.fn.list = function(options) { 3 | 4 | var settings = $.extend({ 5 | min: 1, 6 | table: '', 7 | cellTemplate: '', 8 | updateTemplate: function() {}, 9 | callback: function() {}, 10 | cellHeight: 20, 11 | cellsShowed: 10, 12 | data: [] 13 | }, options); 14 | 15 | var totalSize = settings.data.length * settings.cellHeight; 16 | var lastIndex = 0; 17 | var maxHeight = totalSize-(settings.cellsShowed * settings.cellHeight); 18 | var maxIndex = settings.data.length - settings.cellsShowed; 19 | 20 | $(settings.table + " tbody", this).append(''); 21 | var max = Math.min(settings.data.length, settings.cellsShowed); 22 | for(var i = 0; i < max; i++) { 23 | var $template = settings.updateTemplate($(settings.cellTemplate), settings.data[i], i); 24 | $(settings.table + " tbody", this).append($template); 25 | } 26 | $(settings.table + " tbody", this).append(''); 27 | 28 | this.unbind('scroll'); 29 | this.scroll(function() { 30 | var index = Math.floor($(this).scrollTop()/settings.cellHeight)-settings.min; 31 | if(index < settings.min && index < lastIndex) { 32 | index = 0; 33 | } 34 | if(index >= 0 && index != lastIndex) { 35 | var topSize = index * settings.cellHeight; 36 | var bottomSize = totalSize - (index + settings.cellsShowed) * settings.cellHeight; 37 | 38 | if(topSize > maxHeight) { 39 | topSize = maxHeight; 40 | } 41 | 42 | $(settings.table, this).find('.first-line').height(topSize); 43 | $(settings.table, this).find('.last-line').height(bottomSize); 44 | 45 | if(index > maxIndex) { 46 | index = maxIndex; 47 | } 48 | 49 | var diff = index - lastIndex; 50 | if(diff > 0) { 51 | for(var i = 0; i < diff; i++) { 52 | var $tr = $(settings.table, this).find('tr:eq(1)'); 53 | var realIndex = index-diff+i+settings.cellsShowed; 54 | $tr = settings.updateTemplate($tr, settings.data[realIndex], realIndex); 55 | $(settings.table, this).find('.last-line').before($tr); 56 | } 57 | }else { 58 | for(var i = 0; i > diff; i--) { 59 | var $tr = $(settings.table, this).find('tr:eq('+settings.cellsShowed+')'); 60 | var realIndex = index-diff+i-1; 61 | $tr = settings.updateTemplate($tr, settings.data[realIndex], realIndex); 62 | $(settings.table, this).find('.first-line').after($tr); 63 | } 64 | } 65 | settings.callback(); 66 | lastIndex = index; 67 | } 68 | 69 | }); 70 | } 71 | }(jQuery)); -------------------------------------------------------------------------------- /app/Config/database.php.default: -------------------------------------------------------------------------------- 1 | The name of a supported datasource; valid options are as follows: 25 | * Database/Mysql - MySQL 4 & 5, 26 | * Database/Sqlite - SQLite (PHP5 only), 27 | * Database/Postgres - PostgreSQL 7 and higher, 28 | * Database/Sqlserver - Microsoft SQL Server 2005 and higher 29 | * 30 | * You can add custom database datasources (or override existing datasources) by adding the 31 | * appropriate file to app/Model/Datasource/Database. Datasources should be named 'MyDatasource.php', 32 | * 33 | * 34 | * persistent => true / false 35 | * Determines whether or not the database should use a persistent connection 36 | * 37 | * host => 38 | * the host you connect to the database. To add a socket or port number, use 'port' => # 39 | * 40 | * prefix => 41 | * Uses the given prefix for all the tables in this database. This setting can be overridden 42 | * on a per-table basis with the Model::$tablePrefix property. 43 | * 44 | * schema => 45 | * For Postgres/Sqlserver specifies which schema you would like to use the tables in. 46 | * Postgres defaults to 'public'. For Sqlserver, it defaults to empty and use 47 | * the connected user's default schema (typically 'dbo'). 48 | * 49 | * encoding => 50 | * For MySQL, Postgres specifies the character encoding to use when connecting to the 51 | * database. Uses database default not specified. 52 | * 53 | * unix_socket => 54 | * For MySQL to connect via socket specify the `unix_socket` parameter instead of `host` and `port` 55 | * 56 | * settings => 57 | * Array of key/value pairs, on connection it executes SET statements for each pair 58 | * For MySQL : http://dev.mysql.com/doc/refman/5.6/en/set-statement.html 59 | * For Postgres : http://www.postgresql.org/docs/9.2/static/sql-set.html 60 | * For Sql Server : http://msdn.microsoft.com/en-us/library/ms190356.aspx 61 | */ 62 | class DATABASE_CONFIG { 63 | 64 | public $default = array( 65 | 'datasource' => 'Database/Mysql', 66 | 'persistent' => false, 67 | 'host' => 'localhost', 68 | 'login' => 'user', 69 | 'password' => 'password', 70 | 'database' => 'database_name', 71 | 'prefix' => '', 72 | //'encoding' => 'utf8', 73 | ); 74 | 75 | public $test = array( 76 | 'datasource' => 'Database/Mysql', 77 | 'persistent' => false, 78 | 'host' => 'localhost', 79 | 'login' => 'user', 80 | 'password' => 'password', 81 | 'database' => 'test_database_name', 82 | 'prefix' => '', 83 | //'encoding' => 'utf8', 84 | ); 85 | } 86 | --------------------------------------------------------------------------------