From 2cf26d33b6ab49a613e1937e2c46613a88cb9c26 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Fri, 12 Jul 2013 13:10:35 +0200 Subject: [PATCH 01/72] fixes run testcases from ide --- app/Lib/SaitoControllerTestCase.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Lib/SaitoControllerTestCase.php b/app/Lib/SaitoControllerTestCase.php index bc516d617..3eb30c06d 100644 --- a/app/Lib/SaitoControllerTestCase.php +++ b/app/Lib/SaitoControllerTestCase.php @@ -8,6 +8,8 @@ if ( !defined('FULL_BASE_URL') ) { define('FULL_BASE_URL', 'http://cakephp.org/'); } + Configure::write('App.fullBaseURL', FULL_BASE_URL); + class SaitoControllerTestCase extends ControllerTestCase { From f64756556a102c7f030763da4f5602c65bcbeb7a Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sun, 14 Jul 2013 12:17:44 +0200 Subject: [PATCH 02/72] code formatting --- app/Controller/Component/CurrentUserComponent.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Controller/Component/CurrentUserComponent.php b/app/Controller/Component/CurrentUserComponent.php index 158200150..2bbfd41dc 100755 --- a/app/Controller/Component/CurrentUserComponent.php +++ b/app/Controller/Component/CurrentUserComponent.php @@ -140,7 +140,7 @@ protected function _markOnline() { Stopwatch::start('CurrentUser->_markOnline()'); $id = $this->getId(); - if ($this->isLoggedIn() == false): + if ($this->isLoggedIn() === false): // don't count search bots as guests if ($this->_isBot()) { return; @@ -166,8 +166,8 @@ protected function _isBot() { protected function _cookieRelogin() { $cookie = $this->PersistentCookie->get(); - // is_array -> if cookie could no be correctly deciphered it's just an random string if ($cookie) { + // is_array -> if cookie could no be correctly deciphered it's just an random string if (!is_null($cookie) && is_array($cookie)): if ($this->_Controller->Auth->login($cookie)): return; @@ -203,7 +203,7 @@ public function login() { public function refresh($user = null) { parent::set($this->_Controller->Auth->user()); - // all session should must use current user data (locked, user_type, …) + // all session have to use current user data (locked, user_type, …) if ($this->isLoggedIn()) { $this->_User->id = $this->getId(); parent::set($this->_User->getProfile($this->getId())); From 622f86907a1a03e6268976ab7310f40066fcc669 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Tue, 16 Jul 2013 12:19:19 +0200 Subject: [PATCH 03/72] Entry::update explicitly needs 'id' in data --- app/Controller/EntriesController.php | 11 ++++++----- app/Model/Entry.php | 5 ++++- app/Test/Case/Controller/EntriesControllerTest.php | 2 +- app/Test/Case/Model/EntryTest.php | 5 +++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 1b9a950c1..d70e5cb2c 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -372,11 +372,11 @@ public function threadLine($id = null) { public function edit($id = null) { - if (!$id && empty($this->request->data)) { - throw new NotFoundException(); + if (empty($id)) { + throw new BadRequestException(); } - $old_entry = $this->Entry->getUnsanitized($id); + $old_entry = $this->Entry->getUnsanitized((int)$id); if (!$old_entry) { throw new NotFoundException(); @@ -405,8 +405,9 @@ public function edit($id = null) { } if (!empty($this->request->data)) { - $this->Entry->id = $id; - $new_entry = $this->Entry->update($this->request->data); + $data = $this->request->data; + $data['Entry']['id'] = $id; + $new_entry = $this->Entry->update($data); if ($new_entry) { $this->_afterNewEntry(am($this->request['data'], $old_entry)); return $this->redirect(array('action' => 'view', $id)); diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 45168edea..921e8710e 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -390,7 +390,10 @@ public function update($data, $CurrentUser = null) { $this->_CurrentUser = $CurrentUser; } - $data[$this->alias]['id'] = $this->id; + if (empty($data[$this->alias]['id'])) { + throw new InvalidArgumentException('No entry id in Entry::update()'); + } + $this->prepare($data, ['preFilterFields' => 'update']); // prevents normal user of changing category of complete thread when answering diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 0684a7ee8..434d242b4 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -459,7 +459,7 @@ public function testEditNoEntry() { public function testEditNoEntryId() { $Entries = $this->generate('Entries'); $this->_loginUser(2); - $this->expectException('NotFoundException'); + $this->expectException('BadRequestException'); $this->testAction('entries/edit/'); } diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index dc6a319d4..9a5a8f14d 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -591,6 +591,11 @@ public function testGetThreadIdNotFound() { $result = $this->Entry->getThreadId(999); } + public function testUpdateNoId() { + $this->expectException('InvalidArgumentException'); + $this->Entry->update([]); + } + /** * setUp method * From 95b7b3615e7310fa465811e40320056130413403 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Tue, 16 Jul 2013 13:18:19 +0200 Subject: [PATCH 04/72] decoupled Entry from Settings config --- app/Model/Entry.php | 31 ++++++++++++++++++++++++----- app/Test/Case/Model/EntryTest.php | 24 +++++++++++++++------- app/Test/Fixture/SettingFixture.php | 6 +++++- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 921e8710e..82f02965c 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -206,6 +206,12 @@ class Entry extends AppModel { ] ]; + protected $_isInitialized = false; + + protected $_editPeriod = 1200; + + protected $_subjectMaxLenght = 100; + /** * Caching for isRoot() * @@ -556,10 +562,10 @@ public function beforeFind($queryData) { public function beforeValidate($options = array()) { parent::beforeValidate($options); + $this->_initialize(); - $this->validate['subject']['maxLength']['rule'][1] = Configure::read( - 'Saito.Settings.subject_maxlength' - ); + $this->validate['subject']['maxLength']['rule'][1] = + $this->_subjectMaxLenght; //* in n/t posting delete unnecessary body text if (isset($this->data['Entry']['text'])) { @@ -765,6 +771,8 @@ protected function _addAdditionalFields(&$entries) { * @return boolean */ public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) { + $this->_initialize(); + if ($CurrentUser !== null) { $this->_CurrentUser = $CurrentUser; } @@ -781,8 +789,7 @@ public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) $verboten = true; - $editPeriod = Configure::read('Saito.Settings.edit_period') * 60; - $expired = strtotime($entry['Entry']['time']) + $editPeriod; + $expired = strtotime($entry['Entry']['time']) + $this->_editPeriod; $isOverEditLimit = time() > $expired; $isCurrentUsersPosting = (int)$this->_CurrentUser->getId() @@ -1102,4 +1109,18 @@ protected function _threadChangeCategory($tid = null, $new_category_id = null) { ); return $out; } + + protected function _initialize() { + if ($this->_isInitialized) { + return; + } + $appSettings = Configure::read('Saito.Settings'); + if(isset($appSettings['edit_period'])) { + $this->_editPeriod = $appSettings['edit_period'] * 60; + } + if(isset($appSettings['subject_maxlength'])) { + $this->_subjectMaxLenght = $appSettings['subject_maxlength']; + } + $this->_isInitialized = true; + } } \ No newline at end of file diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index 9a5a8f14d..acdfec0cd 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -6,6 +6,7 @@ class EntryMock extends Entry { public $_CurrentUser; + public $_editPeriod; } class EntryTest extends CakeTestCase { @@ -340,11 +341,12 @@ public function testIsAnsweringForbidden() { } public function testIsEditingForbiddenSuccess() { + $this->Entry->_editPeriod = 1200; $entry = array( 'Entry' => array( 'user_id' => 1, 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60 ) + 1), + time() - $this->Entry->_editPeriod + 1), 'locked' => 0, ) ); @@ -414,13 +416,17 @@ public function testIsEditingForbiddenWrongUser() { } public function testIsEditingForbiddenToLate() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60 ) - 1), + $this->Entry->_editPeriod = 1200; + $entry = [ + 'Entry' => [ + 'user_id' => 1, + 'locked' => false, + 'time' => strftime( + "%c", + time() - $this->Entry->_editPeriod - 1 ) - ); + ] + ]; $user = array( 'id' => 1, 'user_type' => 'user', @@ -596,6 +602,10 @@ public function testUpdateNoId() { $this->Entry->update([]); } + public function testUpdateEntryDoesNotExist() { + $this->Entry->update(['Entry' => ['id' => 999, 'subject' => 'foo']]); + } + /** * setUp method * diff --git a/app/Test/Fixture/SettingFixture.php b/app/Test/Fixture/SettingFixture.php index 42061a95c..c46f462da 100755 --- a/app/Test/Fixture/SettingFixture.php +++ b/app/Test/Fixture/SettingFixture.php @@ -61,7 +61,11 @@ class SettingFixture extends CakeTestFixture { array( 'name' => 'thread_depth_indent', 'value' => 25, - ) + ), + [ + 'name' => 'edit_period', + 'value' => 20, + ] ); } From 0357af4703c8905226d4ce0c0c8782a61be7af82 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Tue, 16 Jul 2013 17:06:08 +0200 Subject: [PATCH 05/72] refactores validation of of Entry subject length --- app/Model/Entry.php | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 82f02965c..6c3ddf26d 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -81,8 +81,7 @@ class Entry extends AppModel { 'rule' => 'notEmpty', ), 'maxLength' => array( - // set to Saito admin pref in beforeValidate() - 'rule' => array('maxLength', 100), + 'rule' => 'validateSubjectMaxLength', ), ), 'category' => array( @@ -219,6 +218,11 @@ class Entry extends AppModel { */ protected $_isRoot = []; + public function __construct($id = false, $table = null, $ds = null) { + $this->_initialize(); + return parent::__construct($id, $table, $ds); + } + public function getRecentEntries(array $options = [], SaitoUser $User) { Stopwatch::start('Model->User->getRecentEntries()'); @@ -562,10 +566,6 @@ public function beforeFind($queryData) { public function beforeValidate($options = array()) { parent::beforeValidate($options); - $this->_initialize(); - - $this->validate['subject']['maxLength']['rule'][1] = - $this->_subjectMaxLenght; //* in n/t posting delete unnecessary body text if (isset($this->data['Entry']['text'])) { @@ -771,7 +771,6 @@ protected function _addAdditionalFields(&$entries) { * @return boolean */ public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) { - $this->_initialize(); if ($CurrentUser !== null) { $this->_CurrentUser = $CurrentUser; @@ -1085,6 +1084,21 @@ public function validateCategoryIsAllowed($check) { return true; } + /** + * + * + * Don't use Cake's build in maxLength. Dynamically setting the length + * afterwards in $this->validates it is a bag of hurt with race + * conditions in ModelValidator::_parseRules() when checking + * if ($this->_validate === $this->_model->validate) is true. + * + * @param $check + * @return bool + */ + public function validateSubjectMaxLength($check) { + return mb_strlen($check['subject']) < $this->_subjectMaxLenght; + } + /** * Changes the category of a thread. * @@ -1119,7 +1133,7 @@ protected function _initialize() { $this->_editPeriod = $appSettings['edit_period'] * 60; } if(isset($appSettings['subject_maxlength'])) { - $this->_subjectMaxLenght = $appSettings['subject_maxlength']; + $this->_subjectMaxLenght = (int)$appSettings['subject_maxlength']; } $this->_isInitialized = true; } From fbb426954db5818ebc93b67b6a33b9855d8b52e5 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:02:47 +0200 Subject: [PATCH 06/72] replaces getUnsanitized with _findUnsanitized in Entry --- app/Controller/EntriesController.php | 15 +++++++++--- app/Model/Entry.php | 34 ++++++++++++++++------------ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index d70e5cb2c..7fd612c2f 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -310,7 +310,10 @@ public function add($id = null) { $this->request->data = null; if ($id !== null) { - $this->request->data = $this->Entry->getUnsanitized($id); + $this->request->data = $this->Entry->find( + 'unsanitized', + ['conditions' => ['Entry.id' => $id]] + ); } if (!empty($this->request->data)): // answer to existing posting @@ -376,7 +379,6 @@ public function edit($id = null) { throw new BadRequestException(); } - $old_entry = $this->Entry->getUnsanitized((int)$id); if (!$old_entry) { throw new NotFoundException(); @@ -416,6 +418,10 @@ public function edit($id = null) { } } + $old_entry = $this->Entry->find( + 'unsanitized', + ['conditions' => ['Entry.id' => $id]] + ); $forbiddenAsNormalUser = $this->Entry->isEditingForbidden($old_entry, $this->CurrentUser->mockUserType('user')); if($forbiddenAsNormalUser) { $this->Session->setFlash(__('notice_you_are_editing_as_mod'), 'flash/warning'); @@ -426,7 +432,10 @@ public function edit($id = null) { // get text of parent entry for citation $parent_entry_id = $old_entry['Entry']['pid']; if ($parent_entry_id > 0) { - $parent_entry = $this->Entry->getUnsanitized($parent_entry_id); + $parent_entry = $this->Entry->find( + 'unsanitized', + ['conditions' => ['Entry.id' => $parent_entry_id]] + ); $this->set('citeText', $parent_entry['Entry']['text']); } diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 6c3ddf26d..6f8bff9fe 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -36,8 +36,9 @@ class Entry extends AppModel { ]; public $findMethods = array( - 'feed' => true, - 'entry' => true + 'feed' => true, + 'entry' => true, + 'unsanitized' => true ); /** @@ -905,7 +906,10 @@ public function prepare(&$data, array $options = []) { } else { $pid = $data[$this->alias]['pid']; } - $parent = $this->getUnsanitized($pid); + $parent = $this->find( + 'unsanitized', + ['conditions' => [$this->alias . '.id' => $pid]] + ); if ($parent === false) { throw new InvalidArgumentException; } @@ -946,20 +950,22 @@ protected function _preFilterFields(&$data, $fields) { } } - public function getUnsanitized($id) { - $this->sanitize(false); - return $this->find( - 'entry', - [ - 'contain' => ['User', 'Category'], - 'conditions' => ['Entry.id' => $id] - ] - ); + protected function _findUnsanitized($state, $query, $results = []) { + if ($state === 'before') { + $query['sanitize'] = false; + } + return $this->_findEntry($state, $query, $results); } - protected function _findEntry($state, $query, $results = array()) { + protected function _findEntry($state, $query, $results = []) { if ($state === 'before') { - $query['contain'] = array('User', 'Category'); + if (isset($query['sanitize'])) { + if ($query['sanitize'] === false) { + $this->sanitize(false); + } + unset($query['sanitize']); + } + $query['contain'] = ['User', 'Category']; $query['fields'] = $this->threadLineFieldList . ',' . $this->showEntryFieldListAdditional; return $query; } From 4112113f6ce0d53e92e5bc80e715291e32ee358a Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:10:36 +0200 Subject: [PATCH 07/72] type hint --- app/Model/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 6f8bff9fe..7da39eb96 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -657,7 +657,7 @@ public function anonymizeEntriesFromUser($user_id) { * @param misc $context Arbitrary data for the function. Useful for providing $this context. * @param array $tree The whole tree. */ - public static function mapTreeElements(&$leafs, $func, $context = null, &$tree = null) { + public static function mapTreeElements(&$leafs, callable $func, $context = null, &$tree = null) { if ($tree === null) { $tree = & $leafs; } From 0477daae301bdd898139cfc6c2a95fdcfe5ae06f Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:10:47 +0200 Subject: [PATCH 08/72] code formatting --- app/Model/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 7da39eb96..5d670fa74 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -102,7 +102,7 @@ class Entry extends AppModel { 'views' => array( 'rule' => array('comparison', '>=', 0), ), - 'name' => array(), + 'name' => array() ); protected $fieldsToSanitize = array( From 949e5de65e86b57fdb43543f453ef06a846fc0ba Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:11:30 +0200 Subject: [PATCH 09/72] refactores Entry::isEditingAllowed --- app/Model/Entry.php | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 5d670fa74..3902645e3 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -766,39 +766,43 @@ protected function _addAdditionalFields(&$entries) { /** * Check if someone is allowed to edit an entry - * - * @param array $entry - * @param SaitoUser $user - * @return boolean */ - public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) { + public function isEditingForbidden($entry, SaitoUser $User = null) { - if ($CurrentUser !== null) { - $this->_CurrentUser = $CurrentUser; + if ($User === null) { + $User = $this->_CurrentUser; } // Anon - if ($this->_CurrentUser->isLoggedIn() === false) { + if ($User->isLoggedIn() !== true) { return true; } // Admins - if ($this->_CurrentUser->isAdmin()) { + if ($User->isAdmin()) { return false; } $verboten = true; + if (!isset($entry['Entry'])) { + $entry = $this->find('entry', ['conditions' => [$this->alias.'.id' => $entry]]); + } + + if (empty($entry)) { + throw new Exception(sprintf('Entry %s not found.', $entry)); + } + $expired = strtotime($entry['Entry']['time']) + $this->_editPeriod; $isOverEditLimit = time() > $expired; - $isCurrentUsersPosting = (int)$this->_CurrentUser->getId() + $isUsersPosting = (int)$User->getId() === (int)$entry['Entry']['user_id']; - if ($this->_CurrentUser->isMod()) { + if ($User->isMod()) { // Mods // @todo mods don't edit admin posts - if ($isCurrentUsersPosting && $isOverEditLimit && + if ($isUsersPosting && $isOverEditLimit && /* Mods should be able to edit their own posts if they are pinned * * @todo this opens a 'mod can pin and then edit root entries'-loophole, @@ -815,7 +819,7 @@ public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) } else { // Users - if ($isCurrentUsersPosting === false) { + if ($isUsersPosting === false) { $verboten = 'user'; } elseif ($isOverEditLimit) { $verboten = 'time'; From 532d1c334ad46ece51c6221983d8c077c99c032a Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:18:52 +0200 Subject: [PATCH 10/72] new Entry::get() --- app/Controller/EntriesController.php | 19 +++++-------------- app/Model/Entry.php | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 7fd612c2f..939b6dc2c 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -190,8 +190,8 @@ public function view($id=null) { return $this->redirect(array( 'action' => 'index' )); } - $this->Entry->id = $id; - $this->request->data = $this->Entry->find('entry', array('conditions' => array('Entry.id' => $id))); + $this->Entry->id = $id; + $this->request->data = $this->Entry->get($id); //* redirect if posting doesn't exists if ( $this->request->data == false ): @@ -310,10 +310,7 @@ public function add($id = null) { $this->request->data = null; if ($id !== null) { - $this->request->data = $this->Entry->find( - 'unsanitized', - ['conditions' => ['Entry.id' => $id]] - ); + $this->request->data = $this->Entry->get($id, true); } if (!empty($this->request->data)): // answer to existing posting @@ -418,10 +415,7 @@ public function edit($id = null) { } } - $old_entry = $this->Entry->find( - 'unsanitized', - ['conditions' => ['Entry.id' => $id]] - ); + $old_entry = $this->Entry->get($id, true); $forbiddenAsNormalUser = $this->Entry->isEditingForbidden($old_entry, $this->CurrentUser->mockUserType('user')); if($forbiddenAsNormalUser) { $this->Session->setFlash(__('notice_you_are_editing_as_mod'), 'flash/warning'); @@ -432,10 +426,7 @@ public function edit($id = null) { // get text of parent entry for citation $parent_entry_id = $old_entry['Entry']['pid']; if ($parent_entry_id > 0) { - $parent_entry = $this->Entry->find( - 'unsanitized', - ['conditions' => ['Entry.id' => $parent_entry_id]] - ); + $parent_entry = $this->Entry->get($parent_entry_id, true); $this->set('citeText', $parent_entry['Entry']['text']); } diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 3902645e3..7536e5dda 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -291,6 +291,19 @@ public function getThreadId($id) { return $entry['Entry']['tid']; } + /** + * Shorthand for reading an entry + */ + public function get($id, $unsanitized = false) { + if (isset($entry[$this->alias]['id'])) { + $id = $entry[$this->alias]['id']; + } + return $this->find( + ($unsanitized) ? 'unsanitized' : 'entry', + ['conditions' => [$this->alias.'.id' => $id]] + ); + } + public function getParentId($id) { $entry = $this->find( 'first', @@ -786,7 +799,7 @@ public function isEditingForbidden($entry, SaitoUser $User = null) { $verboten = true; if (!isset($entry['Entry'])) { - $entry = $this->find('entry', ['conditions' => [$this->alias.'.id' => $entry]]); + $entry = $this->get($entry); } if (empty($entry)) { @@ -910,10 +923,7 @@ public function prepare(&$data, array $options = []) { } else { $pid = $data[$this->alias]['pid']; } - $parent = $this->find( - 'unsanitized', - ['conditions' => [$this->alias . '.id' => $pid]] - ); + $parent = $this->get($pid, true); if ($parent === false) { throw new InvalidArgumentException; } From 374d40798f557be62820173478087435116263db Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:50:42 +0200 Subject: [PATCH 11/72] new Entry::get() --- app/Controller/EntriesController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 939b6dc2c..e2ae99e02 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -416,8 +416,7 @@ public function edit($id = null) { } $old_entry = $this->Entry->get($id, true); - $forbiddenAsNormalUser = $this->Entry->isEditingForbidden($old_entry, $this->CurrentUser->mockUserType('user')); - if($forbiddenAsNormalUser) { + if($old_entry['rights']['isEditingAsUserForbidden']) { $this->Session->setFlash(__('notice_you_are_editing_as_mod'), 'flash/warning'); } From 893f7f8657760d280c61e24dd00ea2ef3d76432e Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:57:39 +0200 Subject: [PATCH 12/72] code refactoring --- app/Controller/EntriesController.php | 34 +++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index e2ae99e02..44023419f 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -376,24 +376,24 @@ public function edit($id = null) { throw new BadRequestException(); } - - if (!$old_entry) { + $oldEntry = $this->Entry->get($id, true); + if (!$oldEntry) { throw new NotFoundException(); } - $forbidden = $this->Entry->isEditingForbidden($old_entry, $this->CurrentUser); - - switch ($forbidden) { + switch ($oldEntry['rights']['isEditingForbidden']) { case 'time': $this->Session->setFlash( 'Stand by your word bro\', it\'s too late. @lo', 'flash/error' ); - return $this->redirect(array('action' => 'view', $id)); + $this->redirect(['action' => 'view', $id]); + return; break; case 'user': $this->Session->setFlash('Not your horse, Hoss! @lo', 'flash/error'); - return $this->redirect(array('action' => 'view', $id)); + $this->redirect(['action' => 'view', $id]); + return; break; case true : $this->Session->setFlash( @@ -403,27 +403,29 @@ public function edit($id = null) { return; } + // try to save edit if (!empty($this->request->data)) { $data = $this->request->data; $data['Entry']['id'] = $id; $new_entry = $this->Entry->update($data); if ($new_entry) { - $this->_afterNewEntry(am($this->request['data'], $old_entry)); - return $this->redirect(array('action' => 'view', $id)); + $this->_afterNewEntry(am($this->request['data'], $oldEntry)); + $this->redirect(['action' => 'view', $id]); + return; } else { $this->Session->setFlash(__('Something clogged the tubes. Could not save entry. Try again.')); } } - $old_entry = $this->Entry->get($id, true); - if($old_entry['rights']['isEditingAsUserForbidden']) { + // show editing form + if($oldEntry['rights']['isEditingAsUserForbidden']) { $this->Session->setFlash(__('notice_you_are_editing_as_mod'), 'flash/warning'); } - $this->request->data = am($old_entry, $this->request->data); + $this->request->data = am($oldEntry, $this->request->data); // get text of parent entry for citation - $parent_entry_id = $old_entry['Entry']['pid']; + $parent_entry_id = $oldEntry['Entry']['pid']; if ($parent_entry_id > 0) { $parent_entry = $this->Entry->get($parent_entry_id, true); $this->set('citeText', $parent_entry['Entry']['text']); @@ -431,15 +433,15 @@ public function edit($id = null) { // get notifications $notis = $this->Entry->Esevent->checkEventsForUser( - $old_entry['Entry']['user_id'], + $oldEntry['Entry']['user_id'], array( array( - 'subject' => $old_entry['Entry']['id'], + 'subject' => $oldEntry['Entry']['id'], 'event' => 'Model.Entry.replyToEntry', 'receiver' => 'EmailNotification', ), array( - 'subject' => $old_entry['Entry']['tid'], + 'subject' => $oldEntry['Entry']['tid'], 'event' => 'Model.Entry.replyToThread', 'receiver' => 'EmailNotification', ), From af65818af15e40ae50fe030b082c9d6e2ccce2cd Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 12:58:43 +0200 Subject: [PATCH 13/72] adds isEditingForbidden as validation rule to Entry --- app/Model/Entry.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 7536e5dda..25234b439 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -428,6 +428,15 @@ public function update($data, $CurrentUser = null) { $data[$this->alias]['edited'] = date('Y-m-d H:i:s'); $data[$this->alias]['edited_by'] = $this->_CurrentUser['username']; + + $this->validator()->add( + 'edited_by', + 'isEditingAllowed', + [ + 'rule' => 'validateEditingAllowed' + ] + ); + return $this->save($data); } @@ -1104,6 +1113,15 @@ public function validateCategoryIsAllowed($check) { return true; } + public function validateEditingAllowed($check) { + $forbidden = $this->isEditingForbidden($this->data['Entry']['id']); + if (is_bool($forbidden)) { + return !$forbidden; + } else { + return $forbidden; + } + } + /** * * From 1d2dff9da14947d2d28a29eeb032a0eed71caff0 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 13:31:34 +0200 Subject: [PATCH 14/72] update only existing entries --- app/Model/Entry.php | 7 ++++++- app/Test/Case/Model/EntryTest.php | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 25234b439..975e2d307 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -415,7 +415,12 @@ public function update($data, $CurrentUser = null) { } if (empty($data[$this->alias]['id'])) { - throw new InvalidArgumentException('No entry id in Entry::update()'); + throw new InvalidArgumentException('Missing entry id in arguments.'); + } + + $id = $data[$this->alias]['id']; + if (!$this->exists($id)) { + throw new NotFoundException(sprintf('Entry with id `%s` not found.', $id)); } $this->prepare($data, ['preFilterFields' => 'update']); diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index acdfec0cd..ce9db2de6 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -602,8 +602,14 @@ public function testUpdateNoId() { $this->Entry->update([]); } + /** + * Throw error if entry to update does not exist. + * + * Don't accidentally `create`. + */ public function testUpdateEntryDoesNotExist() { - $this->Entry->update(['Entry' => ['id' => 999, 'subject' => 'foo']]); + $this->expectException('NotFoundException'); + $this->Entry->update(['Entry' => ['id' => 999]]); } /** From a4e3e4f9e6aa349f809e410d87207913f96f2987 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 17 Jul 2013 13:32:07 +0200 Subject: [PATCH 15/72] fix failing test --- app/Test/Case/Model/SettingTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Test/Case/Model/SettingTest.php b/app/Test/Case/Model/SettingTest.php index 46158a441..a290488bb 100755 --- a/app/Test/Case/Model/SettingTest.php +++ b/app/Test/Case/Model/SettingTest.php @@ -24,7 +24,8 @@ class SettingTest extends CakeTestCase { 'subject_maxlength' => 100, 'tos_enabled' => 1, 'tos_url' => 'http://example.com/tos-url.html/', - 'thread_depth_indent' => '25' + 'thread_depth_indent' => '25', + 'edit_period' => '20' ); public function testAfterSave() { From 170a87c1397c6ca48481297bfe9f4649d95c6975 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 18 Jul 2013 11:02:07 +0200 Subject: [PATCH 16/72] CurrentUser::getId() now returns integer --- app/Controller/UploadsController.php | 2 +- app/Lib/SaitoUser.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controller/UploadsController.php b/app/Controller/UploadsController.php index ee352dd7b..7eda3887c 100755 --- a/app/Controller/UploadsController.php +++ b/app/Controller/UploadsController.php @@ -104,7 +104,7 @@ public function delete($id = null) { $this->Upload->id = (int)$id; $file = $this->Upload->read(); if ( $file - && $file['Upload']['user_id'] === $this->CurrentUser->getId() + && (int)$file['Upload']['user_id'] === $this->CurrentUser->getId() ) { if (!$this->Upload->delete(null, false)) { $this->JsData->addAppJsMessage( diff --git a/app/Lib/SaitoUser.php b/app/Lib/SaitoUser.php index 88413f6c2..5dc126b84 100755 --- a/app/Lib/SaitoUser.php +++ b/app/Lib/SaitoUser.php @@ -69,7 +69,7 @@ public function set($user) { if ( !empty($user) && is_array($user) ) : if (empty($user['id']) === false) { - $this->_id = $user['id']; + $this->_id = (int)$user['id']; $this->_isLoggedIn = true; } $this->_settings = $user; From 72984f1b14b9ea698b60ac57ddb1584821ae9f43 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 20 Jul 2013 21:08:25 +0200 Subject: [PATCH 17/72] use cakes internal timestamp function in AppHelper::getAssetTeimestamp() --- app/View/Helper/AppHelper.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/View/Helper/AppHelper.php b/app/View/Helper/AppHelper.php index b8b02d0cf..d1734ad75 100755 --- a/app/View/Helper/AppHelper.php +++ b/app/View/Helper/AppHelper.php @@ -33,13 +33,11 @@ class AppHelper extends Helper { public function getAssetTimestamp($path) { - $filepath = preg_replace( - '/^' . preg_quote($this->request->webroot, '/') . '/', - '', - $path - ); - $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath); - return @filemtime($webrootPath); + $pathWithTimestamp = $this->assetTimestamp($path); + // extracts integer unixtimestamp from `path/asset.ext? + if ($pathWithTimestamp) { + preg_match('/(?<=\?)[\d]+(?=$|\?|\&)/', $pathWithTimestamp, $matches); + return (int)$matches[0]; + } } - } \ No newline at end of file From 25e71878496960c43517ddbb46deca0c04c00652 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 10:18:10 +0200 Subject: [PATCH 18/72] code formatting --- app/View/Elements/entry/view_content.ctp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/View/Elements/entry/view_content.ctp b/app/View/Elements/entry/view_content.ctp index fd72b7d4d..8c6b3bd6a 100644 --- a/app/View/Elements/entry/view_content.ctp +++ b/app/View/Elements/entry/view_content.ctp @@ -24,7 +24,7 @@ echo $subject; } ?> - – + – isLoggedIn()) : ?> Html->link( @@ -48,7 +48,7 @@ TimeH->formatTime($entry['Entry']['time']); ?>">TimeH->formatTime($entry['Entry']['time'], 'glasen'); ?>, */ ?> - TimeH->formatTime($entry['Entry']['time']); if (isset($entry['Entry']['edited_by']) && !is_null($entry['Entry']['edited_by']) && strtotime($entry['Entry']['edited']) > strtotime($entry['Entry']['time']) + ( Configure::read('Saito.Settings.edit_delay') * 60 ) @@ -72,7 +72,7 @@ } if ( Configure::read('Saito.Settings.store_ip') && $CurrentUser->isMod() ): ?>, IP: - From dfb86958eda7dcefb2a0816a5bf24ca4d83a7202 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 10:19:05 +0200 Subject: [PATCH 19/72] Only use full BBCode parser if there is something to parse performance --- app/View/Helper/BbcodeHelper.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/View/Helper/BbcodeHelper.php b/app/View/Helper/BbcodeHelper.php index 8cffde80e..dfd00c442 100644 --- a/app/View/Helper/BbcodeHelper.php +++ b/app/View/Helper/BbcodeHelper.php @@ -114,14 +114,16 @@ public function beforeRender($viewFile) { * @param array $options * @return string */ - public function parse($string, array $options = array( )) { - Stopwatch::start('Bbcode::parse'); - $this->_initParser($options); - $this->_tagElCounter = 0; - $this->_tagEls = array(); - $string = $this->_Parser->parse($string); - $string = $this->_detaginize($string); - Stopwatch::stop('Bbcode::parse'); + public function parse($string, array $options = []) { + if (empty($string) === false && $string !== 'n/t') { + Stopwatch::start('Bbcode::parse'); + $this->_initParser($options); + $this->_tagElCounter = 0; + $this->_tagEls = array(); + $string = $this->_Parser->parse($string); + $string = $this->_detaginize($string); + Stopwatch::stop('Bbcode::parse'); + } return $string; } From 6168d2da7efc7250872386ea49d90f2f11fc7ca3 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 12:18:50 +0200 Subject: [PATCH 20/72] refactored EmailNotificationComponent DRY --- .../Component/EmailNotificationComponent.php | 158 ++++++++++-------- 1 file changed, 89 insertions(+), 69 deletions(-) diff --git a/app/Controller/Component/EmailNotificationComponent.php b/app/Controller/Component/EmailNotificationComponent.php index 2f552cf13..e31cb2086 100644 --- a/app/Controller/Component/EmailNotificationComponent.php +++ b/app/Controller/Component/EmailNotificationComponent.php @@ -39,65 +39,66 @@ public function dispatchEvent($event) { } protected function _modelEntryReplyToThread($event, array $recipients) { - // get parent entry - foreach ( $recipients as $recipient ): - // don't send answer if new entry belongs to the user itself - if ( Configure::read('debug') === 0 && (int)$recipient['id'] === (int)$event->data['data']['Entry']['user_id'] ) { - continue; - } - $event->subject()->contain(); - $rootEntry = $event->subject()->findById($event->data['data']['Entry']['tid']); - try { - $this->_Controller->SaitoEmail->email(array( - 'recipient' => array('User' => $recipient), - 'subject' => __('New reply to "%s"', $rootEntry['Entry']['subject']), - 'sender' => array( - 'User' => array( - 'user_email' => Configure::read('Saito.Settings.forum_email'), - 'username' => Configure::read('Saito.Settings.forum_name')), - ), - 'template' => Configure::read('Config.language') . DS . 'notification-model-entry-afterReply', - 'viewVars' => array( - 'recipient' => $recipient, - 'parentEntry' => $rootEntry, - 'newEntry' => $event->data['data'], - 'notification' => $recipient['Esnotification'], - ), - )); - } catch ( Exception $exc ) { + $event->subject()->contain(); + $rootEntry = $event->subject()->findById($event->data['data']['Entry']['tid']); + $config = [ + 'subject' => __( + 'New reply to "%s"', + $rootEntry['Entry']['subject'] + ), + 'sender' => array( + 'User' => array( + 'user_email' => Configure::read('Saito.Settings.forum_email'), + 'username' => Configure::read('Saito.Settings.forum_name') + ), + ), + 'template' => Configure::read( + 'Config.language' + ) . DS . 'notification-model-entry-afterReply', + 'viewVars' => array( + 'parentEntry' => $rootEntry, + 'newEntry' => $event->data['data'], + ), + ]; + foreach ( $recipients as $recipient ): + if ($this->_shouldRecipientReceiveReplyMessage($recipient, $event->data['data']['Entry'])) { + $config['recipient'] = ['User' => $recipient]; + $config['viewVars']['recipient'] = $recipient; + $config['viewVars']['notification'] = $recipient['Esnotification']; + $this->_email($config); } endforeach; } protected function _modelEntryReplyToEntry($event, array $recipients) { - // get parent entry + $event->subject()->contain(); + $parentEntry = $event->subject()->findById($event->data['data']['Entry']['pid']); + $config = [ + 'subject' => __( + 'New reply to "%s"', + $parentEntry['Entry']['subject'] + ), + 'sender' => array( + 'User' => array( + 'user_email' => Configure::read('Saito.Settings.forum_email'), + 'username' => Configure::read('Saito.Settings.forum_name') + ), + ), + 'template' => Configure::read( + 'Config.language' + ) . DS . 'notification-model-entry-afterReply', + 'viewVars' => array( + 'parentEntry' => $parentEntry, + 'newEntry' => $event->data['data'] + ) + ]; foreach ( $recipients as $recipient ): - // don't send answer if new entry belongs to the user itself - if ( Configure::read('debug') === 0 && (int)$recipient['id'] === (int)$event->data['data']['Entry']['user_id'] ) { - continue; - } - $event->subject()->contain(); - $parentEntry = $event->subject()->findById($event->data['data']['Entry']['pid']); - try { - $this->_Controller->SaitoEmail->email(array( - 'recipient' => array('User' => $recipient), - 'subject' => __('New reply to "%s"', $parentEntry['Entry']['subject']), - 'sender' => array( - 'User' => array( - 'user_email' => Configure::read('Saito.Settings.forum_email'), - 'username' => Configure::read('Saito.Settings.forum_name')), - ), - 'template' => Configure::read('Config.language') . DS . 'notification-model-entry-afterReply', - 'viewVars' => array( - 'recipient' => $recipient, - 'parentEntry' => $parentEntry, - 'newEntry' => $event->data['data'], - 'notification' => $recipient['Esnotification'], - ), - )); - } catch ( Exception $exc ) { - + if ($this->_shouldRecipientReceiveReplyMessage($recipient, $event->data['data']['Entry'])) { + $config['recipient'] = ['User' => $recipient]; + $config['viewVars']['recipient'] = $recipient; + $config['viewVars']['notification'] = $recipient['Esnotification']; + $this->_email($config); } endforeach; } @@ -107,25 +108,46 @@ public function userActivatedAdminNotice($event) { if ( !is_array($recipients) ) return; $new_user = $event->data['User']; + $config = [ + 'subject' => __('Successfull registration'), + 'sender' => array( + 'User' => array( + 'user_email' => Configure::read('Saito.Settings.forum_email'), + 'username' => Configure::read('Saito.Settings.forum_name') + ), + ), + 'template' => Configure::read( + 'Config.language' + ) . DS . 'notification-admin-user_activated', + 'viewVars' => array('user' => $new_user, 'ip' => env('REMOTE_ADDR')), + + ]; foreach ( $recipients as $recipient ) : - try { - $this->_Controller->SaitoEmail->email(array( - 'recipient' => $recipient, - 'subject' => __('Successfull registration'), - 'sender' => array( - 'User' => array( - 'user_email' => Configure::read('Saito.Settings.forum_email'), - 'username' => Configure::read('Saito.Settings.forum_name')), - ), - 'template' => Configure::read('Config.language') . DS . 'notification-admin-user_activated', - 'viewVars' => array('user' => $new_user, 'ip' => env('REMOTE_ADDR')), - )); - } catch ( Exception $exc ) { - - } + $config['recipient'] = $recipient; + $this->_email($config); endforeach; } + protected function _shouldRecipientReceiveReplyMessage($entry, $recipient) { + if (Configure::read('debug') === 0 && + // don't send answer if new entry belongs to the user itself + (int)$recipient['id'] === (int)$entry['user_id'] + ) { + return false; + } else { + return true; + } + + } + + protected function _email($config) { + try { + $this->_Controller->SaitoEmail->email($config); + } catch (Exception $exc) { + + } + } + protected function _debug($event, array $receivers) { debug($event); foreach ( $receivers as $receiver ) { @@ -134,5 +156,3 @@ protected function _debug($event, array $receivers) { } } - -?> \ No newline at end of file From 0f55f71d5371d1b6e8445af48077a411682e1626 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 12:45:03 +0200 Subject: [PATCH 21/72] log error in EmailNotificationComponent --- app/Controller/Component/EmailNotificationComponent.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Controller/Component/EmailNotificationComponent.php b/app/Controller/Component/EmailNotificationComponent.php index e31cb2086..dab187a31 100644 --- a/app/Controller/Component/EmailNotificationComponent.php +++ b/app/Controller/Component/EmailNotificationComponent.php @@ -144,7 +144,13 @@ protected function _email($config) { try { $this->_Controller->SaitoEmail->email($config); } catch (Exception $exc) { - + $this->log( + sprintf( + "Error %s in EmailNotificationComponent::_email() with %s", + $exc, + print_r($config, true) + ) + ); } } From 37162ce22af593a3fb27b0c4ad230eb4d6195994 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 12:49:00 +0200 Subject: [PATCH 22/72] pass full Entry on creation to event listeners --- app/Model/Entry.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 975e2d307..f4b7e13c5 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -362,9 +362,9 @@ public function createPosting($data, $CurrentUser = null) { return false; } $new_posting_id = $this->id; - if ($new_posting === true) { - $new_posting = $this->read(); - } + $this->contain(); + // make sure we pass the complete ['Entry'] dataset to events + $new_posting = $this->read(); if ($this->isRoot($data)) { // thread-id of new thread is its own id @@ -378,6 +378,7 @@ public function createPosting($data, $CurrentUser = null) { } else { // update last answer time of root entry $this->id = $new_posting[$this->alias]['tid']; + $this->read(null); $this->set('last_answer', $new_posting[$this->alias]['last_answer']); if ($this->save() === false) { // @td raise error and/or roll back new entry From 2d5ab7d1df55d6713a0218615c9109be10a08c3a Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 12:52:06 +0200 Subject: [PATCH 23/72] use Cake 2.4 explicit model::clear instead of read(null) --- app/Model/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index f4b7e13c5..b255b9606 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -377,8 +377,8 @@ public function createPosting($data, $CurrentUser = null) { } } else { // update last answer time of root entry + $this->clear(); $this->id = $new_posting[$this->alias]['tid']; - $this->read(null); $this->set('last_answer', $new_posting[$this->alias]['last_answer']); if ($this->save() === false) { // @td raise error and/or roll back new entry From 398832fe16ea196bc85017c067687ea02b2dc10c Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 13:05:49 +0200 Subject: [PATCH 24/72] fixes testcases --- app/Test/Case/Model/EntryTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index ce9db2de6..8d520ef5d 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -39,10 +39,10 @@ public function testCreateCategoryThreadCounterUpdate() { ['getMaxAccession', 'getId'], [new ComponentCollection] ); - $SaitoUser->expects($this->once()) + $SaitoUser->expects($this->any()) ->method('getMaxAccession') ->will($this->returnValue(2)); - $SaitoUser->expects($this->once()) + $SaitoUser->expects($this->any()) ->method('getId') ->will($this->returnValue(1)); $this->Entry->_CurrentUser = $SaitoUser; From 4b6da1da3e138b54d9606c5e1376e832a291dcf4 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 13:07:06 +0200 Subject: [PATCH 25/72] adds `locked` field to Entry fixture data --- .../Case/Controller/EntriesControllerTest.php | 50 +++++++++++-------- app/Test/Fixture/EntryFixture.php | 13 ++++- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 434d242b4..5bbdc17da 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -506,40 +506,50 @@ public function testEmptyCache() { $this->_loginUser(1); $data['Entry'] = array( - 'pid' => 5, - 'subject' => 'test', - 'category' => 4, + 'pid' => 7, + 'subject' => 'test' ); /* * test entries/add */ - $Entries->CacheTree - ->expects($this->once()) + $Entries->CacheTree->expects($this->once()) ->method('delete') - ->with($this->equalTo('4')); + ->with($this->equalTo('1')); + $Entries->Entry->contain(); $this->testAction( - '/entries/add/5', - array('data' => $data, 'method' => 'post') + '/entries/add/7', + ['data' => $data, 'method' => 'POST'] ); /* * Test entries/edit */ - $Entries = $this->generate('Entries', array( - 'components' => array( - 'CacheTree' => array('delete'), - ) - )); + $Entries = $this->generate( + 'Entries', + [ + 'components' => ['CacheTree' => ['delete']], + 'models' => ['Entry' => ['update']] + ] + ); - $Entries->CacheTree - ->expects($this->once()) - ->method('delete') - ->with($this->equalTo('4')); - $result = $this->testAction('/entries/edit/5', array( - 'data' => $data, - 'method' => 'post')); + $Entries->Entry->expects($this->once()) + ->method('update') + ->will($this->returnValue(true)); + + + $Entries->CacheTree->expects($this->once()) + ->method('delete') + ->with($this->equalTo('1')); + + $this->testAction( + '/entries/edit/7', + [ + 'data' => $data, + 'method' => 'post' + ] + ); } public function testPreviewLoggedIn() { diff --git a/app/Test/Fixture/EntryFixture.php b/app/Test/Fixture/EntryFixture.php index 8622781c8..dfd9d66f1 100755 --- a/app/Test/Fixture/EntryFixture.php +++ b/app/Test/Fixture/EntryFixture.php @@ -55,6 +55,7 @@ class EntryFixture extends CakeTestFixture { 'last_answer' => '2000-01-04 20:02:00', 'category' => 2, // accession = 0 'user_id' => 3, + 'locked' => 0 ), array( 'id' => 2, @@ -66,6 +67,7 @@ class EntryFixture extends CakeTestFixture { 'last_answer' => '2000-01-01 20:01:00', 'category' => 2, 'user_id' => 2, + 'locked' => 0 ), array( 'id' => 3, @@ -81,6 +83,7 @@ class EntryFixture extends CakeTestFixture { 'edited' => '2000-01-01 20:04:00', 'edited_by' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), array( 'id' => 7, @@ -94,6 +97,7 @@ class EntryFixture extends CakeTestFixture { 'user_id' => 3, 'name' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), array( 'id' => 8, @@ -107,6 +111,7 @@ class EntryFixture extends CakeTestFixture { 'user_id' => 3, 'name' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), array( 'id' => 9, @@ -120,6 +125,7 @@ class EntryFixture extends CakeTestFixture { 'user_id' => 3, 'name' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), //* thread 2 array( @@ -133,6 +139,7 @@ class EntryFixture extends CakeTestFixture { // accession = 1 'category' => 4, 'user_id' => 1, + 'locked' => 1 ), array( 'id' => 5, @@ -148,6 +155,7 @@ class EntryFixture extends CakeTestFixture { 'edited' => '0000-00-00 00:00:00', 'edited_by' => NULL, 'ip' => '1.1.1.1', + 'locked' => 1 ), // thread 3 array( @@ -158,14 +166,15 @@ class EntryFixture extends CakeTestFixture { 'tid' => 6, 'time' => '2000-01-01 11:00:00', 'last_answer' => '2000-01-01 11:00:00', + // accession = 2, 'category' => 1, 'user_id' => 1, 'name' => 'Alice', 'edited' => '0000-00-00 00:00:00', 'edited_by' => NULL, 'ip' => '1.1.1.3', - ), - + 'locked' => 0 + ) ); } From bb43e4a7ee324318564df1ef6f974fada93a789c Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Wed, 24 Jul 2013 13:38:41 +0200 Subject: [PATCH 26/72] adds 4th tread to Entry fixture --- .../Case/Controller/EntriesControllerTest.php | 15 +++++++-------- app/Test/Case/Model/EntryTest.php | 2 +- app/Test/Case/Model/UserTest.php | 4 ++-- app/Test/Fixture/EntryFixture.php | 15 ++++++++++++++- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 5bbdc17da..1de9457de 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -307,20 +307,19 @@ public function testCategoryChooserSingleCategory() { } public function testIndex() { - - $Entries = $this->generate('Entries'); + $this->generate('Entries'); $this->_logoutUser(); //* not logged in user - $result = $this->testAction('/entries/index', array('return' => 'vars')); + $result = $this->testAction('/entries/index', array('return' => 'vars')); $entries = $result['entries']; - $this->assertEqual(count($entries), 1); + $this->assertEqual(count($entries), 2); //* logged in user $this->_loginUser(3); - $result = $this->testAction('/entries/index', array('return' => 'vars')); + $result = $this->testAction('/entries/index', array('return' => 'vars')); $entries = $result['entries']; - $this->assertEqual(count($entries), 2); + $this->assertEqual(count($entries), 3); } public function testMergeNoSourceId() { @@ -688,8 +687,8 @@ public function testAppStats() { $this->assertEqual($headerCounter['user_online'], 1); $this->assertEqual($headerCounter['user'], 6); - $this->assertEqual($headerCounter['entries'], 9); - $this->assertEqual($headerCounter['threads'], 3); + $this->assertEqual($headerCounter['entries'], 10); + $this->assertEqual($headerCounter['threads'], 4); $this->assertEqual($headerCounter['user_registered'], 0); $this->assertEqual($headerCounter['user_anonymous'], 1); diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index 8d520ef5d..f1dd4e69e 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -290,7 +290,7 @@ public function testAnonymizeEntriesFromUser() { $this->assertEqual($result, $expected); // entries are now assigned to user_id 0 - $expected = 6; + $expected = 7; $result = $this->Entry->find('count', array( 'conditions' => array ('Entry.user_id' => 0) )); diff --git a/app/Test/Case/Model/UserTest.php b/app/Test/Case/Model/UserTest.php index 9305f355b..a20546057 100755 --- a/app/Test/Case/Model/UserTest.php +++ b/app/Test/Case/Model/UserTest.php @@ -151,9 +151,9 @@ public function testNumberOfEntry() { $result = $this->User->numberOfEntries(); $this->assertEqual($expected, $result); - //* two entries + //* multiple entries $this->User->id = 3; - $expected = 6; + $expected = 7; $result = $this->User->numberOfEntries(); $this->assertEqual($expected, $result); } diff --git a/app/Test/Fixture/EntryFixture.php b/app/Test/Fixture/EntryFixture.php index dfd9d66f1..98d998bf3 100755 --- a/app/Test/Fixture/EntryFixture.php +++ b/app/Test/Fixture/EntryFixture.php @@ -174,7 +174,20 @@ class EntryFixture extends CakeTestFixture { 'edited_by' => NULL, 'ip' => '1.1.1.3', 'locked' => 0 - ) + ), + // thread 4 public locked + [ + 'id' => 10, + 'subject' => 'First_Subject', + 'text' => 'First_Text', + 'pid' => 0, + 'tid' => 10, + 'time' => '2000-01-01 10:59:00', + 'last_answer' => '2000-01-01 10:59:00', + 'category' => 2, // accession = 0 + 'user_id' => 3, + 'locked' => 1 + ] ); } From b896aca9553e6a1707857168df6c281f3e7a6f5b Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 07:30:05 +0200 Subject: [PATCH 27/72] Renames CacheTreeEngine Interface --- app/Lib/CacheTree/CacheTreeAppCacheEngine.php | 4 ++-- ...eTreeCacheEngine.php => CacheTreeCacheEngineInterface.php} | 0 app/Lib/CacheTree/CacheTreeDbCacheEngine.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename app/Lib/CacheTree/{CacheTreeCacheEngine.php => CacheTreeCacheEngineInterface.php} (100%) diff --git a/app/Lib/CacheTree/CacheTreeAppCacheEngine.php b/app/Lib/CacheTree/CacheTreeAppCacheEngine.php index 7ccd2a7fe..49db7355b 100644 --- a/app/Lib/CacheTree/CacheTreeAppCacheEngine.php +++ b/app/Lib/CacheTree/CacheTreeAppCacheEngine.php @@ -1,8 +1,8 @@ Date: Thu, 25 Jul 2013 07:38:47 +0200 Subject: [PATCH 28/72] refactors CacheSupport::clearTrees into CacheSupport::clearTree --- .../Component/CacheSupportComponent.php | 16 +++++++--------- app/Controller/UsersController.php | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/Controller/Component/CacheSupportComponent.php b/app/Controller/Component/CacheSupportComponent.php index 4abb173d9..973eaed57 100644 --- a/app/Controller/Component/CacheSupportComponent.php +++ b/app/Controller/Component/CacheSupportComponent.php @@ -38,18 +38,16 @@ public function clearCake() { Cache::clearGroup('views'); } - public function clearTree($id) { - $this->_clearEntries(); - $this->CacheTree->delete($id); - } - - public function clearTrees() { - $this->_clearEntries(); - $this->CacheTree->reset(); } - protected function _clearEntries() { + public function clearTree($id = null) { Cache::clear(false, 'entries'); + $this->_clearEntries(); + if ($id === null) { + $this->CacheTree->reset(); + } else { + $this->CacheTree->delete($id); + } } /** diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index c13da9380..56ed8ce69 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -359,7 +359,7 @@ public function admin_delete($id = null) { elseif ($id == 1): $this->Session->setFlash(__("You can't delete the installation account."), 'flash/error'); elseif ($this->User->deleteAllExceptEntries($id)): - $this->CacheSupport->clearTrees(); + $this->CacheSupport->clearTree(); $this->Session->setFlash(__('User %s deleted.', $readUser['User']['username']), 'flash/notice'); $this->redirect('/'); return; From 69bea7b8b0123f966f553d9fd190fbdb0dc36840 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 07:40:39 +0200 Subject: [PATCH 29/72] amends cache interface renaming --- app/Lib/CacheTree/CacheTreeCacheEngineInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Lib/CacheTree/CacheTreeCacheEngineInterface.php b/app/Lib/CacheTree/CacheTreeCacheEngineInterface.php index e8384b039..54cf17df9 100644 --- a/app/Lib/CacheTree/CacheTreeCacheEngineInterface.php +++ b/app/Lib/CacheTree/CacheTreeCacheEngineInterface.php @@ -1,6 +1,6 @@ Date: Thu, 25 Jul 2013 07:51:40 +0200 Subject: [PATCH 30/72] refactors most of CacheSupport into Component independend class --- .../Component/CacheSupportComponent.php | 35 +++------ app/Lib/CacheSupport.php | 73 +++++++++++++++++++ 2 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 app/Lib/CacheSupport.php diff --git a/app/Controller/Component/CacheSupportComponent.php b/app/Controller/Component/CacheSupportComponent.php index 973eaed57..356a54397 100644 --- a/app/Controller/Component/CacheSupportComponent.php +++ b/app/Controller/Component/CacheSupportComponent.php @@ -1,6 +1,7 @@ _CacheSupport = new CacheSupport(); + parent::__construct($collection, $settings); + } + public function initialize(Controller $Controller) { $this->CacheTree->initialize($Controller); } @@ -21,28 +29,12 @@ public function shutdown(Controller $Controller) { } public function clearAll() { - $this->clearSaito(); - $this->clearApc(); - $this->clearCake(); - } - - public function clearSaito() { - Cache::clear(false, 'default'); - Cache::clear(false, 'short'); - $this->clearTrees(); - } - - public function clearCake() { - Cache::clearGroup('persistent'); - Cache::clearGroup('models'); - Cache::clearGroup('views'); - } - + $this->_CacheSupport->clear(); + $this->clearTree(); } public function clearTree($id = null) { Cache::clear(false, 'entries'); - $this->_clearEntries(); if ($id === null) { $this->CacheTree->reset(); } else { @@ -54,11 +46,6 @@ public function clearTree($id = null) { * Clears out the APC if available */ public function clearApc() { - if (function_exists('apc_store')) { - apc_clear_cache(); - apc_clear_cache('user'); - apc_clear_cache('opcode'); - } + $this->_CacheSupport->clear('Apc'); } - } diff --git a/app/Lib/CacheSupport.php b/app/Lib/CacheSupport.php new file mode 100644 index 000000000..86cd271a3 --- /dev/null +++ b/app/Lib/CacheSupport.php @@ -0,0 +1,73 @@ +addCache( + [ + 'Apc', + 'Cake', + 'Saito' + ] + ); + } + + public function clear($cache = null, $id = null) { + if ($cache === null) { + foreach ($this->_Caches as $Cachelet) { + $Cachelet->clear(); + } + } else { + $this->_Caches[$cache]->clear($id); + } + } + + public function addCache($cache) { + if (is_array($cache)) { + foreach ($cache as $name) { + $this->_addCachelet($name); + } + } else { + $this->_addCachelet($cache); + } + } + + protected function _addCachelet($cache) { + if (!isset($this->_Caches[$cache])) { + $cache_name = $cache . 'Cachelet'; + $this->_Caches[$cache] = new $cache_name; + } + } + } + + + interface Cachelets { + public function clear($id = null); + } + + class SaitoCachelet implements Cachelets { + public function clear($id = null) { + Cache::clear(false, 'default'); + Cache::clear(false, 'short'); + } + } + + class ApcCachelet implements Cachelets { + public function clear($id = null) { + if (function_exists('apc_store')) { + apc_clear_cache(); + apc_clear_cache('user'); + apc_clear_cache('opcode'); + } + } + } + + class CakeCachelet implements Cachelets { + public function clear($id = null) { + Cache::clearGroup('persistent'); + Cache::clearGroup('models'); + Cache::clearGroup('views'); + } + } \ No newline at end of file From 1dddbfc4e479c83ff142ff12e96b467b1464299c Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 07:55:11 +0200 Subject: [PATCH 31/72] assumes File as default cache engine in CacheTreeComponent instead of Apc --- app/Controller/Component/CacheTreeComponent.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Controller/Component/CacheTreeComponent.php b/app/Controller/Component/CacheTreeComponent.php index 5f49d7b47..6fb400d0b 100755 --- a/app/Controller/Component/CacheTreeComponent.php +++ b/app/Controller/Component/CacheTreeComponent.php @@ -36,10 +36,10 @@ public function initialize(Controller $Controller) { $this->_CurrentUser = $Controller->CurrentUser; $cache_config = Cache::settings(); - if ($cache_config['engine'] === 'File') { - $this->_CacheEngine = new CacheTreeDbCacheEngine; - } else { + if ($cache_config['engine'] === 'Apc') { $this->_CacheEngine = new CacheTreeAppCacheEngine; + } else { + $this->_CacheEngine = new CacheTreeDbCacheEngine; } if ( From 98bc48899ab93aff584386498e6d0e4b5e1c484c Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 08:37:36 +0200 Subject: [PATCH 32/72] refactors CacheTree from Component into Lib class --- .../Component/CacheTreeComponent.php | 188 +--------------- app/Lib/CacheTree/CacheTree.php | 201 ++++++++++++++++++ .../CacheTreeTest.php} | 58 +---- 3 files changed, 217 insertions(+), 230 deletions(-) create mode 100644 app/Lib/CacheTree/CacheTree.php rename app/Test/Case/{Controller/Component/CacheTreeComponentTest.php => Lib/CacheTreeTest.php} (86%) diff --git a/app/Controller/Component/CacheTreeComponent.php b/app/Controller/Component/CacheTreeComponent.php index 6fb400d0b..7c5797f50 100755 --- a/app/Controller/Component/CacheTreeComponent.php +++ b/app/Controller/Component/CacheTreeComponent.php @@ -1,8 +1,7 @@ _CurrentUser = $Controller->CurrentUser; - - $cache_config = Cache::settings(); - if ($cache_config['engine'] === 'Apc') { - $this->_CacheEngine = new CacheTreeAppCacheEngine; - } else { - $this->_CacheEngine = new CacheTreeDbCacheEngine; - } - - if ( - $Controller->params['controller'] === 'entries' && $Controller->params['action'] === 'index' - ) { - $this->_allowUpdate = true; - $this->_allowRead = true; - } - - if ( Configure::read('debug') > 1 || Configure::read('Saito.Cache.Thread') == FALSE ): - $this->_allowUpdate = false; - $this->_allowRead = false; - endif; - - $this->readCache(); + $this->_CacheTree = CacheTree::getInstance(); + $this->_CacheTree->initialize($Controller); } public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { - parent::beforeRedirect($Controller, $url); $this->saveCache(); } public function beforeRender(Controller $Controller) { - parent::beforeRender($Controller); - $Controller->set('CacheTree', $this); + $Controller->set('CacheTree', $this->_CacheTree); } public function shutdown(Controller $Controller) { - parent::shutdown($Controller); $this->saveCache(); } - public function isCacheUpdatable(array $entry) { - if ( !$this->_allowUpdate ) { - return false; + public function __call($method, $params) { + $proxy = [$this->_CacheTree, $method]; + if (is_callable($proxy)) { + return call_user_func_array($proxy, $params); } - return $this->_isEntryOldForUser($entry); } - - public function isCacheValid(array $entry) { - if ( !$this->_allowRead) { - return false; - } - - $isCacheValid = false; - - if ( isset($this->_validEntries[$entry['id']]) ): - return $this->_validEntries[$entry['id']]; - endif; - - if ( isset($this->_cachedEntries[$entry['id']]) && strtotime($entry['last_answer']) <= $this->_cachedEntries[$entry['id']]['metadata']['content_last_updated']) { - if ($this->_isEntryOldForUser($entry)) { - $isCacheValid = true; - } - } - $this->_validEntries[$entry['id']] = $isCacheValid; - return $isCacheValid; - } - - protected function _isEntryOldForUser(array $entry) { - if (!$this->_CurrentUser->isLoggedIn() - || strtotime($entry['last_answer']) < strtotime($this->_CurrentUser['last_refresh'])) { - return true; - } - return false; - } - - public function delete($id) { - $this->_isUpdated = TRUE; - $this->readCache(); - unset($this->_cachedEntries[$id]); - } - - public function reset() { - $this->_isUpdated = true; - $this->_cachedEntries = []; - } - - public function read($id = null) { - if ( !$this->_allowRead ) - return false; - if ( $id === null ) { - return $this->_cachedEntries; - } - - if ( isset($this->_cachedEntries[$id]) ) { - return $this->_cachedEntries[$id]['content']; - } - - return FALSE; - } - - /** - * Puts an entry into the cache - * - * @param type $id Entry-id - * @param type $content Content to be saved in the cache - * @param int $timestamp Unix timestamp - */ - public function update($id, $content, $timestamp = null) { - $now = time(); - if (!$timestamp) { - $timestamp = $now; - } - if (!$this->_allowUpdate) { return false; } - $this->_isUpdated = TRUE; - $this->readCache(); - $metadata = array( - 'created' => $now, - 'content_last_updated' => $timestamp, - ); - $data = array( 'metadata' => $metadata, 'content' => $content ); - $this->_cachedEntries[$id] = $data; - } - - public function readCache() { - if ( $this->_cachedEntries === NULL ): - Stopwatch::start('SaitoCacheTree->readCache()'); - $this->_cachedEntries = $this->_CacheEngine->read(); - - $depractionTime = time() - $this->_CacheEngine->getDeprecationSpan(); - - if(!empty($this->_cachedEntries)) { - foreach ($this->_cachedEntries as $id => $entry) { - if ($entry['metadata']['created'] < $depractionTime) { - unset($this->_cachedEntries[$id]); - $this->_isUpdated = TRUE; - } - } - } - Stopwatch::end('SaitoCacheTree->readCache()'); - endif; - } - - public function saveCache() { - if ( $this->_isUpdated === FALSE ) - return false; - - $this->_gc(); - $this->_CacheEngine->write((array)$this->_cachedEntries); - } - - /** - * Garbage collection - * - * Remove old entries from the cache. - */ - protected function _gc() { - if ( !$this->_cachedEntries ) - return false; - - $number_of_cached_entries = count($this->_cachedEntries); - if ( $number_of_cached_entries > $this->_maxNumberOfEntries ) { - // descending time sort - uasort($this->_cachedEntries, function($a, $b) { - if ($a['metadata']['content_last_updated'] == $b['metadata']['content_last_updated']) { - return 0; - } - return ($a['metadata']['content_last_updated'] < $b['metadata']['content_last_updated']) ? 1 : -1; - }); - $this->_cachedEntries = array_slice($this->_cachedEntries, 0, $this->_maxNumberOfEntries, true); - } - } - } \ No newline at end of file diff --git a/app/Lib/CacheTree/CacheTree.php b/app/Lib/CacheTree/CacheTree.php new file mode 100644 index 000000000..881fe9cbf --- /dev/null +++ b/app/Lib/CacheTree/CacheTree.php @@ -0,0 +1,201 @@ +_CurrentUser = $Controller->CurrentUser; + + $cache_config = Cache::settings(); + if ($cache_config['engine'] === 'Apc') { + $this->_CacheEngine = new CacheTreeAppCacheEngine; + } else { + $this->_CacheEngine = new CacheTreeDbCacheEngine; + } + + if ( + $Controller->params['controller'] === 'entries' && $Controller->params['action'] === 'index' + ) { + $this->_allowUpdate = true; + $this->_allowRead = true; + } + + if ( Configure::read('debug') > 1 || Configure::read('Saito.Cache.Thread') == FALSE ): + $this->_allowUpdate = false; + $this->_allowRead = false; + endif; + + $this->readCache(); + } + + public function isCacheUpdatable(array $entry) { + if ( !$this->_allowUpdate ) { + return false; + } + return $this->_isEntryOldForUser($entry); + } + + public function isCacheValid(array $entry) { + if ( !$this->_allowRead) { + return false; + } + + $isCacheValid = false; + + if ( isset($this->_validEntries[$entry['id']]) ): + return $this->_validEntries[$entry['id']]; + endif; + + if ( isset($this->_cachedEntries[$entry['id']]) && strtotime($entry['last_answer']) <= $this->_cachedEntries[$entry['id']]['metadata']['content_last_updated']) { + if ($this->_isEntryOldForUser($entry)) { + $isCacheValid = true; + } + } + $this->_validEntries[$entry['id']] = $isCacheValid; + return $isCacheValid; + } + + protected function _isEntryOldForUser(array $entry) { + if (!$this->_CurrentUser->isLoggedIn() + || strtotime($entry['last_answer']) < strtotime($this->_CurrentUser['last_refresh'])) { + return true; + } + return false; + } + + public function delete($id) { + $this->_isUpdated = TRUE; + $this->readCache(); + unset($this->_cachedEntries[$id]); + } + + public function reset() { + $this->_isUpdated = true; + $this->_cachedEntries = []; + } + + public function read($id = null) { + if ( !$this->_allowRead ) + return false; + if ( $id === null ) { + return $this->_cachedEntries; + } + + if ( isset($this->_cachedEntries[$id]) ) { + return $this->_cachedEntries[$id]['content']; + } + + return FALSE; + } + + /** + * Puts an entry into the cache + * + * @param type $id Entry-id + * @param type $content Content to be saved in the cache + * @param int $timestamp Unix timestamp + */ + public function update($id, $content, $timestamp = null) { + $now = time(); + if (!$timestamp) { + $timestamp = $now; + } + if (!$this->_allowUpdate) { return false; } + $this->_isUpdated = TRUE; + $this->readCache(); + $metadata = array( + 'created' => $now, + 'content_last_updated' => $timestamp, + ); + $data = array( 'metadata' => $metadata, 'content' => $content ); + $this->_cachedEntries[$id] = $data; + } + + public function readCache() { + if ( $this->_cachedEntries === NULL ): + Stopwatch::start('SaitoCacheTree->readCache()'); + $this->_cachedEntries = $this->_CacheEngine->read(); + + $depractionTime = time() - $this->_CacheEngine->getDeprecationSpan(); + + if(!empty($this->_cachedEntries)) { + foreach ($this->_cachedEntries as $id => $entry) { + if ($entry['metadata']['created'] < $depractionTime) { + unset($this->_cachedEntries[$id]); + $this->_isUpdated = TRUE; + } + } + } + Stopwatch::end('SaitoCacheTree->readCache()'); + endif; + } + + public function saveCache() { + if ( $this->_isUpdated === FALSE ) + return false; + + $this->_gc(); + $this->_CacheEngine->write((array)$this->_cachedEntries); + } + + /** + * Garbage collection + * + * Remove old entries from the cache. + */ + protected function _gc() { + if ( !$this->_cachedEntries ) + return false; + + $number_of_cached_entries = count($this->_cachedEntries); + if ( $number_of_cached_entries > $this->_maxNumberOfEntries ) { + // descending time sort + uasort($this->_cachedEntries, function($a, $b) { + if ($a['metadata']['content_last_updated'] == $b['metadata']['content_last_updated']) { + return 0; + } + return ($a['metadata']['content_last_updated'] < $b['metadata']['content_last_updated']) ? 1 : -1; + }); + $this->_cachedEntries = array_slice($this->_cachedEntries, 0, $this->_maxNumberOfEntries, true); + } + } + + + } \ No newline at end of file diff --git a/app/Test/Case/Controller/Component/CacheTreeComponentTest.php b/app/Test/Case/Lib/CacheTreeTest.php similarity index 86% rename from app/Test/Case/Controller/Component/CacheTreeComponentTest.php rename to app/Test/Case/Lib/CacheTreeTest.php index 4fad92ddd..ea9860cd7 100644 --- a/app/Test/Case/Controller/Component/CacheTreeComponentTest.php +++ b/app/Test/Case/Lib/CacheTreeTest.php @@ -1,9 +1,12 @@ _cachedEntries = $data; @@ -30,7 +33,7 @@ public function setAllowUpdate($state) { * CacheTreeComponent Test Case * */ - class CacheTreeComponentTest extends CakeTestCase { + class CacheTreeTest extends CakeTestCase { /** * setUp method @@ -39,8 +42,7 @@ class CacheTreeComponentTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $Collection = new ComponentCollection(); - $this->CacheTree = new CacheTreeComponentMock($Collection); + $this->CacheTree = new CacheTreeMock(); $cacheData = array( '1' => array( @@ -215,51 +217,5 @@ public function testReset() { $result = $this->CacheTree->read(); $this->assertEqual($result, []); } - - /** - * testDelete method - * - * @return void - */ - public function testDelete() { - - } - - /** - * testRead method - * - * @return void - */ - public function testRead() { - - } - - /** - * testUpdate method - * - * @return void - */ - public function testUpdate() { - - } - - /** - * testReadCache method - * - * @return void - */ - public function testReadCache() { - - } - - /** - * testSaveCache method - * - * @return void - */ - public function testSaveCache() { - - } - } From a5bb50b83446d72cd9dc4d76b5aaf5ad9a5f00ba Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 10:23:56 +0200 Subject: [PATCH 33/72] refactors CacheSupport --- app/Lib/CacheSupport.php | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/app/Lib/CacheSupport.php b/app/Lib/CacheSupport.php index 86cd271a3..c6c1d4a3e 100644 --- a/app/Lib/CacheSupport.php +++ b/app/Lib/CacheSupport.php @@ -7,9 +7,9 @@ class CacheSupport extends Object { public function __construct() { $this->addCache( [ - 'Apc', - 'Cake', - 'Saito' + 'Apc' => 'ApcCacheSupportCachelet', + 'Cake' => 'CakeCacheSupportCachelet', + 'Saito' => 'SaitoCacheSupportCachelet' ] ); } @@ -25,36 +25,31 @@ public function clear($cache = null, $id = null) { } public function addCache($cache) { - if (is_array($cache)) { - foreach ($cache as $name) { - $this->_addCachelet($name); - } - } else { - $this->_addCachelet($cache); + foreach ($cache as $key => $class_name) { + $this->_addCachelet($key, new $class_name); } } - protected function _addCachelet($cache) { - if (!isset($this->_Caches[$cache])) { - $cache_name = $cache . 'Cachelet'; - $this->_Caches[$cache] = new $cache_name; + protected function _addCachelet($key, CacheSupportCacheletInterface $cachelet) { + if (!isset($this->_Caches[$key])) { + $this->_Caches[$key] = $cachelet; } } } - interface Cachelets { + interface CacheSupportCacheletInterface { public function clear($id = null); } - class SaitoCachelet implements Cachelets { + class SaitoCacheSupportCachelet implements CacheSupportCacheletInterface { public function clear($id = null) { Cache::clear(false, 'default'); Cache::clear(false, 'short'); } } - class ApcCachelet implements Cachelets { + class ApcCacheSupportCachelet implements CacheSupportCacheletInterface { public function clear($id = null) { if (function_exists('apc_store')) { apc_clear_cache(); @@ -64,7 +59,7 @@ public function clear($id = null) { } } - class CakeCachelet implements Cachelets { + class CakeCacheSupportCachelet implements CacheSupportCacheletInterface { public function clear($id = null) { Cache::clearGroup('persistent'); Cache::clearGroup('models'); From 0e467e8bf4255bdac28b09664aee2986d8946015 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 11:20:07 +0200 Subject: [PATCH 34/72] refactors CacheTreeComponent into CacheSupportComponent --- .../Component/CacheSupportComponent.php | 42 +++++++------------ .../Component/CacheTreeComponent.php | 37 ---------------- app/Controller/EntriesController.php | 11 +++-- app/Controller/ToolsController.php | 4 +- app/Controller/UsersController.php | 2 +- app/Lib/CacheSupport.php | 26 ++++++++++-- app/Lib/CacheTree/CacheTree.php | 2 +- .../Case/Controller/EntriesControllerTest.php | 26 ++++++------ .../Case/Controller/ToolsControllerTest.php | 23 ++-------- 9 files changed, 62 insertions(+), 111 deletions(-) delete mode 100755 app/Controller/Component/CacheTreeComponent.php diff --git a/app/Controller/Component/CacheSupportComponent.php b/app/Controller/Component/CacheSupportComponent.php index 356a54397..49f94b8a9 100644 --- a/app/Controller/Component/CacheSupportComponent.php +++ b/app/Controller/Component/CacheSupportComponent.php @@ -2,50 +2,36 @@ App::uses('Component', 'Controller'); App::import('Lib', 'CacheSupport'); + App::uses('CacheTree', 'Lib/CacheTree'); class CacheSupportComponent extends Component { - public $components = [ - 'CacheTree' - ]; - protected $_CacheSupport; - public function __construct(ComponentCollection $collection, $settings = array()) { - $this->_CacheSupport = new CacheSupport(); - parent::__construct($collection, $settings); - } + public $CacheTree; public function initialize(Controller $Controller) { + $this->_CacheSupport = new CacheSupport(); + $this->CacheTree = CacheTree::getInstance(); $this->CacheTree->initialize($Controller); } - public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { - $this->CacheTree->beforeRedirect($Controller, $url, $status, $exit); + public function beforeRender(Controller $Controller) { + $Controller->set('CacheTree', $this->CacheTree); } - public function shutdown(Controller $Controller) { - $this->CacheTree->shutdown($Controller); + public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { + $this->CacheTree->saveCache(); } - public function clearAll() { - $this->_CacheSupport->clear(); - $this->clearTree(); + public function shutdown(Controller $Controller) { + $this->CacheTree->saveCache(); } - public function clearTree($id = null) { - Cache::clear(false, 'entries'); - if ($id === null) { - $this->CacheTree->reset(); - } else { - $this->CacheTree->delete($id); + public function __call($method, $params) { + $proxy = [$this->_CacheSupport, $method]; + if (is_callable($proxy)) { + return call_user_func_array($proxy, $params); } } - - /** - * Clears out the APC if available - */ - public function clearApc() { - $this->_CacheSupport->clear('Apc'); - } } diff --git a/app/Controller/Component/CacheTreeComponent.php b/app/Controller/Component/CacheTreeComponent.php deleted file mode 100755 index 7c5797f50..000000000 --- a/app/Controller/Component/CacheTreeComponent.php +++ /dev/null @@ -1,37 +0,0 @@ -_CacheTree = CacheTree::getInstance(); - $this->_CacheTree->initialize($Controller); - } - - public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { - $this->saveCache(); - } - - public function beforeRender(Controller $Controller) { - $Controller->set('CacheTree', $this->_CacheTree); - } - - public function shutdown(Controller $Controller) { - $this->saveCache(); - } - - public function __call($method, $params) { - $proxy = [$this->_CacheTree, $method]; - if (is_callable($proxy)) { - return call_user_func_array($proxy, $params); - } - } - } \ No newline at end of file diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 44023419f..b54e755e8 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -12,7 +12,6 @@ class EntriesController extends AppController { 'Text', ); public $components = [ - 'CacheTree', 'CacheSupport', 'Flattr', 'Search.Prg', @@ -52,7 +51,7 @@ public function index() { $cachedThreads = []; $uncachedThreads = []; foreach($initialThreads as $thread) { - if ($this->CacheTree->isCacheValid($thread)) { + if ($this->CacheSupport->CacheTree->isCacheValid($thread)) { $cachedThreads[$thread['id']] = $thread; } else { $uncachedThreads[$thread['id']] = $thread; @@ -483,7 +482,7 @@ public function delete($id = null) { // Redirect if ($success) { - $this->CacheSupport->clearTree($entry['Entry']['tid']); + $this->CacheSupport->clear('Thread', $entry['Entry']['tid']); if ($this->Entry->isRoot($entry)) { $this->Session->setFlash(__('delete_tree_success'), 'flash/notice'); $this->redirect('/'); @@ -707,7 +706,7 @@ public function merge($id = null) { // success $this->Entry->contain(); $targetEntry = $this->Entry->findById($targetId); - $this->CacheSupport->clearTree($targetEntry['Entry']['id']); + $this->CacheSupport->clear('Thread', $targetEntry['Entry']['id']); return $this->redirect('/entries/view/' . $id); } else { $this->Session->setFlash(__("Error"), 'flash/error'); @@ -743,7 +742,7 @@ public function ajax_toggle($id = null, $toggle = null) { $this->Entry->id = $id; $this->request->data = $this->Entry->toggle($toggle); $tid = $this->Entry->field('tid'); - $this->CacheSupport->clearTree($tid); + $this->CacheSupport->clear('Thread', $tid); return ($this->request->data == 0) ? __d('nondynamic', $toggle . '_set_entry_link') : __d('nondynamic', $toggle . '_unset_entry_link'); } @@ -840,7 +839,7 @@ protected function _prepareSlidetabData() { } protected function _afterNewEntry($newEntry) { - $this->CacheSupport->clearTree($newEntry['Entry']['tid']); + $this->CacheSupport->clear('Thread', $newEntry['Entry']['tid']); // set notifications if (isset($newEntry['Event'])) { $notis = [ diff --git a/app/Controller/ToolsController.php b/app/Controller/ToolsController.php index 1f1952d70..8a8e1eaa3 100644 --- a/app/Controller/ToolsController.php +++ b/app/Controller/ToolsController.php @@ -21,7 +21,7 @@ class ToolsController extends AppController { * Emtpy out all caches */ public function admin_emptyCaches() { - $this->CacheSupport->clearAll(); + $this->CacheSupport->clear(); $this->Session->setFlash(__('Caches have been emptied.'), 'flash/notice'); return $this->redirect($this->referer()); } @@ -42,7 +42,7 @@ public function testJs() { */ public function clearCache() { if (in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) { - $this->CacheSupport->clearApc(); + $this->CacheSupport->clear('Apc'); echo json_encode(array('APC Clear Cache' => true)); } exit; diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 56ed8ce69..9c083d525 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -359,7 +359,7 @@ public function admin_delete($id = null) { elseif ($id == 1): $this->Session->setFlash(__("You can't delete the installation account."), 'flash/error'); elseif ($this->User->deleteAllExceptEntries($id)): - $this->CacheSupport->clearTree(); + $this->CacheSupport->clear('Thread'); $this->Session->setFlash(__('User %s deleted.', $readUser['User']['username']), 'flash/notice'); $this->redirect('/'); return; diff --git a/app/Lib/CacheSupport.php b/app/Lib/CacheSupport.php index c6c1d4a3e..33d903bec 100644 --- a/app/Lib/CacheSupport.php +++ b/app/Lib/CacheSupport.php @@ -7,9 +7,10 @@ class CacheSupport extends Object { public function __construct() { $this->addCache( [ - 'Apc' => 'ApcCacheSupportCachelet', - 'Cake' => 'CakeCacheSupportCachelet', - 'Saito' => 'SaitoCacheSupportCachelet' + 'Apc' => 'ApcCacheSupportCachelet', + 'Cake' => 'CakeCacheSupportCachelet', + 'Saito' => 'SaitoCacheSupportCachelet', + 'Thread' => 'ThreadCacheSupportCachelet' ] ); } @@ -37,11 +38,28 @@ protected function _addCachelet($key, CacheSupportCacheletInterface $cachelet) { } } - interface CacheSupportCacheletInterface { public function clear($id = null); } + App::uses('CacheTree', 'Lib/CacheTree'); + class ThreadCacheSupportCachelet implements CacheSupportCacheletInterface { + protected $_CacheTree; + + public function __construct() { + $this->_CacheTree = CacheTree::getInstance(); + } + + public function clear($id = null) { + Cache::clear(false, 'entries'); + if ($id === null) { + $this->_CacheTree->reset(); + } else { + $this->_CacheTree->delete($id); + } + } + } + class SaitoCacheSupportCachelet implements CacheSupportCacheletInterface { public function clear($id = null) { Cache::clear(false, 'default'); diff --git a/app/Lib/CacheTree/CacheTree.php b/app/Lib/CacheTree/CacheTree.php index 881fe9cbf..fb6c0296f 100644 --- a/app/Lib/CacheTree/CacheTree.php +++ b/app/Lib/CacheTree/CacheTree.php @@ -173,7 +173,7 @@ public function saveCache() { $this->_gc(); $this->_CacheEngine->write((array)$this->_cachedEntries); - } + } /** * Garbage collection diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 1de9457de..b5c167869 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -496,25 +496,25 @@ public function testEditShowForm() { public function testEmptyCache() { - $Entries = $this->generate('Entries', array( - 'components' => array( - 'CacheTree' => array('delete'), - ) - )); + $Entries = $this->generate( + 'Entries', + ['components' => ['CacheSupport' => ['clear']]] + ); - $this->_loginUser(1); + $this->_loginUser(1); $data['Entry'] = array( 'pid' => 7, 'subject' => 'test' ); + /* * test entries/add */ - $Entries->CacheTree->expects($this->once()) - ->method('delete') - ->with($this->equalTo('1')); + $Entries->CacheSupport->expects($this->once()) + ->method('clear') + ->with($this->equalTo('Thread')); $Entries->Entry->contain(); $this->testAction( @@ -528,7 +528,7 @@ public function testEmptyCache() { $Entries = $this->generate( 'Entries', [ - 'components' => ['CacheTree' => ['delete']], + 'components' => ['CacheSupport' => ['clear']], 'models' => ['Entry' => ['update']] ] ); @@ -538,9 +538,9 @@ public function testEmptyCache() { ->will($this->returnValue(true)); - $Entries->CacheTree->expects($this->once()) - ->method('delete') - ->with($this->equalTo('1')); + $Entries->CacheSupport->expects($this->once()) + ->method('clear') + ->with($this->equalTo('Thread')); $this->testAction( '/entries/edit/7', diff --git a/app/Test/Case/Controller/ToolsControllerTest.php b/app/Test/Case/Controller/ToolsControllerTest.php index 8791b6e31..b33b32361 100644 --- a/app/Test/Case/Controller/ToolsControllerTest.php +++ b/app/Test/Case/Controller/ToolsControllerTest.php @@ -45,29 +45,14 @@ public function testAdminEmptyCachesUser() { } public function testAdminEmptyCaches() { - $Tools = $this->generate('Tools', - array( - 'components' => array( - 'CacheSupport' => array( - 'clearAll' - ), - ) - ) + $Tools = $this->generate( + 'Tools', + ['components' => ['CacheSupport' => ['clear']]] ); $this->_loginUser(1); $Tools->CacheSupport->expects($this->once()) - ->method('clearAll'); + ->method('clear'); $this->testAction('admin/tools/emptyCaches'); } - - /** - * testClearCache method - * - * @return void - */ - public function testClearCache() { - - } - } From d37326f96903d077ec833b96d9a57d8e23372e4e Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 16:05:06 +0200 Subject: [PATCH 35/72] CacheTree should always use int for array values --- app/Lib/CacheTree/CacheTree.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/Lib/CacheTree/CacheTree.php b/app/Lib/CacheTree/CacheTree.php index fb6c0296f..3df8f4fc0 100644 --- a/app/Lib/CacheTree/CacheTree.php +++ b/app/Lib/CacheTree/CacheTree.php @@ -83,7 +83,8 @@ public function isCacheValid(array $entry) { return $this->_validEntries[$entry['id']]; endif; - if ( isset($this->_cachedEntries[$entry['id']]) && strtotime($entry['last_answer']) <= $this->_cachedEntries[$entry['id']]['metadata']['content_last_updated']) { + if (isset($this->_cachedEntries[(int)$entry['id']]) && + strtotime($entry['last_answer']) <= $this->_cachedEntries[(int)$entry['id']]['metadata']['content_last_updated']) { if ($this->_isEntryOldForUser($entry)) { $isCacheValid = true; } @@ -103,7 +104,7 @@ protected function _isEntryOldForUser(array $entry) { public function delete($id) { $this->_isUpdated = TRUE; $this->readCache(); - unset($this->_cachedEntries[$id]); + unset($this->_cachedEntries[(int)$id]); } public function reset() { @@ -118,8 +119,8 @@ public function read($id = null) { return $this->_cachedEntries; } - if ( isset($this->_cachedEntries[$id]) ) { - return $this->_cachedEntries[$id]['content']; + if ( isset($this->_cachedEntries[(int)$id]) ) { + return $this->_cachedEntries[(int)$id]['content']; } return FALSE; @@ -145,7 +146,7 @@ public function update($id, $content, $timestamp = null) { 'content_last_updated' => $timestamp, ); $data = array( 'metadata' => $metadata, 'content' => $content ); - $this->_cachedEntries[$id] = $data; + $this->_cachedEntries[(int)$id] = $data; } public function readCache() { From 95d8bc833fbc1882099e7f04dd3e2cda62e8924e Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 17:16:25 +0200 Subject: [PATCH 36/72] refactors cache invalidation from calls in controllers to listeners in models --- app/Controller/AppController.php | 1 + app/Controller/EntriesController.php | 12 +-- app/Controller/ToolsController.php | 4 - app/Controller/UsersController.php | 6 +- app/Lib/CacheSupport.php | 33 +++++++- app/Model/Entry.php | 77 ++++++++++++++----- .../Case/Controller/EntriesControllerTest.php | 57 -------------- 7 files changed, 92 insertions(+), 98 deletions(-) diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 505a8b0ad..266f78dce 100644 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -23,6 +23,7 @@ class AppController extends Controller { */ 'Cookie', 'CurrentUser', + 'CacheSupport', 'JsData', 'SaitoEmail', 'EmailNotification', diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index b54e755e8..ef1d259e2 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -12,7 +12,6 @@ class EntriesController extends AppController { 'Text', ); public $components = [ - 'CacheSupport', 'Flattr', 'Search.Prg', 'Shouts' @@ -482,7 +481,6 @@ public function delete($id = null) { // Redirect if ($success) { - $this->CacheSupport->clear('Thread', $entry['Entry']['tid']); if ($this->Entry->isRoot($entry)) { $this->Session->setFlash(__('delete_tree_success'), 'flash/notice'); $this->redirect('/'); @@ -703,11 +701,8 @@ public function merge($id = null) { $targetId = $this->request->data['Entry']['targetId']; $this->Entry->id = $id; if ($this->Entry->threadMerge($targetId)) { - // success - $this->Entry->contain(); - $targetEntry = $this->Entry->findById($targetId); - $this->CacheSupport->clear('Thread', $targetEntry['Entry']['id']); - return $this->redirect('/entries/view/' . $id); + $this->redirect('/entries/view/' . $id); + return; } else { $this->Session->setFlash(__("Error"), 'flash/error'); } @@ -741,8 +736,6 @@ public function ajax_toggle($id = null, $toggle = null) { else { $this->Entry->id = $id; $this->request->data = $this->Entry->toggle($toggle); - $tid = $this->Entry->field('tid'); - $this->CacheSupport->clear('Thread', $tid); return ($this->request->data == 0) ? __d('nondynamic', $toggle . '_set_entry_link') : __d('nondynamic', $toggle . '_unset_entry_link'); } @@ -839,7 +832,6 @@ protected function _prepareSlidetabData() { } protected function _afterNewEntry($newEntry) { - $this->CacheSupport->clear('Thread', $newEntry['Entry']['tid']); // set notifications if (isset($newEntry['Event'])) { $notis = [ diff --git a/app/Controller/ToolsController.php b/app/Controller/ToolsController.php index 8a8e1eaa3..16b60d25c 100644 --- a/app/Controller/ToolsController.php +++ b/app/Controller/ToolsController.php @@ -9,10 +9,6 @@ */ class ToolsController extends AppController { - public $components = [ - 'CacheSupport' - ]; - public $helpers = array( 'JasmineJs.JasmineJs' ); diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 9c083d525..82813095a 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -5,9 +5,6 @@ class UsersController extends AppController { public $name = 'Users'; - public $components = [ - 'CacheSupport' - ]; public $helpers = [ 'Farbtastic', @@ -359,8 +356,7 @@ public function admin_delete($id = null) { elseif ($id == 1): $this->Session->setFlash(__("You can't delete the installation account."), 'flash/error'); elseif ($this->User->deleteAllExceptEntries($id)): - $this->CacheSupport->clear('Thread'); - $this->Session->setFlash(__('User %s deleted.', $readUser['User']['username']), 'flash/notice'); + $this->Session->setFlash(__('User %s deleted.', $readUser['User']['username']), 'flash/success'); $this->redirect('/'); return; else: diff --git a/app/Lib/CacheSupport.php b/app/Lib/CacheSupport.php index 33d903bec..63f0d77b5 100644 --- a/app/Lib/CacheSupport.php +++ b/app/Lib/CacheSupport.php @@ -42,12 +42,43 @@ interface CacheSupportCacheletInterface { public function clear($id = null); } + App::uses('CakeEvent', 'Event'); + App::uses('CakeEventListener', 'Event'); App::uses('CacheTree', 'Lib/CacheTree'); - class ThreadCacheSupportCachelet implements CacheSupportCacheletInterface { + class ThreadCacheSupportCachelet implements CacheSupportCacheletInterface, + CakeEventListener { + protected $_CacheTree; public function __construct() { $this->_CacheTree = CacheTree::getInstance(); + CakeEventManager::instance()->attach($this); + } + + public function implementedEvents() { + return [ + 'Model.Thread.reset' => 'onThreadReset', + 'Model.Thread.change' => 'onThreadChanged', + 'Model.Entry.replyToEntry' => 'onEntryChanged', + 'Model.Entry.update' => 'onEntryChanged' + ]; + } + + public function onThreadReset($event) { + $this->clear(); + } + + public function onThreadChanged($event) { + $this->clear($event->data['subject']); + } + + public function onEntryChanged($event) { + $model_alias = $event->subject()->alias; + if (!isset($event->data['data'][$model_alias]['tid'])) { + throw new InvalidArgumentException('No thread-id in event data.'); + } + $thread_id = $event->data['data'][$model_alias]['tid']; + $this->clear($thread_id); } public function clear($id = null) { diff --git a/app/Model/Entry.php b/app/Model/Entry.php index b255b9606..876d64676 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -385,25 +385,19 @@ public function createPosting($data, $CurrentUser = null) { return false; } - $this->getEventManager()->dispatch( - new CakeEvent( - 'Model.Entry.replyToEntry', - $this, - array( - 'subject' => $new_posting[$this->alias]['pid'], - 'data' => $new_posting, - ) - ) + $this->_dispatchEvent( + 'Model.Entry.replyToEntry', + [ + 'subject' => $new_posting[$this->alias]['pid'], + 'data' => $new_posting + ] ); - $this->getEventManager()->dispatch( - new CakeEvent( - 'Model.Entry.replyToThread', - $this, - array( - 'subject' => $new_posting[$this->alias]['tid'], - 'data' => $new_posting, - ) - ) + $this->_dispatchEvent( + 'Model.Entry.replyToThread', + [ + 'subject' => $new_posting[$this->alias]['tid'], + 'data' => $new_posting + ] ); } $this->id = $new_posting_id; @@ -443,6 +437,18 @@ public function update($data, $CurrentUser = null) { ] ); + $entry = $this->save($data); + + if ($entry) { + $this->_dispatchEvent( + 'Model.Entry.update', + [ + 'subject' => $entry[$this->alias]['id'], + 'data' => $entry + ] + ); + } + return $this->save($data); } @@ -575,6 +581,16 @@ public function toggle($key) { if ($key === 'locked') { $this->_threadLock($result); } + + $entry = $this->read(); + $this->_dispatchEvent( + 'Model.Entry.update', + [ + 'subject' => $entry[$this->alias]['id'], + 'data' => $entry + ] + ); + return $result; } @@ -621,7 +637,7 @@ public function deleteNode($id = null) { $ids_to_delete = $this->getIdsForNode($id); $success = $this->deleteAll( - ['Entry.id' => $ids_to_delete], + [$this->alias . '.id' => $ids_to_delete], true, true ); @@ -635,6 +651,11 @@ public function deleteNode($id = null) { foreach ($ids_to_delete as $entry_id) { $this->Esevent->deleteSubject($entry_id, 'entry'); } + + $this->_dispatchEvent( + 'Model.Thread.change', + ['subject' => $entry[$this->alias]['tid']] + ); endif; return $success; @@ -666,7 +687,7 @@ public function getIdsForNode($id) { public function anonymizeEntriesFromUser($user_id) { // remove username from all entries and reassign to anonyme user - return $this->updateAll( + $success = $this->updateAll( [ 'Entry.name' => "NULL", 'Entry.edited_by' => "NULL", @@ -675,6 +696,12 @@ public function anonymizeEntriesFromUser($user_id) { ], ['Entry.user_id' => $user_id] ); + + if ($success) { + $this->_dispatchEvent('Model.Thread.reset'); + } + + return $success; } /** @@ -756,14 +783,22 @@ public function threadMerge($targetId) { $this->Esevent->transferSubjectForEventType( $threadIdSource, - $targetEntry['Entry']['tid'], + $targetEntry[$this->alias]['tid'], 'thread' ); + $this->_dispatchEvent( + 'Model.Thread.change', + ['subject' => $targetEntry[$this->alias]['tid']] + ); return true; } return false; } + protected function _dispatchEvent($event, $data = []) { + $this->getEventManager()->dispatch(new CakeEvent($event, $this, $data)); + } + protected function _addAdditionalFields(&$entries) { /** * Function for checking if entry is bookmarked by current user diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index b5c167869..f3a38e69b 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -494,63 +494,6 @@ public function testEditShowForm() { $this->assertPattern('/data\[Event\]\[2\]\[event_type_id\]"\s+?checked="checked"/', $result); } - public function testEmptyCache() { - - $Entries = $this->generate( - 'Entries', - ['components' => ['CacheSupport' => ['clear']]] - ); - - $this->_loginUser(1); - - $data['Entry'] = array( - 'pid' => 7, - 'subject' => 'test' - ); - - - /* - * test entries/add - */ - $Entries->CacheSupport->expects($this->once()) - ->method('clear') - ->with($this->equalTo('Thread')); - - $Entries->Entry->contain(); - $this->testAction( - '/entries/add/7', - ['data' => $data, 'method' => 'POST'] - ); - - /* - * Test entries/edit - */ - $Entries = $this->generate( - 'Entries', - [ - 'components' => ['CacheSupport' => ['clear']], - 'models' => ['Entry' => ['update']] - ] - ); - - $Entries->Entry->expects($this->once()) - ->method('update') - ->will($this->returnValue(true)); - - - $Entries->CacheSupport->expects($this->once()) - ->method('clear') - ->with($this->equalTo('Thread')); - - $this->testAction( - '/entries/edit/7', - [ - 'data' => $data, - 'method' => 'post' - ] - ); - } - public function testPreviewLoggedIn() { $this->setExpectedException('ForbiddenException'); $this->testAction('/entries/preview'); From 018f69bb08b8ceecc8a1f28d81a2dd948238918a Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Thu, 25 Jul 2013 19:49:58 +0200 Subject: [PATCH 37/72] Entry::update always passes full Entry data to event trigger --- app/Model/Entry.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 876d64676..6d53e9fbb 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -292,7 +292,7 @@ public function getThreadId($id) { } /** - * Shorthand for reading an entry + * Shorthand for reading an entry with full data */ public function get($id, $unsanitized = false) { if (isset($entry[$this->alias]['id'])) { @@ -437,19 +437,21 @@ public function update($data, $CurrentUser = null) { ] ); - $entry = $this->save($data); + $result = $this->save($data); - if ($entry) { + if ($result) { + $this->contain(); + $result = $this->read() + $data; $this->_dispatchEvent( 'Model.Entry.update', [ - 'subject' => $entry[$this->alias]['id'], - 'data' => $entry + 'subject' => $result[$this->alias]['id'], + 'data' => $result ] ); } - return $this->save($data); + return $result; } /* @mb `views` into extra related table if performance becomes a problem */ @@ -582,7 +584,9 @@ public function toggle($key) { $this->_threadLock($result); } + $this->contain(); $entry = $this->read(); + $this->_dispatchEvent( 'Model.Entry.update', [ From 3c996b31187966b8200fb94a12ba20e880529a9f Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 12:05:13 +0200 Subject: [PATCH 38/72] refactors category chooser --- app/Controller/EntriesController.php | 97 +++++++++---------- app/Lib/UserCategories.php | 71 ++++++++++++++ .../Case/Controller/EntriesControllerTest.php | 10 +- app/View/Entries/index.ctp | 2 +- 4 files changed, 123 insertions(+), 57 deletions(-) create mode 100644 app/Lib/UserCategories.php diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index ef1d259e2..a171900a2 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -864,57 +864,8 @@ protected function _afterNewEntry($newEntry) { */ protected function _getInitialThreads(CurrentUserComponent $User, $order) { Stopwatch::start('Entries->_getInitialThreads() Paginate'); - // default for logged-in and logged-out users - $cats = $this->Entry->Category->getCategoriesForAccession($User->getMaxAccession()); - - // get data for category chooser - $categoryChooser = $this->Entry->Category->getCategoriesSelectForAccession( - $User->getMaxAccession()); - $this->set('categoryChooser', $categoryChooser); - - $catCT = __('All Categories'); - $catC_isUsed = false; - - // category chooser - if ($User->isLoggedIn()) { - if (Configure::read('Saito.Settings.category_chooser_global') - || (Configure::read('Saito.Settings.category_chooser_user_override') && $User['user_category_override']) - ) { - $catC_isUsed = true; - /* merge the user-cats with all-cats to include categories which are - * new since the user updated his custum-cats the last time - * array (4 => '4', 7 => '7', 13 => '13') + array (4 => true, 7 => '0') - * becomes - * array (4 => true, 7 => '0', 13 => '13') - * with 13 => '13' trueish */ - $user_cats = $User['user_category_custom'] + $cats; - /* then filter for zeros to get only the user categories - * array (4 => true, 13 => '13') */ - $user_cats = array_filter($user_cats); - $user_cats = array_intersect_key($user_cats, $cats); - $this->set('categoryChooserChecked', $user_cats); - - if ($User->isLoggedIn() === false) { - // non logged in user sees his accessions i.e. the default set - } elseif ((int)$User['user_category_active'] === -1) { - // user has choosen to see all available categories i.e. the default set - } elseif ((int)$User['user_category_active'] > 0) { - // logged in users sees his active group if he has access rights - $cats = array_intersect_key($cats, - array($User['user_category_active'] => 1)); - $catCT = $User['user_category_active']; - } elseif (empty($User['user_category_custom'])) { - // for whatever reason we should see a custom category, but there are no set yet - } elseif (empty($User['user_category_custom']) === false) { - // but if he has no active group and a custom groups set he sees his custom group - $cats = array_keys($user_cats); - $catCT = __('Custom'); - } - $this->set('categoryChooserTitleId', $catCT); - } - } - $this->set('categoryChooserIsUsed', $catC_isUsed); + $categories = $this->_setupCategoryChooser($User); $this->paginate = array( /* Whenever you change the conditions here check if you have to adjust @@ -923,7 +874,7 @@ protected function _getInitialThreads(CurrentUserComponent $User, $order) { */ 'conditions' => array( 'pid' => 0, - 'Entry.category' => $cats, + 'Entry.category' => $categories, ), 'contain' => false, 'fields' => 'id, pid, tid, time, last_answer', @@ -943,6 +894,50 @@ protected function _getInitialThreads(CurrentUserComponent $User, $order) { return $initial_threads_new; } + protected function _setupCategoryChooser(SaitoUser $User) { + $categories = $this->Entry->Category->getCategoriesForAccession( + $User->getMaxAccession() + ); + + $is_used = $User->isLoggedIn() && + ( + Configure::read('Saito.Settings.category_chooser_global') || + ( + Configure::read( + 'Saito.Settings.category_chooser_user_override' + ) && $User['user_category_override'] + ) + ); + + if ($is_used) { + // @todo find right place for this; also: User::getCategories(); + App::uses('UserCategories', 'Lib'); + $UserCategories = new UserCategories($User->getSettings(), $categories); + list($categories, $type, $custom) = $UserCategories->get(); + + $this->set('categoryChooserChecked', $custom); + + switch ($type) { + case 'single': + $title = $User['user_category_active']; + break; + case 'custom': + $title = __('Custom'); + break; + default: + $title = __('All Categories'); + } + $this->set('categoryChooserTitleId', $title); + $this->set( + 'categoryChooser', + $this->Entry->Category->getCategoriesSelectForAccession( + $User->getMaxAccession() + ) + ); + } + return $categories; + } + protected function _teardownAdd() { //* find categories for dropdown $categories = $this->Entry->Category->getCategoriesSelectForAccession($this->CurrentUser->getMaxAccession()); diff --git a/app/Lib/UserCategories.php b/app/Lib/UserCategories.php new file mode 100644 index 000000000..2e964d1b1 --- /dev/null +++ b/app/Lib/UserCategories.php @@ -0,0 +1,71 @@ +_user = $user; + $this->_categories = $categories; + } + + protected function _isAll() { + return (int)$this->_user['user_category_active'] === -1; + } + + protected function _isSingle() { + return (int)$this->_user['user_category_active'] > 0; + } + + protected function _isCustom() { + return (int)$this->_user['user_category_active'] == 0 && + empty($this->_user['user_category_custom']) === false; + } + + protected function _getCustom() { + // merge all-cats onto user-cats to include categories which are + // new since the user updated his user-cats the last time + // + // [4 => '4', 7 => '7', 13 => '13'] + [4 => true, 7 => '0'] + // becomes + // [4 => true, 7 => '0', 13 => '13'] + // with 13 => '13' trueish + $custom = $this->_user['user_category_custom'] + $this->_categories; + // then filter for zeros to get only the user categories + // [4 => true, 13 => '13'] + $custom = array_filter($custom); + $custom = array_intersect_key($custom, $this->_categories); + return $custom; + } + + /** + * @return array + * + * $categories: array with categories for the active type [cat_id1, cat_id2, …] + * $type: active type: 'all', 'single' or 'custom' + * $custom: categories for 'custom' [cat_id1, cat_id2, …] + */ + public function get() { + $custom = $this->_getCustom(); + if ($this->_isSingle()) { + $type = 'single'; + $categories = array_intersect_key( + $this->_categories, + [$this->_user['user_category_active'] => 1] + ); + } elseif ($this->_isCustom()) { + $type = 'custom'; + $categories = array_keys($custom); + } else { + $type = 'all'; + $categories = $this->_categories; + } + return [$categories, $type, $custom]; + } + } + diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index f3a38e69b..0261883c0 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -110,7 +110,7 @@ public function testCategoryChooserNotLoggedIn() { $User = new CurrentUserComponent(new ComponentCollection()); $User->set(array()); $Entries->getInitialThreads($User); - $this->assertFalse($Entries->viewVars['categoryChooserIsUsed']); + $this->assertFalse(isset($Entries->viewVars['categoryChooser'])); } /** @@ -154,7 +154,7 @@ public function testCategoryChooserDeactivated() { 'user_category_override' => 1, )); $Entries->getInitialThreads($User); - $this->assertFalse($Entries->viewVars['categoryChooserIsUsed']); + $this->assertFalse(isset($Entries->viewVars['categoryChooser'])); } public function testCategoryChooserEmptyCustomSet() { @@ -196,7 +196,7 @@ public function testCategoryChooserEmptyCustomSet() { 'user_category_override' => 1, )); $Entries->getInitialThreads($User); - $this->assertTrue($Entries->viewVars['categoryChooserIsUsed']); + $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserTitleId'], 'All Categories'); } @@ -248,7 +248,7 @@ public function testCategoryChooserCustomSet() { 'user_category_custom' => array(1 => 1, 2 => 1, 7 => 0), )); $Entries->getInitialThreads($User); - $this->assertTrue($Entries->viewVars['categoryChooserIsUsed']); + $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserChecked'], array( '2' => 1, '8' => '8', @@ -298,7 +298,7 @@ public function testCategoryChooserSingleCategory() { 'user_category_custom' => array(1 => 1, 2 => 1, 7 => 0), )); $Entries->getInitialThreads($User); - $this->assertTrue($Entries->viewVars['categoryChooserIsUsed']); + $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserTitleId'], 7); $this->assertEqual($Entries->viewVars['categoryChooserChecked'], array( '1' => 1, diff --git a/app/View/Entries/index.ctp b/app/View/Entries/index.ctp index fdc59f0f0..53bee6f7f 100644 --- a/app/View/Entries/index.ctp +++ b/app/View/Entries/index.ctp @@ -26,7 +26,7 @@ $this->end(); $this->start('headerSubnavRightTop'); - if (isset($categoryChooserIsUsed) && $categoryChooserIsUsed): + if (isset($categoryChooser)): // category-chooser link echo $this->Html->link( ' ' From 48b93d99ce50a775c907da85196b8d2b0fe99f9e Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 12:05:34 +0200 Subject: [PATCH 39/72] removed dead code --- app/Model/Category.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/Model/Category.php b/app/Model/Category.php index 6b3aad6d9..85fb3f7ce 100755 --- a/app/Model/Category.php +++ b/app/Model/Category.php @@ -54,18 +54,8 @@ public function getCategoriesSelectForAccession($accession) { return $categories; } - /* - public function afterSave($created) { - debug($this->data); - if ($created || - $this->clearCache(); - ) - } - */ - protected function _getCategoriesForAccession($accession) { if (!isset($this->_cache[$accession])) { - // $this->_cache = Cache::read('Saito.Cache.catForAccession'); if (empty($this->_cache[$accession])) { $this->_cache[$accession] = $this->find('list', array( @@ -77,17 +67,10 @@ protected function _getCategoriesForAccession($accession) { ) ); } - // Cache::write('Saito.Cache.catForAccession', $this->_cache); } return $this->_cache[$accession]; } - /* - public function clearCache() { - - } - */ - public function mergeIntoCategory($targetCategory) { if (!isset($this->id)) return false; From 8e16cdf11d461ebef10b532d30790dc591e18f01 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 12:06:03 +0200 Subject: [PATCH 40/72] renames Entries::_afterNewEntry to Entries::_setNotifications --- app/Controller/EntriesController.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index a171900a2..a1346ec83 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -243,7 +243,7 @@ public function add($id = null) { // inserting new posting was successful if ($new_posting !== false) : - $this->_afterNewEntry($new_posting); + $this->_setNotifications($new_posting); if ($this->request->is('ajax')) : // Ajax request came from front answer on front page /entries/index if ($this->localReferer('action') === 'index') { @@ -407,7 +407,7 @@ public function edit($id = null) { $data['Entry']['id'] = $id; $new_entry = $this->Entry->update($data); if ($new_entry) { - $this->_afterNewEntry(am($this->request['data'], $oldEntry)); + $this->_setNotifications(am($this->request['data'], $oldEntry)); $this->redirect(['action' => 'view', $id]); return; } else { @@ -831,8 +831,7 @@ protected function _prepareSlidetabData() { } } - protected function _afterNewEntry($newEntry) { - // set notifications + protected function _setNotifications($newEntry) { if (isset($newEntry['Event'])) { $notis = [ [ From c297f7770603d457798d913144923dea06bfaa40 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 14:56:07 +0200 Subject: [PATCH 41/72] refactors UserCategories --- app/Lib/UserCategories.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/app/Lib/UserCategories.php b/app/Lib/UserCategories.php index 2e964d1b1..6ca4e441e 100644 --- a/app/Lib/UserCategories.php +++ b/app/Lib/UserCategories.php @@ -27,20 +27,27 @@ protected function _isCustom() { empty($this->_user['user_category_custom']) === false; } + protected function _filterOutNonExisting($categories) { + return array_intersect_key($categories, $this->_categories); + } + protected function _getCustom() { - // merge all-cats onto user-cats to include categories which are - // new since the user updated his user-cats the last time + // add new categories to custom set // - // [4 => '4', 7 => '7', 13 => '13'] + [4 => true, 7 => '0'] + // [4 => true, 7 => '0'] + [4 => '4', 7 => '7', 13 => '13'] // becomes // [4 => true, 7 => '0', 13 => '13'] // with 13 => '13' trueish $custom = $this->_user['user_category_custom'] + $this->_categories; + // then filter for zeros to get only the user categories // [4 => true, 13 => '13'] $custom = array_filter($custom); - $custom = array_intersect_key($custom, $this->_categories); - return $custom; + + $custom = $this->_filterOutNonExisting($custom); + + $keys = array_keys($custom); + return array_combine($keys, $keys); } /** @@ -54,13 +61,12 @@ public function get() { $custom = $this->_getCustom(); if ($this->_isSingle()) { $type = 'single'; - $categories = array_intersect_key( - $this->_categories, - [$this->_user['user_category_active'] => 1] + $categories = $this->_filterOutNonExisting( + [$this->_user['user_category_active'] => $this->_user['user_category_active']] ); } elseif ($this->_isCustom()) { $type = 'custom'; - $categories = array_keys($custom); + $categories = $custom; } else { $type = 'all'; $categories = $this->_categories; From d84af5e6f97a2b194cebef8ee53a7df0e07f31a3 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 18:46:10 +0200 Subject: [PATCH 42/72] flag to permanently disable model sanitation --- app/Model/AppModel.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index a7769b52c..e98587d2e 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -12,6 +12,11 @@ class AppModel extends Model { # Entry->User->UserOnline public $recursive = 1; + /* + * Lock to disable sanitation permanently + */ + static $sanitizeEnabled = true; + static $sanitize = true; /** @@ -43,11 +48,13 @@ protected function _sanitizeFields($results) { public function afterFind($results, $primary = false) { parent::afterFind($results, $primary); - if (self::$sanitize) { - $results = $this->_sanitizeFields($results); - } elseif (self::$_lock_no_sanitize === $this->alias) { - // sanitizing can only be disabled for one find - $this->sanitize(true); + if (self::$sanitizeEnabled) { + if (self::$sanitize) { + $results = $this->_sanitizeFields($results); + } elseif (self::$_lock_no_sanitize === $this->alias) { + // sanitizing can only be disabled for one find + $this->sanitize(true); + } } return $results; } From bbc4ff927fb4f0e99a5aada640df876d14490c23 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 18:46:22 +0200 Subject: [PATCH 43/72] fixes test cases --- app/Test/Case/Controller/EntriesControllerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 0261883c0..59074a04e 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -250,7 +250,7 @@ public function testCategoryChooserCustomSet() { $Entries->getInitialThreads($User); $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserChecked'], array( - '2' => 1, + '2' => '2', '8' => '8', )); $this->assertEqual($Entries->viewVars['categoryChooser'], array( @@ -301,8 +301,8 @@ public function testCategoryChooserSingleCategory() { $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserTitleId'], 7); $this->assertEqual($Entries->viewVars['categoryChooserChecked'], array( - '1' => 1, - '2' => 1, + '1' => '1', + '2' => '2', )); } From acf8a365224e8045dbe78df61b8dfa1ea1c4f78c Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sat, 27 Jul 2013 18:46:38 +0200 Subject: [PATCH 44/72] updates fixtures --- app/Test/Fixture/EntryFixture.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/Test/Fixture/EntryFixture.php b/app/Test/Fixture/EntryFixture.php index 98d998bf3..937543941 100755 --- a/app/Test/Fixture/EntryFixture.php +++ b/app/Test/Fixture/EntryFixture.php @@ -45,18 +45,18 @@ class EntryFixture extends CakeTestFixture { */ public $records = array( //* thread 1 - array( - 'id' => 1, - 'subject' => 'First_Subject', - 'text' => 'First_Text', - 'pid' => 0, - 'tid' => 1, - 'time' => '2000-01-01 20:00:00', - 'last_answer' => '2000-01-04 20:02:00', - 'category' => 2, // accession = 0 - 'user_id' => 3, - 'locked' => 0 - ), + [ + 'id' => 1, + 'subject' => 'First_Subject', + 'text' => 'First_Text', + 'pid' => 0, + 'tid' => 1, + 'time' => '2000-01-01 20:00:00', + 'last_answer' => '2000-01-04 20:02:00', + 'category' => 2, // accession = 0 + 'user_id' => 3, + 'locked' => 0 + ], array( 'id' => 2, 'subject' => 'Second_Subject', @@ -184,6 +184,8 @@ class EntryFixture extends CakeTestFixture { 'tid' => 10, 'time' => '2000-01-01 10:59:00', 'last_answer' => '2000-01-01 10:59:00', + 'edited' => '0000-00-00 00:00:00', + 'edited_by' => null, 'category' => 2, // accession = 0 'user_id' => 3, 'locked' => 1 From 598b4ee18a547d3a9382c4079d11eda0d2180957 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sun, 28 Jul 2013 10:56:01 +0200 Subject: [PATCH 45/72] refactors SaitoCurrentUserLastRefresh --- .../Component/CurrentUserComponent.php | 18 ++++++++++++------ app/Controller/EntriesController.php | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/Controller/Component/CurrentUserComponent.php b/app/Controller/Component/CurrentUserComponent.php index 2bbfd41dc..6854b9508 100755 --- a/app/Controller/Component/CurrentUserComponent.php +++ b/app/Controller/Component/CurrentUserComponent.php @@ -368,12 +368,18 @@ public function __construct(SaitoUser $currentUser, User $user) { $this->user = $user; } - public function forceSet() { - $this->_set(date("Y-m-d H:i:s")); - } - - public function set() { - $this->_set($this->currentUser['last_refresh_tmp']); + /** + * @param mixed $timestamp + * + * null|'now'|<`Y-m-d H:i:s` timestamp> + */ + public function set($timestamp = null) { + if ($timestamp === 'now') { + $this->_set(date("Y-m-d H:i:s")); + } elseif ($timestamp === null) { + $timestamp = $this->currentUser['last_refresh_tmp']; + } + $this->_set($timestamp); } public function setMarker() { diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index a1346ec83..34f31f4ac 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -156,7 +156,7 @@ public function mix($tid) { */ public function update() { $this->autoRender = false; - $this->CurrentUser->LastRefresh->forceSet(); + $this->CurrentUser->LastRefresh->set('now'); $this->redirect('/entries/index'); } From 8f108dabd3049fc31d08645090289a6e5d4cceef Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sun, 28 Jul 2013 10:59:09 +0200 Subject: [PATCH 46/72] ads documentation for upcoming api --- docs/api-v1.md | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 docs/api-v1.md diff --git a/docs/api-v1.md b/docs/api-v1.md new file mode 100644 index 000000000..38d067fa9 --- /dev/null +++ b/docs/api-v1.md @@ -0,0 +1,327 @@ +# API v1 # + +## Objects ## + +### Introduction ### + +#### Basics #### + +All timestamps are ISO 8601 UTC. + +All text is raw with unencoded html entities unless mentioned otherwise. + +#### Entries and Threads #### + +Entries are grouped by `thread_id` into Threads. All entries in the same thread share the same `thread_id`. The `thread_id` is identical to the `id` of the root entry in that thread. + +Entries are related to each other via the `parent_id`. The `parent_id` of root entries is `0`. + +#### Mark as Read #### + +Mark as read handles what a (logged in) user perceives as new entries and is controlled by a timestamp variable `last_refresh`. + +Every entry with a `time < last_refresh` is considered old/read and vice versa. So to e.g. mark _everything_ read `last_refresh` should be set to _now_. + +### Entry ### + + { + "id": 423739, + "parent_id": 0, + "thread_id": 423739, + "subject": "my subject", + "is_nt": true, + "time": "2013-07-04T19:53:14+00:00", + "text": "", + "html": "", + "user_id": 1, + "user_name": "Alice", + "edit_name": "Bob", + "edit_time": "2013-07-05T19:53:14+00:00" + "category_id": 1, + "category_name": "my category" + + // *** additional data if user is authorized *** // + "is_locked": false, + } + +`is_nt` +: boolean +: True if `text` is empty. + +`is_locked` +: boolean +: Entry is locked and answering is not possible. + +`text` +: unencoded, raw bbcode text + +`html` +: encoded and parsed bbcode as html + +`edit_name` +: name of editor or `null` if unedited + +: `edit_time`": +: timestamp of edit or `null` if unedited + +### User ### + + { + "isLoggedIn": true|false, // @todo rename + + // *** additional data if user is authorized *** // + "id": 1, + "last_refresh": "2013-06-26T15:51:32+00:00", + "threads_order": "time" + } + +`threads_order` +: values: `time`, `answer` + +### Category ### + + { + "id": 1, + "order": 1, + "title": "Category Title 1", + "description": "Category Description", + "accession": 0 + } + +`order` +: Asc. integer representing sort order. + +`accession` +: `0`: all; `1`: registered users; `2`: mod/admin + +### Settings ### + + { + "edit_period": 20, + "subject_maxlength": 75 + } + +`edit_period` +: period after creating it that editing is allowed in minutes + +### Server ### + + { + "time": "2013-07-24T07:44:36+00:00" + } + +`time` +: current server time + +## Endpoints ## + + +### Basics ### + +The base url is `api/v1`. + +The API requests should be performed with `Accept: application/json` header. + +Usually the API also answers to other `Accept` headers by appending `.json` to the request url. E.g. to show the latest threads in the browser use `api/v1/threads.json`. + +A successful request will respond with a 2xx HTTP code, on errors the code is 4xx/5xx. + +Authentication is handled by sending the standard Saito cookies (session or permanent). + +### bootstrap GET ### + +Global data about application and user state. + +- url: `api/v1/bootstrap` +- request method: `GET` +- authorization: limited +- returns: + - information about the current user + - categories accessible to the current user + - global forum settings + +Response: + + { + "categories": [ + // List of Category objects + ], + "settings": { + // Settings object + }, + "server": { + // Server object + } + "user": { + // User object + } + } + + +### threads GET ### + +Index of latest threads. + +- url: `api/v1/threads` +- request method: `GET` +- authorization: limited (different categories) +- query parameter: + - `order` (optional) + - values: + - `time` (default) + - `answer` + - `limit` (optional) + - int; `10` is default; 0 < limit < 100 + - `offset` (optional) + - int; default is `0` + +Response + + [ + { + "id": 423739, + "subject": "my subject", + "is_nt": true|false + "time": "2013-07-04T19:53:14+00:00", + "last_answer": "2013-07-05T10:45:33+00:00", + "user_id": 1, + "user_name": "Alice", + "category_id": 1, + "category_name": "my category" + }, + … + ] + + +### threads/#id GET ### + +Complete thread with id `` + +- url: `api/v1/threads/` +- authorization: limited (different categories) +- request method: `GET` + +Response: List of Entry objects + + [ + // Entry object 1, + … + ] + + +### entries POST ### + +Create a new entry. + +- url: `api/v1/entries` +- request method: `POST` +- authorization: logged in + +Request data for creating a new thread/new root entry: + + { + "subject": "test", + "text": "test", + "category_id": 1 + } + +Request data for creating an answer to an existing entry: + + { + "subject": "test", + "text": "test", + "parent_id": 123 + } + +`subject` +: optional: if empty parent subject is used + +Response: Entry object of the newly created entry. + + +### entries/#id PUT ### + +Updates an existing entry. + +- url: `api/v1/entries/` +- request method: `PUT` +- authorization: logged in + +Request data: + + { + "subject": "test", + "text": "test", + "category_id": 2 + } + +`subject` +: optional for non-root entries + +`text` +: optional + +`category_id` +: only for root entries + +Response: Entry object of the updated entry. + + +### login POST ### + +Marks user as logged in. Also sends authentication cookies to client. + +- url: `api/v1/login` +- request method: `POST` +- authorization: none + +Request data: + + { + "username": "your username", + "password": "your password", + "remember_me": true + } + +`remember_me` +: boolean +: optional +: stay logged in/set cookie + +Response: User object. + + +### logout POST ### + +Marks the user as logout out. Also removes authentication cookies from client. + +- url: `api/v1/logout` +- request method: `POST` +- authorization: logged in + +Request data: + + { + id: 1 // user id + } + +Response: User object with `"isLoggedIn": false` on success. + + +### markasread POST ### + +Send mark as read event. + +- url: `api/v1/marksasread` +- request method: `POST` +- authorization: logged in + +Request data: + + { + "user_id": 1 + "last_refresh": "2013-07-04T19:53:14+00:00" + } + +`last_refresh` +: optional: if empty current server time is used From 1a20ff51707f04266a8940a002afe8181649f107 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Sun, 28 Jul 2013 11:29:54 +0200 Subject: [PATCH 47/72] updated api doc --- docs/api-v1.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/api-v1.md b/docs/api-v1.md index 38d067fa9..3b27bb242 100644 --- a/docs/api-v1.md +++ b/docs/api-v1.md @@ -302,9 +302,12 @@ Marks the user as logout out. Also removes authentication cookies from client. Request data: { - id: 1 // user id + "id: 1 } +`id` +: user id + Response: User object with `"isLoggedIn": false` on success. @@ -319,9 +322,19 @@ Send mark as read event. Request data: { - "user_id": 1 + "id": 1 "last_refresh": "2013-07-04T19:53:14+00:00" } +`id` +: user id + `last_refresh` : optional: if empty current server time is used + +Response: + + { + "id": 1 + "last_refresh": "2013-07-04T19:53:14+00:00" + } From 4f81028944064b52bb3c68270062d06a065349d5 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 09:48:46 +0200 Subject: [PATCH 48/72] code formatting --- app/Model/Entry.php | 4 +++- app/Test/Case/Model/EntryTest.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 6d53e9fbb..4f4b11b9f 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -361,9 +361,11 @@ public function createPosting($data, $CurrentUser = null) { if ($new_posting === false) { return false; } + $new_posting_id = $this->id; - $this->contain(); + // make sure we pass the complete ['Entry'] dataset to events + $this->contain(); $new_posting = $this->read(); if ($this->isRoot($data)) { diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index f1dd4e69e..896149a28 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -20,6 +20,8 @@ class EntryTest extends CakeTestCase { 'app.esnotification', ); + + public function testBeforeValidate() { //* save entry with text From 17f55683663fdc39166a19511c9821aef66583d9 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 09:54:22 +0200 Subject: [PATCH 49/72] fixes Entry::createPosting sanitizes `text` and `subject` --- app/Model/Entry.php | 2 +- app/Test/Case/Model/EntryTest.php | 45 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 4f4b11b9f..d5e8c172f 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -370,7 +370,7 @@ public function createPosting($data, $CurrentUser = null) { if ($this->isRoot($data)) { // thread-id of new thread is its own id - if ($this->save(['tid' => $new_posting_id]) === false) { + if ($this->save(['tid' => $new_posting_id], false, ['tid']) === false) { // @td raise error and/or roll back new entry return false; } else { diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index 896149a28..6cab8563b 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -7,6 +7,10 @@ class EntryMock extends Entry { public $_CurrentUser; public $_editPeriod; + + public function prepareBbcode($string) { + return $string; + } } class EntryTest extends CakeTestCase { @@ -33,6 +37,47 @@ public function testBeforeValidate() { ); } + public function testCreateSuccess() { + App::uses('Category', 'Model'); + + $SaitoUser = $this->getMock( + 'SaitoUser', + ['getMaxAccession', 'getId', 'getBookmarks'], + [new ComponentCollection] + ); + $SaitoUser->expects($this->any()) + ->method('getMaxAccession') + ->will($this->returnValue(2)); + $SaitoUser->expects($this->any()) + ->method('getId') + ->will($this->returnValue(1)); + $this->Entry->_CurrentUser = $SaitoUser; + + Configure::write('Saito.Settings.subject_maxlength', 75); + + $data[$this->Entry->alias] = [ + 'pid' => 0, + 'subject' => 'Sübject', + 'text' => 'Täxt', + 'category' => 1 + ]; + + $this->Entry->createPosting($data); + $result = $this->Entry->get($this->Entry->id, true); + + $expected = $data; + $result = array_intersect_key($result, $expected); + $result[$this->Entry->alias] = array_intersect_key( + $result[$this->Entry->alias], + $expected[$this->Entry->alias] + ); + + $this->assertEqual( + $result, + $expected + ); + } + public function testCreateCategoryThreadCounterUpdate() { App::uses('Category', 'Model'); From ffa1ddb6520e4959cdd90310127ef312a4f9a069 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 10:02:32 +0200 Subject: [PATCH 50/72] fixes sometimes failling test because of time() --- app/Test/Case/Model/UserOnlineTest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/Test/Case/Model/UserOnlineTest.php b/app/Test/Case/Model/UserOnlineTest.php index 3238945ed..003f1822c 100644 --- a/app/Test/Case/Model/UserOnlineTest.php +++ b/app/Test/Case/Model/UserOnlineTest.php @@ -96,13 +96,21 @@ public function testSetOffline() { //* insert new user $user_id = 5; - $this->_startUsersOnline[0]['UserOnline'] = array('user_id' => '5', 'time' => time(), 'logged_in' => 1 ); + $this->_startUsersOnline[0]['UserOnline'] = [ + 'user_id' => '5', + 'logged_in' => 1 + ]; $this->UserOnline->setOnline($user_id, TRUE); //* test if user is inserted $this->UserOnline->contain(); $result = $this->UserOnline->find('all', $this->_fields); $expected = $this->_startUsersOnline; + + $time = $result[0]['UserOnline']['time']; + $this->assertGreaterThan(time() - 5, $time); + unset($result[0]['UserOnline']['time'], $time); + $this->assertEqual($result, $expected); //* try to delte new user From 21042f88cc1185e3cf213df925530e0c1f95adfd Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 10:11:33 +0200 Subject: [PATCH 51/72] removed dead code --- app/Model/Entry.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index d5e8c172f..8ec86a31e 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -295,9 +295,6 @@ public function getThreadId($id) { * Shorthand for reading an entry with full data */ public function get($id, $unsanitized = false) { - if (isset($entry[$this->alias]['id'])) { - $id = $entry[$this->alias]['id']; - } return $this->find( ($unsanitized) ? 'unsanitized' : 'entry', ['conditions' => [$this->alias.'.id' => $id]] From d64c2f08cff3e925e6f98043754cead2d44cb886 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 10:15:47 +0200 Subject: [PATCH 52/72] sets sub- and thread removal flash from `notice` to `success` --- app/Controller/EntriesController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 34f31f4ac..7cb8ac193 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -482,10 +482,10 @@ public function delete($id = null) { // Redirect if ($success) { if ($this->Entry->isRoot($entry)) { - $this->Session->setFlash(__('delete_tree_success'), 'flash/notice'); + $this->Session->setFlash(__('delete_tree_success'), 'flash/success'); $this->redirect('/'); } else { - $this->Session->setFlash(__('delete_subtree_success'), 'flash/notice'); + $this->Session->setFlash(__('delete_subtree_success'), 'flash/success'); $this->redirect('/entries/view/' . $entry['Entry']['pid']); } } else { From 7de918ef72996325a0b1574e5d9c9ddedef41e19 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 11:49:32 +0200 Subject: [PATCH 53/72] new SettingHelper --- app/Controller/SettingsController.php | 7 +- app/View/Helper/SettingHelper.php | 48 ++ app/View/Settings/admin_index.ctp | 789 +++----------------------- 3 files changed, 134 insertions(+), 710 deletions(-) create mode 100644 app/View/Helper/SettingHelper.php diff --git a/app/Controller/SettingsController.php b/app/Controller/SettingsController.php index cce715776..88189323d 100755 --- a/app/Controller/SettingsController.php +++ b/app/Controller/SettingsController.php @@ -5,9 +5,10 @@ class SettingsController extends AppController { public $name = 'Settings'; - public $helpers = array( - 'TimeH', - ); + public $helpers = [ + 'Setting', + 'TimeH' + ]; /** * Subset of MLF settings currently used by Saito diff --git a/app/View/Helper/SettingHelper.php b/app/View/Helper/SettingHelper.php new file mode 100644 index 000000000..42740a197 --- /dev/null +++ b/app/View/Helper/SettingHelper.php @@ -0,0 +1,48 @@ +tableHeaders(); + foreach ($setting_names as $name) { + $out .= $this->tableRow($name, $Settings); + } + $out = '' + . $out + . '
'; + $out = '

' . $table_name . '

' . $out; + return $out; + } + + public function tableRow($name, $Settings) { + return $this->Html->tableCells( + [ + __($name), + $Settings[$name], + "

" . __($name . '_exp') . "

", + $this->Html->link( + __('edit'), + ['controller' => 'settings', 'action' => 'edit', $name], + ['class' => 'btn'] + ) + ] + ); + } + + public function tableHeaders() { + return $this->Html->tableHeaders( + [ + __('Key'), + __('Value'), + __('Explanation'), + __('Actions') + ] + ); + } + } diff --git a/app/View/Settings/admin_index.ctp b/app/View/Settings/admin_index.ctp index e5bd4c2fd..0f8f730c4 100644 --- a/app/View/Settings/admin_index.ctp +++ b/app/View/Settings/admin_index.ctp @@ -1,724 +1,99 @@ Html->addCrumb(__('Settings'), '/admin/settings'); - $tableHeadersHtml = $this->Html->tableHeaders(array( - __('Key'), - __('Value'), - __('Explanation'), - __('Actions') - )); + $tableHeadersHtml = $this->Setting->tableHeaders(); ?>

-

- - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'forum_disabled' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'forum_disabled_text'), - array( 'class' => 'btn' ) - ); - ?> -
+ - - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'forum_name' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'timezone'), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Deactivate Forum'), + ['forum_disabled', 'forum_disabled_text'], + $Settings + ); -

- - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'forum_email' ), - array( 'class' => 'btn' ) - ); - ?> -
-

- -

+ echo $this->Setting->table( + __('Base Preferences'), + ['forum_name', 'timezone'], + $Settings + ); -

- - - - - - - - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'block_user_ui' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'store_ip' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'store_ip_anonymized' ), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Email'), + ['forum_email'], + $Settings + ); -

- - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'tos_enabled' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'tos_url' ), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Moderation'), + ['block_user_ui', 'store_ip', 'store_ip_anonymized'], + $Settings + ); -

- - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'edit_period' ), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Registration'), + ['tos_enabled', 'tos_url'], + $Settings + ); -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'edit_delay' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'topics_per_page' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'thread_depth_indent' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'autolink' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'bbcode_img' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'quote_symbol' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'signature_separator' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'subject_maxlength' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'text_word_maxlength' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'userranks_show' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'userranks_ranks' ), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'video_domains_allowed' ), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Edit'), + ['edit_period', 'edit_delay'], + $Settings + ); -

- - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'category_chooser_global'), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'category_chooser_user_override'), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('View'), + [ + 'topics_per_page', + 'thread_depth_indent', + 'autolink', + 'bbcode_img', + 'quote_symbol', + 'signature_separator', + 'subject_maxlength', + 'text_word_maxlength', + 'userranks_show', + 'userranks_ranks', + 'video_domains_allowed' + ], + $Settings + ); -

Shoutbox

- - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'shoutbox_enabled'), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'shoutbox_max_shouts'), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Category Chooser'), + ['category_chooser_global', 'category_chooser_user_override'], + $Settings + ); -

Uploads

- - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'upload_max_img_size'), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'upload_max_number_of_uploads'), - array( 'class' => 'btn' ) - ); - ?> -
-

Html->link('Flattr', 'http://flattr.com/'); ?>

- - - - - - - - - - - - - - - - - - - - -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'flattr_enabled'), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'flattr_language'), - array( 'class' => 'btn' ) - ); - ?> -
- - - - -

-
- Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'flattr_category'), - array( 'class' => 'btn' ) - ); - ?> -
+ echo $this->Setting->table( + __('Shoutbox'), + ['shoutbox_enabled', 'shoutbox_max_shouts'], + $Settings + ); -

Html->link('Embed.ly', 'http://embed.ly/'); ?>

- - - Html->tableCells(array( - array( - __('embedly_enabled'), - $Settings['embedly_enabled'], - __('embedly_enabled_exp'), - $this->Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'embedly_enabled' ), - array( 'class' => 'btn' ) - ) - ), - array( - __('embedly_key'), - $Settings['embedly_key'], - __('embedly_key_exp'), - $this->Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'embedly_key' ), - array( 'class' => 'btn' ) - ) - ) - )); - ?> -
+ echo $this->Setting->table( + __('Uploads'), + ['upload_max_img_size', 'upload_max_number_of_uploads'], + $Settings + ); -

- - - Html->tableCells(array( - array( - __('stopwatch_get'), - $Settings['stopwatch_get'], - __('stopwatch_get_exp'), - $this->Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'stopwatch_get' ), - array( 'class' => 'btn' ) - ) - ), - )); - ?> -
+ echo $this->Setting->table( + $this->Html->link('Flattr', 'http://flattr.com/'), + ['flattr_enabled', 'flattr_language', 'flattr_category'], + $Settings + ); + + echo $this->Setting->table( + $this->Html->link('Embed.ly', 'http://embed.ly/'), + ['embedly_enabled', 'embedly_key'], + $Settings + ); + + echo $this->Setting->table( + __('Debug'), + ['stopwatch_get'], + $Settings + ); + ?>
From 1985f7256c189cdda49c8ec843be17128d442579 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 13:06:23 +0200 Subject: [PATCH 54/72] new scrollspy for admin settings --- app/View/Helper/SettingHelper.php | 24 +++- app/View/Settings/admin_index.ctp | 189 +++++++++++++++++------------- 2 files changed, 129 insertions(+), 84 deletions(-) diff --git a/app/View/Helper/SettingHelper.php b/app/View/Helper/SettingHelper.php index 42740a197..d0dd22739 100644 --- a/app/View/Helper/SettingHelper.php +++ b/app/View/Helper/SettingHelper.php @@ -4,22 +4,42 @@ class SettingHelper extends AppHelper { + protected $_headers = []; + public $helpers = [ 'Html' ]; - public function table($table_name, array $setting_names, $Settings) { + public function table($table_name, array $setting_names, $Settings, array $options = []) { + $defaults = [ + 'nav-title' => $table_name + ]; + $options += $defaults; + $out = $this->tableHeaders(); foreach ($setting_names as $name) { $out .= $this->tableRow($name, $Settings); } + $key = $this->addHeader($options['nav-title']); $out = '' . $out . '
'; - $out = '

' . $table_name . '

' . $out; + $out = '' + . '

' . $table_name . '

' + . $out; return $out; } + public function addHeader($header) { + $id = count($this->_headers) + 1; + $this->_headers[$id] = $header; + return $id; + } + + public function getHeaders() { + return $this->_headers; + } + public function tableRow($name, $Settings) { return $this->Html->tableCells( [ diff --git a/app/View/Settings/admin_index.ctp b/app/View/Settings/admin_index.ctp index 0f8f730c4..e5951583d 100644 --- a/app/View/Settings/admin_index.ctp +++ b/app/View/Settings/admin_index.ctp @@ -1,99 +1,124 @@ Html->addCrumb(__('Settings'), '/admin/settings'); $tableHeadersHtml = $this->Setting->tableHeaders(); -?> -
-

- Setting->table( - __('Deactivate Forum'), - ['forum_disabled', 'forum_disabled_text'], - $Settings - ); + $this->start('settings'); + echo $this->Setting->table( + __('Deactivate Forum'), + ['forum_disabled', 'forum_disabled_text'], + $Settings + ); - echo $this->Setting->table( - __('Base Preferences'), - ['forum_name', 'timezone'], - $Settings - ); + echo $this->Setting->table( + __('Base Preferences'), + ['forum_name', 'timezone'], + $Settings + ); - echo $this->Setting->table( - __('Email'), - ['forum_email'], - $Settings - ); + echo $this->Setting->table( + __('Email'), + ['forum_email'], + $Settings + ); - echo $this->Setting->table( - __('Moderation'), - ['block_user_ui', 'store_ip', 'store_ip_anonymized'], - $Settings - ); + echo $this->Setting->table( + __('Moderation'), + ['block_user_ui', 'store_ip', 'store_ip_anonymized'], + $Settings + ); - echo $this->Setting->table( - __('Registration'), - ['tos_enabled', 'tos_url'], - $Settings - ); + echo $this->Setting->table( + __('Registration'), + ['tos_enabled', 'tos_url'], + $Settings + ); - echo $this->Setting->table( - __('Edit'), - ['edit_period', 'edit_delay'], - $Settings - ); + echo $this->Setting->table( + __('Edit'), + ['edit_period', 'edit_delay'], + $Settings + ); - echo $this->Setting->table( - __('View'), - [ - 'topics_per_page', - 'thread_depth_indent', - 'autolink', - 'bbcode_img', - 'quote_symbol', - 'signature_separator', - 'subject_maxlength', - 'text_word_maxlength', - 'userranks_show', - 'userranks_ranks', - 'video_domains_allowed' - ], - $Settings - ); + echo $this->Setting->table( + __('View'), + [ + 'topics_per_page', + 'thread_depth_indent', + 'autolink', + 'bbcode_img', + 'quote_symbol', + 'signature_separator', + 'subject_maxlength', + 'text_word_maxlength', + 'userranks_show', + 'userranks_ranks', + 'video_domains_allowed' + ], + $Settings + ); - echo $this->Setting->table( - __('Category Chooser'), - ['category_chooser_global', 'category_chooser_user_override'], - $Settings - ); + echo $this->Setting->table( + __('Category Chooser'), + ['category_chooser_global', 'category_chooser_user_override'], + $Settings + ); - echo $this->Setting->table( - __('Shoutbox'), - ['shoutbox_enabled', 'shoutbox_max_shouts'], - $Settings - ); + echo $this->Setting->table( + __('Shoutbox'), + ['shoutbox_enabled', 'shoutbox_max_shouts'], + $Settings + ); - echo $this->Setting->table( - __('Uploads'), - ['upload_max_img_size', 'upload_max_number_of_uploads'], - $Settings - ); + echo $this->Setting->table( + __('Uploads'), + ['upload_max_img_size', 'upload_max_number_of_uploads'], + $Settings + ); - echo $this->Setting->table( - $this->Html->link('Flattr', 'http://flattr.com/'), - ['flattr_enabled', 'flattr_language', 'flattr_category'], - $Settings - ); + echo $this->Setting->table( + $this->Html->link('Flattr', 'http://flattr.com/'), + ['flattr_enabled', 'flattr_language', 'flattr_category'], + $Settings, + ['nav-title' => 'Flattr'] + ); - echo $this->Setting->table( - $this->Html->link('Embed.ly', 'http://embed.ly/'), - ['embedly_enabled', 'embedly_key'], - $Settings - ); + echo $this->Setting->table( + $this->Html->link('Embed.ly', 'http://embed.ly/'), + ['embedly_enabled', 'embedly_key'], + $Settings, + ['nav-title' => 'Embedly'] + ); - echo $this->Setting->table( - __('Debug'), - ['stopwatch_get'], - $Settings - ); - ?> + echo $this->Setting->table( + __('Debug'), + ['stopwatch_get'], + $Settings + ); + $this->end('settings'); +?> +
+
+ +
+

+ fetch('settings') ?> +
+
+ From 3385d6f0f605e62bea3042366befcf47e216b165 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 14:20:48 +0200 Subject: [PATCH 55/72] updated l10n --- app/Locale/deu/LC_MESSAGES/default.po | 7 +++---- app/Locale/eng/LC_MESSAGES/default.po | 28 +++++++++++++-------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/app/Locale/deu/LC_MESSAGES/default.po b/app/Locale/deu/LC_MESSAGES/default.po index 7efef771f..8c011254b 100644 --- a/app/Locale/deu/LC_MESSAGES/default.po +++ b/app/Locale/deu/LC_MESSAGES/default.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-05-30 12:55+0100\n" -"PO-Revision-Date: 2013-06-06 19:45+0100\n" +"PO-Revision-Date: 2013-07-29 14:20+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: de_DE\n" @@ -14,7 +14,7 @@ msgstr "" "X-Poedit-Basepath: /Users/siezi/Sites/private/personal_projects/" "macnemo_2_github/app/\n" "X-Poedit-SourceCharset: UTF-8\n" -"X-Generator: Poedit 1.5.5\n" +"X-Generator: Poedit 1.5.7\n" "X-Poedit-SearchPath-0: .\n" #: Plugin/Install/View/Install/adminuser.ctp:2 @@ -1161,11 +1161,10 @@ msgid "stopwatch_get" msgstr "Stopuhr" #: View/Settings/admin_index.ctp:714 -#, fuzzy msgid "stopwatch_get_exp" msgstr "" "Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ an " -"URL." +"URL in Production-Modus für eingeloggte Nutzer." #: View/SmileyCodes/admin_add.ctp:4 #, fuzzy diff --git a/app/Locale/eng/LC_MESSAGES/default.po b/app/Locale/eng/LC_MESSAGES/default.po index 577ace188..208886d73 100644 --- a/app/Locale/eng/LC_MESSAGES/default.po +++ b/app/Locale/eng/LC_MESSAGES/default.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-03-05 13:35+0100\n" -"PO-Revision-Date: 2013-03-08 12:16+0100\n" +"PO-Revision-Date: 2013-07-29 14:19+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: en_US\n" @@ -15,7 +15,7 @@ msgstr "" "macnemo_2_github/app/\n" "'auth_autherror', true;X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n" -"X-Generator: Poedit 1.5.5\n" +"X-Generator: Poedit 1.5.7\n" "X-Poedit-SearchPath-0: .\n" #: Plugin/Install/View/Install/adminuser.ctp:2 @@ -769,11 +769,11 @@ msgstr "" #: View/Settings/admin_index.ctp:17 msgid "forum_disabled" -msgstr "" +msgstr "Disable Forum" #: View/Settings/admin_index.ctp:23 msgid "forum_disabled_exp" -msgstr "" +msgstr "Shows the maintenance page except for logged in admins." #: View/Settings/admin_index.ctp:27 View/Settings/admin_index.ctp:46 #: View/Settings/admin_index.ctp:70 View/Settings/admin_index.ctp:89 @@ -798,11 +798,11 @@ msgstr "" #: View/Settings/admin_index.ctp:36 msgid "forum_disabled_text" -msgstr "" +msgstr "Maintenance Notice" #: View/Settings/admin_index.ctp:42 msgid "forum_disabled_text_exp" -msgstr "" +msgstr "Text notice shown on the forum disabled page." #: View/Settings/admin_index.ctp:55 msgid "Base Preferences" @@ -818,11 +818,11 @@ msgstr "The name of this forum." #: View/Settings/admin_index.ctp:79 msgid "timezone" -msgstr "" +msgstr "Timezone" #: View/Settings/admin_index.ctp:85 msgid "timezone_exp" -msgstr "" +msgstr "All time values displayed are offset to match this timezone." #: View/Settings/admin_index.ctp:98 msgid "Email" @@ -1051,19 +1051,19 @@ msgstr "Number of entries shown in the Shoutbox." #: View/Settings/admin_index.ctp:578 msgid "upload_max_img_size" -msgstr "" +msgstr "Upload Max Image Size " #: View/Settings/admin_index.ctp:584 msgid "upload_max_img_size_exp" -msgstr "" +msgstr "Max image size in kB for uploaded content." #: View/Settings/admin_index.ctp:597 msgid "upload_max_number_of_uploads" -msgstr "" +msgstr "Max Number of Uploads" #: View/Settings/admin_index.ctp:603 msgid "upload_max_number_of_uploads_exp" -msgstr "" +msgstr "Max number of uploaded files for a single user." #: View/Settings/admin_index.ctp:620 msgid "flattr_enabled" @@ -1118,10 +1118,10 @@ msgid "stopwatch_get" msgstr "Stopwatch" #: View/Settings/admin_index.ctp:714 -#, fuzzy msgid "stopwatch_get_exp" msgstr "" -"Outputs stopwatch times by appending /stopwatch:true/ to url." +"Outputs stopwatch times in production mode for logged in users by appending " +"/stopwatch:true/ to url." #: View/SmileyCodes/admin_add.ctp:4 msgid "Add Smiley Code" From d9e35120a3b818c67eb7991b1a6517f517038fb2 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 14:54:08 +0200 Subject: [PATCH 56/72] new SettingHelper needs nondynamic po file --- app/Locale/deu/LC_MESSAGES/default.po | 811 ++++++++++------------ app/Locale/deu/LC_MESSAGES/nondynamic.po | 284 ++++++++ app/Locale/eng/LC_MESSAGES/default.po | 832 ++++++++--------------- app/Locale/eng/LC_MESSAGES/nondynamic.po | 285 ++++++++ app/View/Helper/SettingHelper.php | 4 +- app/View/Settings/admin_index.ctp | 1 - 6 files changed, 1203 insertions(+), 1014 deletions(-) diff --git a/app/Locale/deu/LC_MESSAGES/default.po b/app/Locale/deu/LC_MESSAGES/default.po index 8c011254b..68ce5d235 100644 --- a/app/Locale/deu/LC_MESSAGES/default.po +++ b/app/Locale/deu/LC_MESSAGES/default.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-05-30 12:55+0100\n" -"PO-Revision-Date: 2013-07-29 14:20+0100\n" +"POT-Creation-Date: 2013-07-29 14:53+0100\n" +"PO-Revision-Date: 2013-07-29 14:53+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: de_DE\n" @@ -98,8 +98,8 @@ msgstr "Einträge" msgid "User Registrations" msgstr "Benutzerregistrierungen" -#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:323 -#: Controller/EntriesController.php:371 +#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:298 +#: Controller/EntriesController.php:351 msgid "back_to_overview_linkname" msgstr "Zurück zur Übersicht" @@ -124,6 +124,7 @@ msgid "Bookmarks" msgstr "Lesezeichen" #: View/Bookmarks/index.ctp:24 View/Categories/view.ctp:64 +#: View/Entries/add.ctp:89 msgid "Subject" msgstr "Betreff" @@ -157,9 +158,9 @@ msgstr "Absenden" #: View/Categories/add.ctp:16 View/Categories/admin_index.ctp:16 #: View/Categories/edit.ctp:17 View/Categories/index.ctp:11 #: View/Categories/index.ctp:51 View/Categories/view.ctp:37 -#: View/Categories/view.ctp:73 View/Settings/admin_index.ctp:7 -#: View/SmileyCodes/admin_add.ctp:14 View/SmileyCodes/admin_index.ctp:12 -#: View/Smilies/admin_add.ctp:18 View/Smilies/admin_index.ctp:14 +#: View/Categories/view.ctp:73 View/SmileyCodes/admin_add.ctp:14 +#: View/SmileyCodes/admin_index.ctp:12 View/Smilies/admin_add.ctp:18 +#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:64 msgid "Actions" msgstr "Aktion" @@ -238,7 +239,7 @@ msgid "New Category" msgstr "Neue Kategorie" #: View/Categories/admin_index.ctp:34 View/Categories/index.ctp:30 -#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:230 +#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:37 #: View/SmileyCodes/admin_index.ctp:29 View/Smilies/admin_index.ctp:31 #: View/Users/view.ctp:190 msgid "Edit" @@ -252,7 +253,7 @@ msgid "Are you sure you want to delete # %s?" msgstr "# %s wirklich löschen?" #: View/Categories/index.ctp:29 View/Categories/view.ctp:106 -#: View/Settings/admin_index.ctp:254 +#: View/Settings/admin_index.ctp:43 msgid "View" msgstr "Anzeigen" @@ -390,7 +391,7 @@ msgstr "" "einzelne Kategorie ohne Auswahländerung aktivieren." #: View/Elements/entry/category-chooser.ctp:42 View/Entries/search.ctp:82 -#: Controller/EntriesController.php:889 +#: Controller/EntriesController.php:927 msgid "All Categories" msgstr "Alle Kategorien" @@ -445,7 +446,7 @@ msgstr "Antworten" #: View/Elements/entry/view_posting.ctp:76 #: View/Elements/entry/view_posting.ctp:104 -#: Controller/EntriesController.php:479 +#: Controller/EntriesController.php:457 msgid "edit_linkname" msgstr "Bearbeiten" @@ -541,12 +542,12 @@ msgstr "Keine Hilfe für diese Seite verfügbar." msgid "search_submit" msgstr "Suchen" -#: View/Elements/layout/slidetab_recententries.ctp:10 +#: View/Elements/layout/slidetab_recententries.ctp:9 #, fuzzy msgid "Recent entries" msgstr "Letzte Einträge" -#: View/Elements/layout/slidetab_recentposts.ctp:12 View/Users/view.ctp:229 +#: View/Elements/layout/slidetab_recentposts.ctp:13 View/Users/view.ctp:229 msgid "user_recentposts" msgstr "Letzte Beiträge" @@ -629,38 +630,38 @@ msgstr "" "Um Ihr Benutzerkonto zu aktivieren, besuchen Sie bitte folgenden Verweis " "innerhalb der nächsten 24 Stunden: %s" -#: View/Entries/add.ctp:31 View/Entries/add.ctp:220 +#: View/Entries/add.ctp:34 View/Entries/add.ctp:187 msgid "preview" msgstr "Vorschau" -#: View/Entries/add.ctp:79 Controller/UsersController.php:485 +#: View/Entries/add.ctp:87 Controller/UsersController.php:477 msgid "error_subject_empty" msgstr "Betreff darf nicht leer sein." -#: View/Entries/add.ctp:116 +#: View/Entries/add.ctp:130 msgid "Cite" msgstr "Zitieren" -#: View/Entries/add.ctp:138 +#: View/Entries/add.ctp:155 View/Entries/add.ctp:175 View/Entries/add.ctp:289 +msgid "submit_button" +msgstr "Eintragen" + +#: View/Entries/add.ctp:202 msgid "Notify on reply" msgstr "Beitrag abonnieren" -#: View/Entries/add.ctp:146 +#: View/Entries/add.ctp:216 msgid "Notify on thread replies" msgstr "Thread abonnieren" -#: View/Entries/add.ctp:153 View/Helper/EntryHHelper.php:159 +#: View/Entries/add.ctp:225 View/Helper/EntryHHelper.php:159 msgid "entry_nsfw_title" msgstr "Nicht bürosicher (NBS)" -#: View/Entries/add.ctp:161 +#: View/Entries/add.ctp:238 msgid "entry_flattr_this_posting" msgstr "Flattr-Knopf anzeigen" -#: View/Entries/add.ctp:200 View/Entries/add.ctp:209 View/Entries/add.ctp:239 -msgid "submit_button" -msgstr "Eintragen" - #: View/Entries/index.ctp:6 msgid "new_entry_linkname" msgstr "Neuer Eintrag" @@ -727,7 +728,7 @@ msgid "Overview" msgstr "Übersicht" #: View/Layouts/admin.ctp:41 View/Settings/admin_edit.ctp:2 -#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:11 +#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:113 #: View/Users/edit.ctp:163 msgid "Settings" msgstr "Einstellungen" @@ -757,11 +758,11 @@ msgstr "" msgid "RSS Feed" msgstr "RSS Feed" -#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:121 +#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:110 msgid "Last entries" msgstr "Letzte Einträge" -#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:117 +#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:106 msgid "Last started threads" msgstr "Letzte Threads" @@ -769,403 +770,46 @@ msgstr "Letzte Threads" msgid "_exp" msgstr "" -# View/Settings/admin_index.ctp: -#: View/Settings/admin_index.ctp:4 -msgid "Key" -msgstr "Schlüssel" - -# View/Settings/admin_index.ctp: -#: View/Settings/admin_index.ctp:5 -msgid "Value" -msgstr "Wert" - -# View/Settings/admin_index.ctp: -#: View/Settings/admin_index.ctp:6 -msgid "Explanation" -msgstr "Beschreibung" - -#: View/Settings/admin_index.ctp:12 +#: View/Settings/admin_index.ctp:7 msgid "Deactivate Forum" msgstr "Forum deaktivieren" -#: View/Settings/admin_index.ctp:17 -msgid "forum_disabled" -msgstr "Forum deaktivieren" - -#: View/Settings/admin_index.ctp:23 -msgid "forum_disabled_exp" -msgstr "Forum deaktivieren." - -#: View/Settings/admin_index.ctp:27 View/Settings/admin_index.ctp:46 -#: View/Settings/admin_index.ctp:70 View/Settings/admin_index.ctp:89 -#: View/Settings/admin_index.ctp:113 View/Settings/admin_index.ctp:140 -#: View/Settings/admin_index.ctp:159 View/Settings/admin_index.ctp:178 -#: View/Settings/admin_index.ctp:202 View/Settings/admin_index.ctp:221 -#: View/Settings/admin_index.ctp:245 View/Settings/admin_index.ctp:269 -#: View/Settings/admin_index.ctp:288 View/Settings/admin_index.ctp:307 -#: View/Settings/admin_index.ctp:326 View/Settings/admin_index.ctp:345 -#: View/Settings/admin_index.ctp:364 View/Settings/admin_index.ctp:383 -#: View/Settings/admin_index.ctp:402 View/Settings/admin_index.ctp:421 -#: View/Settings/admin_index.ctp:440 View/Settings/admin_index.ctp:459 -#: View/Settings/admin_index.ctp:478 View/Settings/admin_index.ctp:502 -#: View/Settings/admin_index.ctp:521 View/Settings/admin_index.ctp:545 -#: View/Settings/admin_index.ctp:564 View/Settings/admin_index.ctp:588 -#: View/Settings/admin_index.ctp:607 View/Settings/admin_index.ctp:630 -#: View/Settings/admin_index.ctp:649 View/Settings/admin_index.ctp:668 -#: View/Settings/admin_index.ctp:687 View/Settings/admin_index.ctp:697 -#: View/Settings/admin_index.ctp:716 -msgid "edit" -msgstr "Bearbeiten" - -#: View/Settings/admin_index.ctp:36 -msgid "forum_disabled_text" -msgstr "Forum deaktivieren Infotext" - -#: View/Settings/admin_index.ctp:42 -msgid "forum_disabled_text_exp" -msgstr "Infotext, welcher auf der Hinweisseite mit ausgegeben wird." - -#: View/Settings/admin_index.ctp:55 +#: View/Settings/admin_index.ctp:13 msgid "Base Preferences" msgstr "Grundeinstellungen" -#: View/Settings/admin_index.ctp:60 -msgid "forum_name" -msgstr "Forumtitel" - -#: View/Settings/admin_index.ctp:66 -msgid "forum_name_exp" -msgstr "Der Name dieses Forums." - -#: View/Settings/admin_index.ctp:79 -msgid "timezone" -msgstr "Zeitzone" - -#: View/Settings/admin_index.ctp:85 -msgid "timezone_exp" -msgstr "Zeitzone, indem sich das Forum befindet." - -#: View/Settings/admin_index.ctp:98 +#: View/Settings/admin_index.ctp:19 #, fuzzy msgid "Email" msgstr "E-Mail" -#: View/Settings/admin_index.ctp:103 -#, fuzzy -msgid "forum_email" -msgstr "Email" - -#: View/Settings/admin_index.ctp:109 -#, fuzzy -msgid "forum_email_exp" -msgstr "" -"Emailadresse, welche als Absenderadresse für vom Forum versendet Emails " -"(bspw. bei der Registrierung) verwendet wird. " - -#: View/Settings/admin_index.ctp:122 -msgid "email_admin_config_exp" -msgstr "" -"Erweiterte Email-Einstellungen sind durch das Anlegen einer $saito-Konfiguration in app/config/email.php möglich. Als " -"Vorlage sollte app/config/email.php.default verwendet werden. " -"Siehe auch." - -#: View/Settings/admin_index.ctp:125 +#: View/Settings/admin_index.ctp:25 msgid "Moderation" msgstr "Moderation" -# -#: View/Settings/admin_index.ctp:130 -msgid "block_user_ui" -msgstr "Nutzer sperren" - -# -#: View/Settings/admin_index.ctp:136 -msgid "block_user_ui_exp" -msgstr "Moderatoren dürfen Benutzer sperren." - -#: View/Settings/admin_index.ctp:149 -msgid "store_ip" -msgstr "IP speichern" - -#: View/Settings/admin_index.ctp:155 -msgid "store_ip_exp" -msgstr "" -"Beim Einträgen ins Forum werden die IP-Adresse des Eintragenden gespeichert. " -"Moderatoren wird die Adresse im Eintrag angezeigt." - -#: View/Settings/admin_index.ctp:168 -msgid "store_ip_anonymized" -msgstr "IP anonymisieren" - -#: View/Settings/admin_index.ctp:174 -msgid "store_ip_anonymized_exp" -msgstr "Anonymisieren der gespeicherten IP-Adresse." - -#: View/Settings/admin_index.ctp:187 +#: View/Settings/admin_index.ctp:31 #, fuzzy msgid "Registration" msgstr "Registrierung" -#: View/Settings/admin_index.ctp:192 -#, fuzzy -msgid "tos_enabled" -msgstr "Nutzungsbedingungen aktivieren" - -#: View/Settings/admin_index.ctp:198 -#, fuzzy -msgid "tos_enabled_exp" -msgstr "" -"Setzt einen Verweis und eine zu bestätigende Checkbox für die " -"Nutzungsbedingungen auf die Registrierungsseite." - -#: View/Settings/admin_index.ctp:211 -msgid "tos_url" -msgstr "Nutzungsbedingungen-URL" - -#: View/Settings/admin_index.ctp:217 -#, fuzzy -msgid "tos_url_exp" -msgstr "" -"URL zu den Nutzungsbedingen. Falls leer, werden die eingebauten " -"Standardseiten verwendet." - -# -#: View/Settings/admin_index.ctp:235 -msgid "edit_period" -msgstr "Nachbearbeitungszeit" - -# -#: View/Settings/admin_index.ctp:241 -msgid "edit_period_exp" -msgstr "" -"Minuten, die ein neuer Eintrag nach dem Anlegen nachbearbeitet werden kann." - -# -#: View/Settings/admin_index.ctp:259 -msgid "edit_delay" -msgstr "Nachbearbeiten anzeigen" - -# -#: View/Settings/admin_index.ctp:265 -msgid "edit_delay_exp" -msgstr "" -"Minuten, die ein Eintrag nach dem Anlegen ohne Bearbeitungshinweis " -"nachbearbeitet werden kann." - -#: View/Settings/admin_index.ctp:278 -msgid "topics_per_page" -msgstr "Threads pro Seite" - -#: View/Settings/admin_index.ctp:284 -msgid "topics_per_page_exp" -msgstr "Anzahl der Threads, die auf der Startseite angezeigt werden." - -#: View/Settings/admin_index.ctp:297 -msgid "thread_depth_indent" -msgstr "Einrücktiefe Threads" - -#: View/Settings/admin_index.ctp:303 -msgid "thread_depth_indent_exp" -msgstr "" -"Ab dieser Tiefe werden werden Einträge in der Thread-Ansicht nicht weiter " -"nach rechts eingerückt dargestellt." - -#: View/Settings/admin_index.ctp:316 -msgid "autolink" -msgstr "Autolink" - -#: View/Settings/admin_index.ctp:322 -msgid "autolink_exp" -msgstr "Verweise im Text automatisch erkennen und verlinken." - -#: View/Settings/admin_index.ctp:335 -msgid "bbcode_img" -msgstr "Bilder" - -#: View/Settings/admin_index.ctp:341 -msgid "bbcode_img_exp" -msgstr "Einbinden von Bildern erlauben." - -#: View/Settings/admin_index.ctp:354 -msgid "quote_symbol" -msgstr "Zitatzeichen" - -#: View/Settings/admin_index.ctp:360 -msgid "quote_symbol_exp" -msgstr "Zitatzeichen" - -#: View/Settings/admin_index.ctp:373 -msgid "signature_separator" -msgstr "Signaturtrenner" - -#: View/Settings/admin_index.ctp:379 -msgid "signature_separator_exp" -msgstr "Signaturtrenner" - -#: View/Settings/admin_index.ctp:392 -msgid "subject_maxlength" -msgstr "Betrefflänge" - -#: View/Settings/admin_index.ctp:398 -msgid "subject_maxlength_exp" -msgstr "Maximale Zeichenanzahl in Betreff." - -#: View/Settings/admin_index.ctp:411 -msgid "text_word_maxlength" -msgstr "Wordlänge" - -#: View/Settings/admin_index.ctp:417 -msgid "text_word_maxlength_exp" -msgstr "Maximale Zeichenanzahl pro Wort." - -#: View/Settings/admin_index.ctp:430 -msgid "userranks_show" -msgstr "Ränge anzeigen" - -#: View/Settings/admin_index.ctp:436 -msgid "userranks_show_exp" -msgstr "Benutzerränge anzeigen." - -#: View/Settings/admin_index.ctp:449 -msgid "userranks_ranks" -msgstr "Ränge" - -#: View/Settings/admin_index.ctp:455 -msgid "userranks_ranks_exp" -msgstr "Benutzerrang basierend auf Anzahl der gemachten Beiträge." - -# -#: View/Settings/admin_index.ctp:468 -msgid "video_domains_allowed" -msgstr "Einbettbare (Video-) Quellen" - -# -#: View/Settings/admin_index.ctp:474 -msgid "video_domains_allowed_exp" -msgstr "" -"Domainnamen einbindbar für Video und iFrame. Ein Asterisk * bedeuted freies " -"Einbinden (Vorsicht, dies kann ebenfalls zum Einbinden von Schadsoftware " -"genutzt werden!)." - -#: View/Settings/admin_index.ctp:487 +#: View/Settings/admin_index.ctp:61 #, fuzzy msgid "Category Chooser" msgstr "Kategorienfilter" -#: View/Settings/admin_index.ctp:492 -msgid "category_chooser_global" -msgstr "Standard" - -#: View/Settings/admin_index.ctp:498 -msgid "category_chooser_global_exp" -msgstr "Kategorienfilter auf der Frontseite immer anzeigen." - -#: View/Settings/admin_index.ctp:511 -msgid "category_chooser_user_override" -msgstr "Benutzereinstellung" - -#: View/Settings/admin_index.ctp:517 -msgid "category_chooser_user_override_exp" -msgstr "" -"Erlaubt einem Benutzern den Kategorienfilter in seinen Einstellungen zu " -"aktivieren, selbst wenn der Filter standardmässig deaktiviert ist." - -#: View/Settings/admin_index.ctp:535 -msgid "shoutbox_enabled" -msgstr "Aktivieren" - -#: View/Settings/admin_index.ctp:541 -msgid "shoutbox_enabled_exp" -msgstr "Shoutbox aktivieren" - -#: View/Settings/admin_index.ctp:554 -msgid "shoutbox_max_shouts" -msgstr "Anzahl" - -#: View/Settings/admin_index.ctp:560 -msgid "shoutbox_max_shouts_exp" -msgstr "Anzahl der angezeigten Shoutboxeinträge" - -#: View/Settings/admin_index.ctp:578 -msgid "upload_max_img_size" -msgstr "Uploadgröße maximal" - -#: View/Settings/admin_index.ctp:584 -msgid "upload_max_img_size_exp" -msgstr "Maximale Größe, die ein Upload besitzen darf in kB." - -#: View/Settings/admin_index.ctp:597 -msgid "upload_max_number_of_uploads" -msgstr "Anzahl an Uploads pro Benutzer" - -#: View/Settings/admin_index.ctp:603 -msgid "upload_max_number_of_uploads_exp" -msgstr "" -"Maximale Anzahl von Dateien, die ein einzelner Benutzer gleichzeitig auf dem " -"Server hochgeladen haben kann." - -#: View/Settings/admin_index.ctp:620 -msgid "flattr_enabled" -msgstr "Flattr aktivieren" - -#: View/Settings/admin_index.ctp:626 -msgid "flattr_enabled_exp" -msgstr "Ermöglicht es Benutzern flattr zu verwenden." - -#: View/Settings/admin_index.ctp:639 -msgid "flattr_language" -msgstr "Flattr-Sprache" - -#: View/Settings/admin_index.ctp:645 -msgid "flattr_language_exp" -msgstr "" -"Sprache, die flattr den hereinzeigenden Verweisen zuordnet. Siehe Doku auf " -"flattr.com für Kürzel." - -#: View/Settings/admin_index.ctp:658 -msgid "flattr_category" -msgstr "Flattr-Kategorie" - -#: View/Settings/admin_index.ctp:664 -msgid "flattr_category_exp" +#: View/Settings/admin_index.ctp:67 +msgid "Shoutbox" msgstr "" -"Kategorie, die Flattr den hereinzeigenden Verweisen zuordnet. Siehe flattr." -"com für verfügbare Kategorien." - -#: View/Settings/admin_index.ctp:683 -msgid "embedly_enabled" -msgstr "Embed.ly aktivieren" -#: View/Settings/admin_index.ctp:685 -msgid "embedly_enabled_exp" -msgstr "" -"Aktiviert den [embed]-BBcode, welcher seinen Inhalt durch Embed.ly darstellt." - -#: View/Settings/admin_index.ctp:693 -msgid "embedly_key" -msgstr "Embed.ly Key" - -# -#: View/Settings/admin_index.ctp:695 -msgid "embedly_key_exp" -msgstr "API-Key, den man nach der Registrierung bei Embed.ly erhält." +#: View/Settings/admin_index.ctp:73 +#, fuzzy +msgid "Uploads" +msgstr "Hochladen" -#: View/Settings/admin_index.ctp:706 +#: View/Settings/admin_index.ctp:93 msgid "Debug" msgstr "" -#: View/Settings/admin_index.ctp:712 -msgid "stopwatch_get" -msgstr "Stopuhr" - -#: View/Settings/admin_index.ctp:714 -msgid "stopwatch_get_exp" -msgstr "" -"Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ an " -"URL in Production-Modus für eingeloggte Nutzer." - #: View/SmileyCodes/admin_add.ctp:4 #, fuzzy msgid "Add Smiley Code" @@ -1573,7 +1217,7 @@ msgid "register_success_content" msgstr "" "Ihre Registrierung war erfolgreich. Sie können sich nun einloggen. Viel Spaß!" -#: View/Users/register.ctp:23 Controller/EntriesController.php:328 +#: View/Users/register.ctp:23 Controller/EntriesController.php:305 msgid "js-required" msgstr "Zum Nutzen des Forums bitte JavaScript aktivieren." @@ -1697,55 +1341,51 @@ msgstr "Flattr-Kategorie" msgid "Category deleted." msgstr "Kategorie" -#: Controller/EntriesController.php:103 +#: Controller/EntriesController.php:88 msgid "page" msgstr "Seite" -#: Controller/EntriesController.php:204 Controller/EntriesController.php:213 +#: Controller/EntriesController.php:187 Controller/EntriesController.php:196 msgid "Invalid post" msgstr "Posting konnte nicht gefunden werden." -#: Controller/EntriesController.php:255 +#: Controller/EntriesController.php:238 msgid "new_entry_linktitle" msgstr "Neuen Eintrag verfassen" -#: Controller/EntriesController.php:260 -msgid "logged_in_users_only" -msgstr "Nur für eingeloggte Benutzer." - -#: Controller/EntriesController.php:318 Controller/EntriesController.php:436 +#: Controller/EntriesController.php:293 Controller/EntriesController.php:414 msgid "Something clogged the tubes. Could not save entry. Try again." msgstr "" -#: Controller/EntriesController.php:364 Controller/EntriesController.php:476 +#: Controller/EntriesController.php:344 Controller/EntriesController.php:454 msgid "back_to_posting_from_linkname" msgstr "zurück zum Posting von %s" -#: Controller/EntriesController.php:375 +#: Controller/EntriesController.php:355 msgid "answer_marking" msgstr "Antwort verfassen" -#: Controller/EntriesController.php:442 +#: Controller/EntriesController.php:420 msgid "notice_you_are_editing_as_mod" msgstr "Achtung: Die folgende Aktion wird als Moderator vorgenommen!" -#: Controller/EntriesController.php:508 +#: Controller/EntriesController.php:485 msgid "delete_tree_success" msgstr "Thread erfolgreich entfernt." -#: Controller/EntriesController.php:511 +#: Controller/EntriesController.php:488 msgid "delete_subtree_success" msgstr "Subthread erfolgreich entfernt." -#: Controller/EntriesController.php:515 +#: Controller/EntriesController.php:492 msgid "delete_tree_error" msgstr "Thread konnte nicht gelöscht werden." -#: Controller/EntriesController.php:732 +#: Controller/EntriesController.php:707 msgid "Error" msgstr "" -#: Controller/EntriesController.php:925 +#: Controller/EntriesController.php:924 msgid "Custom" msgstr "Angepasst" @@ -1816,7 +1456,7 @@ msgstr "" msgid "Smily was not deleted" msgstr "" -#: Controller/ToolsController.php:30 +#: Controller/ToolsController.php:21 msgid "Caches have been emptied." msgstr "Caches wurden geleert." @@ -1824,98 +1464,98 @@ msgstr "Caches wurden geleert." msgid "upload_max_number_of_uploads_failed" msgstr "Maximale Anzahl an Uploads erreicht (%s)." -#: Controller/UsersController.php:56 Controller/UsersController.php:343 +#: Controller/UsersController.php:37 Controller/UsersController.php:329 #, php-format msgid "User %s is locked." msgstr "Benutzer %s wurde gesperrt." -#: Controller/UsersController.php:62 +#: Controller/UsersController.php:43 msgid "auth_loginerror" msgstr "Fehler beim Login!" -#: Controller/UsersController.php:112 +#: Controller/UsersController.php:92 msgid "register_email_subject" msgstr "Willkommen zu %s!" -#: Controller/UsersController.php:190 Controller/UsersController.php:212 -#: Controller/UsersController.php:248 +#: Controller/UsersController.php:172 Controller/UsersController.php:195 +#: Controller/UsersController.php:233 msgid "Invalid user" msgstr "Ungültiger Benutzer" -#: Controller/UsersController.php:239 +#: Controller/UsersController.php:224 msgid "User :name" msgstr "Benutzer :name" -#: Controller/UsersController.php:293 +#: Controller/UsersController.php:278 msgid "The user could not be saved. Please, try again." msgstr "Der Benutzer konnte nicht gespeichert werden." -#: Controller/UsersController.php:325 Controller/UsersController.php:362 +#: Controller/UsersController.php:311 Controller/UsersController.php:348 msgid "User not found." msgstr "Benutzer nicht gefunden." -#: Controller/UsersController.php:333 +#: Controller/UsersController.php:320 msgid "You can't lock yourself." msgstr "Man kann sich nicht selbst sperren." -#: Controller/UsersController.php:335 +#: Controller/UsersController.php:322 msgid "You can't lock administrators." msgstr "Administratoren können nicht gesperrt werden." -#: Controller/UsersController.php:345 +#: Controller/UsersController.php:331 #, php-format msgid "User %s is unlocked." msgstr "Benutzer %s wurde freigegeben." -#: Controller/UsersController.php:349 +#: Controller/UsersController.php:335 msgid "Error while un/locking." msgstr "" -#: Controller/UsersController.php:368 +#: Controller/UsersController.php:355 msgid "You can't delete yourself." msgstr "Man kann sich nicht selbst löschen." -#: Controller/UsersController.php:370 +#: Controller/UsersController.php:357 msgid "You can't delete the installation account." msgstr "Der Installationsbenutzer kann nicht gelöscht werden." -#: Controller/UsersController.php:372 +#: Controller/UsersController.php:359 #, php-format msgid "User %s deleted." msgstr "Nutzer %s gelöscht." -#: Controller/UsersController.php:375 +#: Controller/UsersController.php:363 msgid "Couldn't delete user." msgstr "" -#: Controller/UsersController.php:400 +#: Controller/UsersController.php:387 msgid "change_password_success" msgstr "Passwort erfolgreich geändert." -#: Controller/UsersController.php:463 +#: Controller/UsersController.php:455 msgid "error_email_not-valid" msgstr "Keine gültige Email-Adresse." -#: Controller/UsersController.php:510 +#: Controller/UsersController.php:502 msgid "Message was send." msgstr "Nachricht wurde versandt." -#: Controller/UsersController.php:513 +#: Controller/UsersController.php:507 #, fuzzy msgid "Message couldn't be send! " msgstr "Fehler, Nachricht konnte nicht versandt werden." -#: Controller/Component/CurrentUserComponent.php:258 +#: Controller/Component/CurrentUserComponent.php:308 msgid "auth_autherror" msgstr "Der Zugriff auf diese Funktion ist eingeschränkt!" -#: Controller/Component/EmailNotificationComponent.php:53 -#: Controller/Component/EmailNotificationComponent.php:85 +#: Controller/Component/EmailNotificationComponent.php:47 +#: Controller/Component/EmailNotificationComponent.php:79 #, php-format msgid "New reply to \"%s\"" msgstr "Neu Antwort zu \"%s\"" -#: Controller/Component/EmailNotificationComponent.php:114 +#: Controller/Component/EmailNotificationComponent.php:112 msgid "Successfull registration" msgstr "" @@ -1996,7 +1636,7 @@ msgstr "" msgid "Abend" msgstr "" -#: View/Helper/BbcodeHelper.php:566 +#: View/Helper/BbcodeHelper.php:580 #, fuzzy msgid "" "Your browser does not support HTML5 video. Please updgrade to a " @@ -2006,7 +1646,7 @@ msgstr "" "Ihr Browser unterstützt kein HTML5-Video. Bitte verwenden Sie einen modernen " "Browser." -#: View/Helper/BbcodeHelper.php:584 +#: View/Helper/BbcodeHelper.php:601 #, fuzzy msgid "" "Your browser does not support HTML5 audio. Please updgrade to a " @@ -2016,17 +1656,17 @@ msgstr "" "Ihr Browser unterstützt kein HTML5-Video. Bitte verwenden Sie einen modernen " "Browser." -#: View/Helper/BbcodeHelper.php:881 +#: View/Helper/BbcodeHelper.php:926 #, fuzzy msgid "[embed] tag not enabled." msgstr "Embed.ly aktivieren" -#: View/Helper/BbcodeHelper.php:971 +#: View/Helper/BbcodeHelper.php:1012 #, php-format msgid "Domain %s not allowed for embedding video." msgstr "Domain %s bei Videos nicht erlaubt." -#: View/Helper/BbcodeHelper.php:979 +#: View/Helper/BbcodeHelper.php:1020 msgid "Video domain is not allowed." msgstr "" @@ -2126,6 +1766,25 @@ msgstr "Hochladen" msgid "Media" msgstr "Einbinden" +#: View/Helper/SettingHelper.php:50 +msgid "edit" +msgstr "Bearbeiten" + +# View/Settings/admin_index.ctp: +#: View/Helper/SettingHelper.php:61 +msgid "Key" +msgstr "Schlüssel" + +# View/Settings/admin_index.ctp: +#: View/Helper/SettingHelper.php:62 +msgid "Value" +msgstr "Wert" + +# View/Settings/admin_index.ctp: +#: View/Helper/SettingHelper.php:63 +msgid "Explanation" +msgstr "Beschreibung" + #: View/Helper/TimeHHelper.php:100 View/Helper/TimeHHelper.php:119 msgid "yesterday" msgstr "Gestern" @@ -2214,6 +1873,270 @@ msgstr "" msgid "token test" msgstr "" +#~ msgid "forum_disabled" +#~ msgstr "Forum deaktivieren" + +#~ msgid "forum_disabled_exp" +#~ msgstr "Forum deaktivieren." + +#~ msgid "forum_disabled_text" +#~ msgstr "Forum deaktivieren Infotext" + +#~ msgid "forum_disabled_text_exp" +#~ msgstr "Infotext, welcher auf der Hinweisseite mit ausgegeben wird." + +#~ msgid "forum_name" +#~ msgstr "Forumtitel" + +#~ msgid "forum_name_exp" +#~ msgstr "Der Name dieses Forums." + +#~ msgid "timezone" +#~ msgstr "Zeitzone" + +#~ msgid "timezone_exp" +#~ msgstr "Zeitzone, indem sich das Forum befindet." + +#, fuzzy +#~ msgid "forum_email" +#~ msgstr "Email" + +#, fuzzy +#~ msgid "forum_email_exp" +#~ msgstr "" +#~ "Emailadresse, welche als Absenderadresse für vom Forum versendet Emails " +#~ "(bspw. bei der Registrierung) verwendet wird. " + +#~ msgid "email_admin_config_exp" +#~ msgstr "" +#~ "Erweiterte Email-Einstellungen sind durch das Anlegen einer $saito-Konfiguration in app/config/email.php möglich. Als " +#~ "Vorlage sollte app/config/email.php.default verwendet " +#~ "werden. Siehe auch." + +# +#~ msgid "block_user_ui" +#~ msgstr "Nutzer sperren" + +# +#~ msgid "block_user_ui_exp" +#~ msgstr "Moderatoren dürfen Benutzer sperren." + +#~ msgid "store_ip" +#~ msgstr "IP speichern" + +#~ msgid "store_ip_exp" +#~ msgstr "" +#~ "Beim Einträgen ins Forum werden die IP-Adresse des Eintragenden " +#~ "gespeichert. Moderatoren wird die Adresse im Eintrag angezeigt." + +#~ msgid "store_ip_anonymized" +#~ msgstr "IP anonymisieren" + +#~ msgid "store_ip_anonymized_exp" +#~ msgstr "Anonymisieren der gespeicherten IP-Adresse." + +#, fuzzy +#~ msgid "tos_enabled" +#~ msgstr "Nutzungsbedingungen aktivieren" + +#, fuzzy +#~ msgid "tos_enabled_exp" +#~ msgstr "" +#~ "Setzt einen Verweis und eine zu bestätigende Checkbox für die " +#~ "Nutzungsbedingungen auf die Registrierungsseite." + +#~ msgid "tos_url" +#~ msgstr "Nutzungsbedingungen-URL" + +#, fuzzy +#~ msgid "tos_url_exp" +#~ msgstr "" +#~ "URL zu den Nutzungsbedingen. Falls leer, werden die eingebauten " +#~ "Standardseiten verwendet." + +# +#~ msgid "edit_period" +#~ msgstr "Nachbearbeitungszeit" + +# +#~ msgid "edit_period_exp" +#~ msgstr "" +#~ "Minuten, die ein neuer Eintrag nach dem Anlegen nachbearbeitet werden " +#~ "kann." + +# +#~ msgid "edit_delay" +#~ msgstr "Nachbearbeiten anzeigen" + +# +#~ msgid "edit_delay_exp" +#~ msgstr "" +#~ "Minuten, die ein Eintrag nach dem Anlegen ohne Bearbeitungshinweis " +#~ "nachbearbeitet werden kann." + +#~ msgid "topics_per_page" +#~ msgstr "Threads pro Seite" + +#~ msgid "topics_per_page_exp" +#~ msgstr "Anzahl der Threads, die auf der Startseite angezeigt werden." + +#~ msgid "thread_depth_indent" +#~ msgstr "Einrücktiefe Threads" + +#~ msgid "thread_depth_indent_exp" +#~ msgstr "" +#~ "Ab dieser Tiefe werden werden Einträge in der Thread-Ansicht nicht weiter " +#~ "nach rechts eingerückt dargestellt." + +#~ msgid "autolink" +#~ msgstr "Autolink" + +#~ msgid "autolink_exp" +#~ msgstr "Verweise im Text automatisch erkennen und verlinken." + +#~ msgid "bbcode_img" +#~ msgstr "Bilder" + +#~ msgid "bbcode_img_exp" +#~ msgstr "Einbinden von Bildern erlauben." + +#~ msgid "quote_symbol" +#~ msgstr "Zitatzeichen" + +#~ msgid "quote_symbol_exp" +#~ msgstr "Zitatzeichen" + +#~ msgid "signature_separator" +#~ msgstr "Signaturtrenner" + +#~ msgid "signature_separator_exp" +#~ msgstr "Signaturtrenner" + +#~ msgid "subject_maxlength" +#~ msgstr "Betrefflänge" + +#~ msgid "subject_maxlength_exp" +#~ msgstr "Maximale Zeichenanzahl in Betreff." + +#~ msgid "text_word_maxlength" +#~ msgstr "Wordlänge" + +#~ msgid "text_word_maxlength_exp" +#~ msgstr "Maximale Zeichenanzahl pro Wort." + +#~ msgid "userranks_show" +#~ msgstr "Ränge anzeigen" + +#~ msgid "userranks_show_exp" +#~ msgstr "Benutzerränge anzeigen." + +#~ msgid "userranks_ranks" +#~ msgstr "Ränge" + +#~ msgid "userranks_ranks_exp" +#~ msgstr "Benutzerrang basierend auf Anzahl der gemachten Beiträge." + +# +#~ msgid "video_domains_allowed" +#~ msgstr "Einbettbare (Video-) Quellen" + +# +#~ msgid "video_domains_allowed_exp" +#~ msgstr "" +#~ "Domainnamen einbindbar für Video und iFrame. Ein Asterisk * bedeuted " +#~ "freies Einbinden (Vorsicht, dies kann ebenfalls zum Einbinden von " +#~ "Schadsoftware genutzt werden!)." + +#~ msgid "category_chooser_global" +#~ msgstr "Standard" + +#~ msgid "category_chooser_global_exp" +#~ msgstr "Kategorienfilter auf der Frontseite immer anzeigen." + +#~ msgid "category_chooser_user_override" +#~ msgstr "Benutzereinstellung" + +#~ msgid "category_chooser_user_override_exp" +#~ msgstr "" +#~ "Erlaubt einem Benutzern den Kategorienfilter in seinen Einstellungen zu " +#~ "aktivieren, selbst wenn der Filter standardmässig deaktiviert ist." + +#~ msgid "shoutbox_enabled" +#~ msgstr "Aktivieren" + +#~ msgid "shoutbox_enabled_exp" +#~ msgstr "Shoutbox aktivieren" + +#~ msgid "shoutbox_max_shouts" +#~ msgstr "Anzahl" + +#~ msgid "shoutbox_max_shouts_exp" +#~ msgstr "Anzahl der angezeigten Shoutboxeinträge" + +#~ msgid "upload_max_img_size" +#~ msgstr "Uploadgröße maximal" + +#~ msgid "upload_max_img_size_exp" +#~ msgstr "Maximale Größe, die ein Upload besitzen darf in kB." + +#~ msgid "upload_max_number_of_uploads" +#~ msgstr "Anzahl an Uploads pro Benutzer" + +#~ msgid "upload_max_number_of_uploads_exp" +#~ msgstr "" +#~ "Maximale Anzahl von Dateien, die ein einzelner Benutzer gleichzeitig auf " +#~ "dem Server hochgeladen haben kann." + +#~ msgid "flattr_enabled" +#~ msgstr "Flattr aktivieren" + +#~ msgid "flattr_enabled_exp" +#~ msgstr "Ermöglicht es Benutzern flattr zu verwenden." + +#~ msgid "flattr_language" +#~ msgstr "Flattr-Sprache" + +#~ msgid "flattr_language_exp" +#~ msgstr "" +#~ "Sprache, die flattr den hereinzeigenden Verweisen zuordnet. Siehe Doku " +#~ "auf flattr.com für Kürzel." + +#~ msgid "flattr_category" +#~ msgstr "Flattr-Kategorie" + +#~ msgid "flattr_category_exp" +#~ msgstr "" +#~ "Kategorie, die Flattr den hereinzeigenden Verweisen zuordnet. Siehe " +#~ "flattr.com für verfügbare Kategorien." + +#~ msgid "embedly_enabled" +#~ msgstr "Embed.ly aktivieren" + +#~ msgid "embedly_enabled_exp" +#~ msgstr "" +#~ "Aktiviert den [embed]-BBcode, welcher seinen Inhalt durch Embed.ly " +#~ "darstellt." + +#~ msgid "embedly_key" +#~ msgstr "Embed.ly Key" + +# +#~ msgid "embedly_key_exp" +#~ msgstr "API-Key, den man nach der Registrierung bei Embed.ly erhält." + +#~ msgid "stopwatch_get" +#~ msgstr "Stopuhr" + +#~ msgid "stopwatch_get_exp" +#~ msgstr "" +#~ "Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ " +#~ "an URL in Production-Modus für eingeloggte Nutzer." + +#~ msgid "logged_in_users_only" +#~ msgstr "Nur für eingeloggte Benutzer." + #~ msgid "user_edit_success" #~ msgstr "Die Daten wurden gespeichert." diff --git a/app/Locale/deu/LC_MESSAGES/nondynamic.po b/app/Locale/deu/LC_MESSAGES/nondynamic.po index f97ac07cb..eb3049a5f 100644 --- a/app/Locale/deu/LC_MESSAGES/nondynamic.po +++ b/app/Locale/deu/LC_MESSAGES/nondynamic.po @@ -91,3 +91,287 @@ msgstr "Hinweis" # msgid "success" msgstr "Erfolg" + +#******************************************************************************* +#* App Settings Start +#******************************************************************************/ + +# +msgid "forum_disabled" +msgstr "Forum deaktivieren" + +# +msgid "forum_disabled_exp" +msgstr "Forum deaktivieren." + +# +msgid "forum_disabled_text" +msgstr "Forum deaktivieren Infotext" + +# +msgid "forum_disabled_text_exp" +msgstr "Infotext, welcher auf der Hinweisseite mit ausgegeben wird." + +# +msgid "forum_name" +msgstr "Forumtitel" + +# +msgid "forum_name_exp" +msgstr "Der Name dieses Forums." + +# +msgid "timezone" +msgstr "Zeitzone" + +# +msgid "timezone_exp" +msgstr "Zeitzone, indem sich das Forum befindet." + +# +msgid "forum_email" +msgstr "Email" + +# +msgid "forum_email_exp" +msgstr "Emailadresse, welche als Absenderadresse für vom Forum versendet Emails (bspw. bei der Registrierung) verwendet wird. " + +# +msgid "block_user_ui" +msgstr "Nutzer sperren" + +# +msgid "block_user_ui_exp" +msgstr "Moderatoren dürfen Benutzer sperren." + +# +msgid "store_ip" +msgstr "IP speichern" + +# +msgid "store_ip_exp" +msgstr "Beim Einträgen ins Forum werden die IP-Adresse des Eintragenden gespeichert. Moderatoren wird die Adresse im Eintrag angezeigt." + +# +msgid "store_ip_anonymized" +msgstr "IP anonymisieren" + +# +msgid "store_ip_anonymized_exp" +msgstr "Anonymisieren der gespeicherten IP-Adresse." + +# +msgid "tos_enabled" +msgstr "Nutzungsbedingungen aktivieren" + +# +msgid "tos_enabled_exp" +msgstr "Setzt einen Verweis und eine zu bestätigende Checkbox für die Nutzungsbedingungen auf die Registrierungsseite." + +# +msgid "tos_url" +msgstr "Nutzungsbedingungen-URL" + +# +msgid "tos_url_exp" +msgstr "URL zu den Nutzungsbedingen. Falls leer, werden die eingebauten Standardseiten verwendet." + +# +msgid "edit_period" +msgstr "Nachbearbeitungszeit" + +# +msgid "edit_period_exp" +msgstr "Minuten, die ein neuer Eintrag nach dem Anlegen nachbearbeitet werden kann." + +# +msgid "edit_delay" +msgstr "Nachbearbeiten anzeigen" + +# +msgid "edit_delay_exp" +msgstr "Minuten, die ein Eintrag nach dem Anlegen ohne Bearbeitungshinweis nachbearbeitet werden kann." + +# +msgid "topics_per_page" +msgstr "Threads pro Seite" + +# +msgid "topics_per_page_exp" +msgstr "Anzahl der Threads, die auf der Startseite angezeigt werden." + +# +msgid "thread_depth_indent" +msgstr "Einrücktiefe Threads" + +# +msgid "thread_depth_indent_exp" +msgstr "Ab dieser Tiefe werden werden Einträge in der Thread-Ansicht nicht weiter nach rechts eingerückt dargestellt." + +# +msgid "autolink" +msgstr "Autolink" + +# +msgid "autolink_exp" +msgstr "Verweise im Text automatisch erkennen und verlinken." + +# +msgid "bbcode_img" +msgstr "Bilder" + +# +msgid "bbcode_img_exp" +msgstr "Einbinden von Bildern erlauben." + +# +msgid "quote_symbol" +msgstr "Zitatzeichen" + +# +msgid "quote_symbol_exp" +msgstr "Zitatzeichen" + +# +msgid "signature_separator" +msgstr "Signaturtrenner" + +# +msgid "signature_separator_exp" +msgstr "Signaturtrenner" + +# +msgid "subject_maxlength" +msgstr "Betrefflänge" + +# +msgid "subject_maxlength_exp" +msgstr "Maximale Zeichenanzahl in Betreff." + +# +msgid "text_word_maxlength" +msgstr "Wordlänge" + +# +msgid "text_word_maxlength_exp" +msgstr "Maximale Zeichenanzahl pro Wort." + +# +msgid "userranks_show" +msgstr "Ränge anzeigen" + +# +msgid "userranks_show_exp" +msgstr "Benutzerränge anzeigen." + +# +msgid "userranks_ranks" +msgstr "Ränge" + +# +msgid "userranks_ranks_exp" +msgstr "Benutzerrang basierend auf Anzahl der gemachten Beiträge." + +# +msgid "video_domains_allowed" +msgstr "Einbettbare (Video-) Quellen" + +# +msgid "video_domains_allowed_exp" +msgstr "Domainnamen einbindbar für Video und iFrame. Ein Asterisk * bedeuted freies Einbinden (Vorsicht, dies kann ebenfalls zum Einbinden von Schadsoftware genutzt werden!)." + +# +msgid "category_chooser_global" +msgstr "Standard" + +# +msgid "category_chooser_global_exp" +msgstr "Kategorienfilter auf der Frontseite immer anzeigen." + +# +msgid "category_chooser_user_override" +msgstr "Benutzereinstellung" + +# +msgid "category_chooser_user_override_exp" +msgstr "Erlaubt einem Benutzern den Kategorienfilter in seinen Einstellungen zu aktivieren, selbst wenn der Filter standardmässig deaktiviert ist." + +# +msgid "shoutbox_enabled" +msgstr "Aktivieren" + +# +msgid "shoutbox_enabled_exp" +msgstr "Shoutbox aktivieren" + +# +msgid "shoutbox_max_shouts" +msgstr "Anzahl" + +# +msgid "shoutbox_max_shouts_exp" +msgstr "Anzahl der angezeigten Shoutboxeinträge" + +# +msgid "upload_max_img_size" +msgstr "Uploadgröße maximal" + +# +msgid "upload_max_img_size_exp" +msgstr "Maximale Größe, die ein Upload besitzen darf in kB." + +# +msgid "upload_max_number_of_uploads" +msgstr "Anzahl an Uploads pro Benutzer" + +# +msgid "upload_max_number_of_uploads_exp" +msgstr "Maximale Anzahl von Dateien, die ein einzelner Benutzer gleichzeitig auf dem Server hochgeladen haben kann." + +# +msgid "flattr_enabled" +msgstr "Flattr aktivieren" + +# +msgid "flattr_enabled_exp" +msgstr "Ermöglicht es Benutzern flattr zu verwenden." + +# +msgid "flattr_language" +msgstr "Flattr-Sprache" + +# +msgid "flattr_language_exp" +msgstr "Sprache, die flattr den hereinzeigenden Verweisen zuordnet. Siehe Doku auf flattr.com für Kürzel." + +# +msgid "flattr_category" +msgstr "Flattr-Kategorie" + +# +msgid "flattr_category_exp" +msgstr "Kategorie, die Flattr den hereinzeigenden Verweisen zuordnet. Siehe flattr.com für verfügbare Kategorien." + +# +msgid "embedly_enabled" +msgstr "Embed.ly aktivieren" + +# +msgid "embedly_enabled_exp" +msgstr "Aktiviert den [embed]-BBcode, welcher seinen Inhalt durch Embed.ly darstellt." + +# +msgid "embedly_key" +msgstr "Embed.ly Key" + +# +msgid "embedly_key_exp" +msgstr "API-Key, den man nach der Registrierung bei Embed.ly erhält." + +# +msgid "stopwatch_get" +msgstr "Stopuhr" + +# +msgid "stopwatch_get_exp" +msgstr "Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ an URL in Production-Modus für eingeloggte Nutzer." diff --git a/app/Locale/eng/LC_MESSAGES/default.po b/app/Locale/eng/LC_MESSAGES/default.po index 208886d73..f13f3629c 100644 --- a/app/Locale/eng/LC_MESSAGES/default.po +++ b/app/Locale/eng/LC_MESSAGES/default.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-05 13:35+0100\n" -"PO-Revision-Date: 2013-07-29 14:19+0100\n" +"POT-Creation-Date: 2013-07-29 14:47+0100\n" +"PO-Revision-Date: 2013-07-29 14:48+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: en_US\n" @@ -64,12 +64,12 @@ msgstr "" #: Plugin/Install/View/Install/index.ctp:24 #, php-format -msgid "PHP version %s > 5.3" +msgid "PHP version %s > 5.4" msgstr "" #: Plugin/Install/View/Install/index.ctp:27 #, php-format -msgid "PHP version %s < 5.3" +msgid "PHP version %s < 5.4" msgstr "" #: Plugin/Install/View/Install/index.ctp:33 @@ -85,7 +85,7 @@ msgstr "" msgid "Empty Caches" msgstr "" -#: View/Admins/admin_stats.ctp:8 View/Layouts/admin.ctp:58 +#: View/Admins/admin_stats.ctp:8 View/Layouts/admin.ctp:53 msgid "Stats" msgstr "" @@ -98,8 +98,8 @@ msgstr "" msgid "User Registrations" msgstr "Registration" -#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:323 -#: Controller/EntriesController.php:375 +#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:298 +#: Controller/EntriesController.php:351 msgid "back_to_overview_linkname" msgstr "Back to Overview" @@ -124,6 +124,7 @@ msgid "Bookmarks" msgstr "" #: View/Bookmarks/index.ctp:24 View/Categories/view.ctp:64 +#: View/Entries/add.ctp:89 msgid "Subject" msgstr "" @@ -134,7 +135,7 @@ msgstr "" #: View/Bookmarks/index.ctp:49 View/Categories/admin_index.ctp:39 #: View/Categories/edit.ctp:20 View/Categories/index.ctp:31 #: View/Categories/view.ctp:108 View/SmileyCodes/admin_index.ctp:30 -#: View/Smilies/admin_index.ctp:32 View/Users/view.ctp:192 +#: View/Smilies/admin_index.ctp:32 View/Users/view.ctp:200 msgid "Delete" msgstr "" @@ -150,16 +151,16 @@ msgstr "" #: View/Categories/add.ctp:13 View/Categories/admin_edit.ctp:26 #: View/Categories/edit.ctp:14 View/Settings/admin_edit.ctp:20 #: View/SmileyCodes/admin_edit.ctp:16 View/Smilies/admin_add.ctp:12 -#: View/Smilies/admin_edit.ctp:13 View/Users/contact.ctp:45 +#: View/Smilies/admin_edit.ctp:13 View/Users/contact.ctp:51 msgid "Submit" msgstr "" #: View/Categories/add.ctp:16 View/Categories/admin_index.ctp:16 #: View/Categories/edit.ctp:17 View/Categories/index.ctp:11 #: View/Categories/index.ctp:51 View/Categories/view.ctp:37 -#: View/Categories/view.ctp:73 View/Settings/admin_index.ctp:7 -#: View/SmileyCodes/admin_add.ctp:14 View/SmileyCodes/admin_index.ctp:12 -#: View/Smilies/admin_add.ctp:18 View/Smilies/admin_index.ctp:14 +#: View/Categories/view.ctp:73 View/SmileyCodes/admin_add.ctp:14 +#: View/SmileyCodes/admin_index.ctp:12 View/Smilies/admin_add.ctp:18 +#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:80 msgid "Actions" msgstr "" @@ -182,7 +183,7 @@ msgstr "" #: View/Categories/admin_add.ctp:1 View/Categories/admin_delete.ctp:1 #: View/Categories/admin_edit.ctp:1 View/Categories/admin_index.ctp:1 #: View/Categories/admin_index.ctp:3 View/Categories/index.ctp:2 -#: View/Layouts/admin.ctp:52 webroot/js/main-prod.js:79 +#: View/Layouts/admin.ctp:47 webroot/js/main-prod.js:100 #: webroot/js/views/categoryChooser.js:18 msgid "Categories" msgstr "" @@ -221,7 +222,7 @@ msgstr "" msgid "Make It So" msgstr "" -#: View/Categories/admin_delete.ctp:76 View/Users/admin_delete.ctp:54 +#: View/Categories/admin_delete.ctp:77 View/Users/admin_delete.ctp:55 msgid "Abort" msgstr "" @@ -236,9 +237,9 @@ msgid "New Category" msgstr "" #: View/Categories/admin_index.ctp:34 View/Categories/index.ctp:30 -#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:230 +#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:37 #: View/SmileyCodes/admin_index.ctp:29 View/Smilies/admin_index.ctp:31 -#: View/Users/view.ctp:182 +#: View/Users/view.ctp:190 msgid "Edit" msgstr "" @@ -250,7 +251,7 @@ msgid "Are you sure you want to delete # %s?" msgstr "" #: View/Categories/index.ctp:29 View/Categories/view.ctp:106 -#: View/Settings/admin_index.ctp:254 +#: View/Settings/admin_index.ctp:43 msgid "View" msgstr "" @@ -369,23 +370,23 @@ msgstr "" msgid "email_footer" msgstr "This email was send by %s (%s)." -#: View/Elements/entry/category-chooser.ctp:12 +#: View/Elements/entry/category-chooser.ctp:18 #, fuzzy msgid "category_chooser_context_exp" msgstr "" "Choose your custom categories.

Click on a name to quickly show this " "category without changing your preferences." -#: View/Elements/entry/category-chooser.ctp:36 View/Entries/search.ctp:82 -#: Controller/EntriesController.php:881 +#: View/Elements/entry/category-chooser.ctp:42 View/Entries/search.ctp:82 +#: Controller/EntriesController.php:927 msgid "All Categories" msgstr "" -#: View/Elements/entry/category-chooser.ctp:83 +#: View/Elements/entry/category-chooser.ctp:89 msgid "Apply" msgstr "" -#: View/Elements/entry/thread_cached_init.ctp:8 View/Entries/view.ctp:31 +#: View/Elements/entry/thread_cached_init.ctp:8 msgid "btn-showThreadInMixView" msgstr "mix" @@ -414,36 +415,36 @@ msgstr "" msgid "views_headline" msgstr "views" -#: View/Elements/entry/view_posting.ctp:59 +#: View/Elements/entry/view_posting.ctp:57 #: View/Elements/layout/header_login.ctp:38 -#: View/Elements/layout/header_login.ctp:43 View/Entries/index.ctp:19 -#: View/Entries/search.ctp:22 View/Users/view.ctp:155 +#: View/Elements/layout/header_login.ctp:43 View/Entries/index.ctp:21 +#: View/Entries/search.ctp:22 View/Users/view.ctp:163 msgid "Help" msgstr "" -#: View/Elements/entry/view_posting.ctp:60 +#: View/Elements/entry/view_posting.ctp:58 msgid "answering_forbidden_locked_shp" msgstr "Answering forbidden by moderator." -#: View/Elements/entry/view_posting.ctp:65 +#: View/Elements/entry/view_posting.ctp:63 msgid "forum_answer_linkname" msgstr "Answer" -#: View/Elements/entry/view_posting.ctp:79 -#: View/Elements/entry/view_posting.ctp:107 -#: Controller/EntriesController.php:481 +#: View/Elements/entry/view_posting.ctp:76 +#: View/Elements/entry/view_posting.ctp:104 +#: Controller/EntriesController.php:457 msgid "edit_linkname" msgstr "Edit" -#: View/Elements/entry/view_posting.ctp:147 +#: View/Elements/entry/view_posting.ctp:144 msgid "merge_tree_link" msgstr "Merge" -#: View/Elements/entry/view_posting.ctp:158 +#: View/Elements/entry/view_posting.ctp:155 msgid "delete_tree_link" msgstr "Delete" -#: View/Elements/entry/view_posting.ctp:161 +#: View/Elements/entry/view_posting.ctp:158 msgid "delete_tree_link_confirm_message" msgstr "" "Deleting a (sub)thread can't be undone! Are you sure you want to delete the " @@ -456,7 +457,7 @@ msgstr "" #: View/Elements/layout/disclaimer.ctp:7 #: View/Themed/Macnemo/Elements/layout/disclaimer.ctp:10 -#: View/Users/view.ctp:46 +#: View/Users/view.ctp:45 msgid "Contact" msgstr "" @@ -508,7 +509,7 @@ msgid "Forum Settings" msgstr "" #: View/Elements/layout/header_login.ctp:22 View/Users/edit.ctp:133 -#: View/Users/view.ctp:88 View/Users/view.ctp:124 +#: View/Users/view.ctp:93 View/Users/view.ctp:132 msgid "user_profile" msgstr "User Profile" @@ -520,34 +521,34 @@ msgstr "Logout" msgid "No help for this page available." msgstr "" -#: View/Elements/layout/header_search.ctp:16 View/Entries/search.ctp:26 +#: View/Elements/layout/header_search.ctp:28 View/Entries/search.ctp:26 #: View/Entries/search.ctp:105 msgid "search_submit" msgstr "Search" -#: View/Elements/layout/slidetab_recententries.ctp:11 +#: View/Elements/layout/slidetab_recententries.ctp:9 msgid "Recent entries" msgstr "" -#: View/Elements/layout/slidetab_recentposts.ctp:13 View/Users/view.ctp:219 +#: View/Elements/layout/slidetab_recentposts.ctp:13 View/Users/view.ctp:229 msgid "user_recentposts" msgstr "recent posts" -#: View/Elements/layout/slidetab_userlist.ctp:18 +#: View/Elements/layout/slidetab_userlist.ctp:17 msgid "user_area_linkname" msgstr "User" -#: View/Elements/layout/slidetab_userlist.ctp:16 +#: View/Elements/layout/slidetab_userlist.ctp:15 #, php-format msgid "%s online (%s)" msgstr "" -#: View/Elements/layout/slidetab_userlist.ctp:32 +#: View/Elements/layout/slidetab_userlist.ctp:31 #: View/Helper/UserHHelper.php:116 msgid "ud_admin" msgstr "Administrator" -#: View/Elements/layout/slidetab_userlist.ctp:33 +#: View/Elements/layout/slidetab_userlist.ctp:32 #: View/Helper/UserHHelper.php:114 msgid "ud_mod" msgstr "Moderator" @@ -611,43 +612,43 @@ msgstr "" "To activate your account please click the following link: %s within the next " "24 hours." -#: View/Entries/add.ctp:31 View/Entries/add.ctp:220 +#: View/Entries/add.ctp:34 View/Entries/add.ctp:187 msgid "preview" msgstr "Preview" -#: View/Entries/add.ctp:79 Controller/UsersController.php:444 +#: View/Entries/add.ctp:87 Controller/UsersController.php:477 msgid "error_subject_empty" msgstr "Subject must not be empty." -#: View/Entries/add.ctp:116 +#: View/Entries/add.ctp:130 msgid "Cite" msgstr "" -#: View/Entries/add.ctp:138 +#: View/Entries/add.ctp:155 View/Entries/add.ctp:175 View/Entries/add.ctp:289 +msgid "submit_button" +msgstr "Submit" + +#: View/Entries/add.ctp:202 msgid "Notify on reply" msgstr "" -#: View/Entries/add.ctp:146 +#: View/Entries/add.ctp:216 msgid "Notify on thread replies" msgstr "" -#: View/Entries/add.ctp:153 View/Helper/EntryHHelper.php:196 +#: View/Entries/add.ctp:225 View/Helper/EntryHHelper.php:159 msgid "entry_nsfw_title" msgstr "NSFW" -#: View/Entries/add.ctp:161 +#: View/Entries/add.ctp:238 msgid "entry_flattr_this_posting" msgstr "flattr-Button" -#: View/Entries/add.ctp:200 View/Entries/add.ctp:209 View/Entries/add.ctp:239 -msgid "submit_button" -msgstr "Submit" - -#: View/Entries/index.ctp:5 +#: View/Entries/index.ctp:6 msgid "new_entry_linkname" msgstr "New Entry" -#: View/Entries/index.ctp:20 +#: View/Entries/index.ctp:22 msgid "btn_manualy_mark_as_read_shp" msgstr "Marks all new entries as read." @@ -701,33 +702,33 @@ msgstr "Since" msgid "search_simple" msgstr "Simple Search" -#: View/Entries/search.ctp:163 +#: View/Entries/search.ctp:167 msgid "search_nothing_found" msgstr "Nothing found." -#: View/Layouts/admin.ctp:41 +#: View/Layouts/admin.ctp:36 msgid "Overview" msgstr "" -#: View/Layouts/admin.ctp:46 View/Settings/admin_edit.ctp:2 -#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:11 -#: View/Users/edit.ctp:164 +#: View/Layouts/admin.ctp:41 View/Settings/admin_edit.ctp:2 +#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:114 +#: View/Users/edit.ctp:163 msgid "Settings" msgstr "" -#: View/Layouts/admin.ctp:49 View/Users/admin_add.ctp:2 +#: View/Layouts/admin.ctp:44 View/Users/admin_add.ctp:2 #: View/Users/admin_index.ctp:1 View/Users/admin_index.ctp:3 msgid "Users" msgstr "" -#: View/Layouts/admin.ctp:55 View/SmileyCodes/admin_edit.ctp:1 +#: View/Layouts/admin.ctp:50 View/SmileyCodes/admin_edit.ctp:1 #: View/SmileyCodes/admin_index.ctp:1 View/Smilies/admin_add.ctp:2 #: View/Smilies/admin_edit.ctp:1 View/Smilies/admin_index.ctp:1 #: View/Smilies/admin_index.ctp:3 msgid "Smilies" msgstr "" -#: View/Layouts/admin.ctp:65 +#: View/Layouts/admin.ctp:60 msgid "Forum" msgstr "" @@ -739,390 +740,56 @@ msgstr "" msgid "RSS Feed" msgstr "" -#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:111 +#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:110 msgid "Last entries" msgstr "" -#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:107 +#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:106 msgid "Last started threads" msgstr "" #: View/Settings/admin_edit.ctp:27 View/Settings/admin_timezone.ctp:24 +#: View/Helper/SettingHelper.php:48 View/Helper/SettingHelper.php:64 msgid "_exp" msgstr "" -#: View/Settings/admin_index.ctp:4 -msgid "Key" -msgstr "" - -#: View/Settings/admin_index.ctp:5 -msgid "Value" -msgstr "" - -#: View/Settings/admin_index.ctp:6 -msgid "Explanation" -msgstr "" - -#: View/Settings/admin_index.ctp:12 +#: View/Settings/admin_index.ctp:7 msgid "Deactivate Forum" msgstr "" -#: View/Settings/admin_index.ctp:17 -msgid "forum_disabled" -msgstr "Disable Forum" - -#: View/Settings/admin_index.ctp:23 -msgid "forum_disabled_exp" -msgstr "Shows the maintenance page except for logged in admins." - -#: View/Settings/admin_index.ctp:27 View/Settings/admin_index.ctp:46 -#: View/Settings/admin_index.ctp:70 View/Settings/admin_index.ctp:89 -#: View/Settings/admin_index.ctp:113 View/Settings/admin_index.ctp:140 -#: View/Settings/admin_index.ctp:159 View/Settings/admin_index.ctp:178 -#: View/Settings/admin_index.ctp:202 View/Settings/admin_index.ctp:221 -#: View/Settings/admin_index.ctp:245 View/Settings/admin_index.ctp:269 -#: View/Settings/admin_index.ctp:288 View/Settings/admin_index.ctp:307 -#: View/Settings/admin_index.ctp:326 View/Settings/admin_index.ctp:345 -#: View/Settings/admin_index.ctp:364 View/Settings/admin_index.ctp:383 -#: View/Settings/admin_index.ctp:402 View/Settings/admin_index.ctp:421 -#: View/Settings/admin_index.ctp:440 View/Settings/admin_index.ctp:459 -#: View/Settings/admin_index.ctp:478 View/Settings/admin_index.ctp:502 -#: View/Settings/admin_index.ctp:521 View/Settings/admin_index.ctp:545 -#: View/Settings/admin_index.ctp:564 View/Settings/admin_index.ctp:588 -#: View/Settings/admin_index.ctp:607 View/Settings/admin_index.ctp:630 -#: View/Settings/admin_index.ctp:649 View/Settings/admin_index.ctp:668 -#: View/Settings/admin_index.ctp:687 View/Settings/admin_index.ctp:697 -#: View/Settings/admin_index.ctp:716 -msgid "edit" -msgstr "" - -#: View/Settings/admin_index.ctp:36 -msgid "forum_disabled_text" -msgstr "Maintenance Notice" - -#: View/Settings/admin_index.ctp:42 -msgid "forum_disabled_text_exp" -msgstr "Text notice shown on the forum disabled page." - -#: View/Settings/admin_index.ctp:55 +#: View/Settings/admin_index.ctp:13 msgid "Base Preferences" msgstr "" -#: View/Settings/admin_index.ctp:60 -msgid "forum_name" -msgstr "Forum Title" - -#: View/Settings/admin_index.ctp:66 -msgid "forum_name_exp" -msgstr "The name of this forum." - -#: View/Settings/admin_index.ctp:79 -msgid "timezone" -msgstr "Timezone" - -#: View/Settings/admin_index.ctp:85 -msgid "timezone_exp" -msgstr "All time values displayed are offset to match this timezone." - -#: View/Settings/admin_index.ctp:98 +#: View/Settings/admin_index.ctp:19 msgid "Email" msgstr "" -#: View/Settings/admin_index.ctp:103 -msgid "forum_email" -msgstr "Email" - -#: View/Settings/admin_index.ctp:109 -msgid "forum_email_exp" -msgstr "" -"Email address used as sender address for emails send by the forum (e.g. when " -"registering)." - -#: View/Settings/admin_index.ctp:122 -msgid "email_admin_config_exp" -msgstr "" -"Additional settings are possible by providing a $saito config " -"in app/config/email.php. You should use app/config/email." -"php.default as template. See also." - -#: View/Settings/admin_index.ctp:125 +#: View/Settings/admin_index.ctp:25 msgid "Moderation" msgstr "Moderation" -#: View/Settings/admin_index.ctp:130 -msgid "block_user_ui" -msgstr "Lock Users" - -# -#: View/Settings/admin_index.ctp:136 -msgid "block_user_ui_exp" -msgstr "Moderators are allowed to block users." - -#: View/Settings/admin_index.ctp:149 -msgid "store_ip" -msgstr "Store IP" - -#: View/Settings/admin_index.ctp:155 -msgid "store_ip_exp" -msgstr "Stores the IP-address for new entries. The IP is shown do moderators." - -#: View/Settings/admin_index.ctp:168 -msgid "store_ip_anonymized" -msgstr "IP Anonymized" - -#: View/Settings/admin_index.ctp:174 -msgid "store_ip_anonymized_exp" -msgstr "Strore IP anonymized." - -#: View/Settings/admin_index.ctp:187 +#: View/Settings/admin_index.ctp:31 msgid "Registration" msgstr "Registration" -#: View/Settings/admin_index.ctp:192 -msgid "tos_enabled" -msgstr "ToS Enabled" - -#: View/Settings/admin_index.ctp:198 -msgid "tos_enabled_exp" -msgstr "" -"Puts a Term of Service checkbox and link on the user registration page." - -#: View/Settings/admin_index.ctp:211 -msgid "tos_url" -msgstr "ToS Url" - -#: View/Settings/admin_index.ctp:217 -msgid "tos_url_exp" -msgstr "" -"Sets the url for the ToS link. If empty, use the build-in default pages." - -# -#: View/Settings/admin_index.ctp:235 -msgid "edit_period" -msgstr "Edit Period" - -# -#: View/Settings/admin_index.ctp:241 -msgid "edit_period_exp" -msgstr "Minutes a user can edit a new entry." - -# -#: View/Settings/admin_index.ctp:259 -msgid "edit_delay" -msgstr "Show Edit" - -# -#: View/Settings/admin_index.ctp:265 -msgid "edit_delay_exp" -msgstr "Minutes a new entry can be edited without being marked as edited." - -#: View/Settings/admin_index.ctp:278 -msgid "topics_per_page" -msgstr "Topics per Page" - -#: View/Settings/admin_index.ctp:284 -msgid "topics_per_page_exp" -msgstr "Number of topics shown on the frontpage." - -#: View/Settings/admin_index.ctp:297 -msgid "thread_depth_indent" -msgstr "Thread Indent Depth" - -#: View/Settings/admin_index.ctp:303 -msgid "thread_depth_indent_exp" -msgstr "" -"Max. indent to the the right for threads in thread-view. After that threads " -"appear on the same level." - -#: View/Settings/admin_index.ctp:316 -msgid "autolink" -msgstr "Autolink" - -#: View/Settings/admin_index.ctp:322 -msgid "autolink_exp" -msgstr "Detect and automatically generate links." - -#: View/Settings/admin_index.ctp:335 -msgid "bbcode_img" -msgstr "Images" - -#: View/Settings/admin_index.ctp:341 -msgid "bbcode_img_exp" -msgstr "Allow embedding of images." - -#: View/Settings/admin_index.ctp:354 -msgid "quote_symbol" -msgstr "Quote Symbol" - -#: View/Settings/admin_index.ctp:360 -msgid "quote_symbol_exp" -msgstr "Quote symbol." - -#: View/Settings/admin_index.ctp:373 -msgid "signature_separator" -msgstr "Signature Separator" - -#: View/Settings/admin_index.ctp:379 -msgid "signature_separator_exp" -msgstr "Signature separator." - -#: View/Settings/admin_index.ctp:392 -msgid "subject_maxlength" -msgstr "Subject Length" - -#: View/Settings/admin_index.ctp:398 -msgid "subject_maxlength_exp" -msgstr "Number of chars allowed in subject." - -#: View/Settings/admin_index.ctp:411 -msgid "text_word_maxlength" -msgstr "Word Length" - -#: View/Settings/admin_index.ctp:417 -msgid "text_word_maxlength_exp" -msgstr "Max numbers of chars allowed in one word." - -#: View/Settings/admin_index.ctp:430 -msgid "userranks_show" -msgstr "Enable Ranks" - -#: View/Settings/admin_index.ctp:436 -msgid "userranks_show_exp" -msgstr "Show user ranks." - -#: View/Settings/admin_index.ctp:449 -msgid "userranks_ranks" -msgstr "Ranks" - -#: View/Settings/admin_index.ctp:455 -msgid "userranks_ranks_exp" -msgstr "User ranks based on number of postings." - -# -#: View/Settings/admin_index.ctp:468 -msgid "video_domains_allowed" -msgstr "Embeddable Video Sources" - -# -#: View/Settings/admin_index.ctp:474 -msgid "video_domains_allowed_exp" -msgstr "" -"Domain names embeddable for video/iframe. Asterisk * means all sources " -"allowed (This could pose a security risk if malicious code is embedded!)." - -#: View/Settings/admin_index.ctp:487 +#: View/Settings/admin_index.ctp:61 msgid "Category Chooser" msgstr "" -#: View/Settings/admin_index.ctp:492 -msgid "category_chooser_global" -msgstr "Default" - -#: View/Settings/admin_index.ctp:498 -msgid "category_chooser_global_exp" -msgstr "Always show the category chooser on the front-page." - -#: View/Settings/admin_index.ctp:511 -msgid "category_chooser_user_override" -msgstr "User Override" - -#: View/Settings/admin_index.ctp:517 -msgid "category_chooser_user_override_exp" -msgstr "" -"If the category chooser is off be default this allows users to enable it in " -"their user preferences." - -#: View/Settings/admin_index.ctp:535 -msgid "shoutbox_enabled" -msgstr "Enable" - -#: View/Settings/admin_index.ctp:541 -msgid "shoutbox_enabled_exp" -msgstr "Shoutbox is enabled." - -#: View/Settings/admin_index.ctp:554 -msgid "shoutbox_max_shouts" -msgstr "Number" - -#: View/Settings/admin_index.ctp:560 -msgid "shoutbox_max_shouts_exp" -msgstr "Number of entries shown in the Shoutbox." - -#: View/Settings/admin_index.ctp:578 -msgid "upload_max_img_size" -msgstr "Upload Max Image Size " - -#: View/Settings/admin_index.ctp:584 -msgid "upload_max_img_size_exp" -msgstr "Max image size in kB for uploaded content." - -#: View/Settings/admin_index.ctp:597 -msgid "upload_max_number_of_uploads" -msgstr "Max Number of Uploads" - -#: View/Settings/admin_index.ctp:603 -msgid "upload_max_number_of_uploads_exp" -msgstr "Max number of uploaded files for a single user." - -#: View/Settings/admin_index.ctp:620 -msgid "flattr_enabled" -msgstr "Flattr Enabled" - -#: View/Settings/admin_index.ctp:626 -msgid "flattr_enabled_exp" -msgstr "" -"Allows users to add a flattr button to their profil and their postings." - -#: View/Settings/admin_index.ctp:639 -msgid "flattr_language" -msgstr "Flattr Language" - -#: View/Settings/admin_index.ctp:645 -msgid "flattr_language_exp" +#: View/Settings/admin_index.ctp:67 +msgid "Shoutbox" msgstr "" -"Flattr expects an language key for the content send. See flattr " -"documentation for available language keys." - -#: View/Settings/admin_index.ctp:658 -msgid "flattr_category" -msgstr "Flattr Category" - -#: View/Settings/admin_index.ctp:664 -msgid "flattr_category_exp" -msgstr "See flattr documentation for available categories." - -#: View/Settings/admin_index.ctp:683 -msgid "embedly_enabled" -msgstr "Enable Embedly" -#: View/Settings/admin_index.ctp:685 -msgid "embedly_enabled_exp" -msgstr "Enables the [embed] BBcode which parses content through Embed.ly" - -#: View/Settings/admin_index.ctp:693 -msgid "embedly_key" -msgstr "Embedly Key" - -#: View/Settings/admin_index.ctp:695 -msgid "embedly_key_exp" -msgstr "" -"You have to register at Embed.ly to receive an API-key, which you enter here." +#: View/Settings/admin_index.ctp:73 +#, fuzzy +msgid "Uploads" +msgstr "Upload" -#: View/Settings/admin_index.ctp:706 +#: View/Settings/admin_index.ctp:93 msgid "Debug" msgstr "" -#: View/Settings/admin_index.ctp:712 -msgid "stopwatch_get" -msgstr "Stopwatch" - -#: View/Settings/admin_index.ctp:714 -msgid "stopwatch_get_exp" -msgstr "" -"Outputs stopwatch times in production mode for logged in users by appending " -"/stopwatch:true/ to url." - #: View/SmileyCodes/admin_add.ctp:4 msgid "Add Smiley Code" msgstr "" @@ -1171,7 +838,7 @@ msgid "" "geheuert, :loggedin an Deck, :anon Blinde Passagiere" msgstr "" -#: View/Uploads/add.ctp:10 webroot/js/templates/uploadNew.html:23 +#: View/Uploads/add.ctp:10 webroot/js/templates/uploadNew.html:22 msgid "upload_btn" msgstr "Upload" @@ -1202,7 +869,7 @@ msgid "New User" msgstr "" #: View/Users/admin_index.ctp:10 View/Users/edit.ctp:32 View/Users/edit.ctp:80 -#: View/Users/index.ctp:14 View/Users/view.ctp:26 +#: View/Users/index.ctp:14 View/Users/view.ctp:25 msgid "username_marking" msgstr "Name" @@ -1257,15 +924,15 @@ msgstr "Contact %s" msgid "user_contact_sender-contact" msgstr "Your Contact Address" -#: View/Users/contact.ctp:18 +#: View/Users/contact.ctp:21 msgid "user_contact_subject" msgstr "Subject" -#: View/Users/contact.ctp:23 +#: View/Users/contact.ctp:29 msgid "user_contact_message" msgstr "Message" -#: View/Users/contact.ctp:37 +#: View/Users/contact.ctp:43 msgid "user_contact_send_carbon_copy" msgstr "Send copy to yourself." @@ -1278,7 +945,7 @@ msgstr "" msgid "Profil" msgstr "" -#: View/Users/edit.ctp:37 View/Users/edit.ctp:85 View/Users/view.ctp:53 +#: View/Users/edit.ctp:37 View/Users/edit.ctp:85 View/Users/view.ctp:52 msgid "userlist_email" msgstr "Email" @@ -1303,7 +970,7 @@ msgstr "" msgid "user_show_email_exp" msgstr "" -#: View/Users/edit.ctp:114 View/Users/view.ctp:39 +#: View/Users/edit.ctp:114 View/Users/view.ctp:38 msgid "user_real_name" msgstr "Real Name" @@ -1311,7 +978,7 @@ msgstr "Real Name" msgid "user_real_name_exp" msgstr "" -#: View/Users/edit.ctp:121 View/Users/view.ctp:60 +#: View/Users/edit.ctp:121 View/Users/view.ctp:59 msgid "user_hp" msgstr "Homepage" @@ -1319,7 +986,7 @@ msgstr "Homepage" msgid "user_hp_exp" msgstr "" -#: View/Users/edit.ctp:128 View/Users/view.ctp:67 +#: View/Users/edit.ctp:128 View/Users/view.ctp:66 msgid "user_place" msgstr "Place" @@ -1331,7 +998,7 @@ msgstr "" msgid "user_profile_exp" msgstr "" -#: View/Users/edit.ctp:144 View/Users/view.ctp:96 +#: View/Users/edit.ctp:144 View/Users/view.ctp:101 msgid "user_signature" msgstr "Signature" @@ -1339,162 +1006,162 @@ msgstr "Signature" msgid "user_signature_exp" msgstr "" -#: View/Users/edit.ctp:174 +#: View/Users/edit.ctp:173 msgid "user_sort_last_answer" msgstr "" -#: View/Users/edit.ctp:180 +#: View/Users/edit.ctp:179 msgid "user_sort_last_answer_time" msgstr "" -#: View/Users/edit.ctp:181 +#: View/Users/edit.ctp:180 msgid "user_sort_last_answer_last_answer" msgstr "" -#: View/Users/edit.ctp:189 +#: View/Users/edit.ctp:188 msgid "user_sort_last_answer_exp" msgstr "" -#: View/Users/edit.ctp:194 +#: View/Users/edit.ctp:193 msgid "user_automaticaly_mark_as_read" msgstr "" -#: View/Users/edit.ctp:195 +#: View/Users/edit.ctp:194 msgid "user_automaticaly_mark_as_read_exp" msgstr "" -#: View/Users/edit.ctp:199 +#: View/Users/edit.ctp:198 msgid "user_signatures_hide" msgstr "" -#: View/Users/edit.ctp:201 +#: View/Users/edit.ctp:200 msgid "user_signatures_hide_exp" msgstr "" -#: View/Users/edit.ctp:203 +#: View/Users/edit.ctp:202 msgid "user_signatures_images_hide_exp" msgstr "" -#: View/Users/edit.ctp:208 +#: View/Users/edit.ctp:207 msgid "user_forum_refresh_time" msgstr "" -#: View/Users/edit.ctp:221 +#: View/Users/edit.ctp:220 msgid "user_forum_refresh_time_exp" msgstr "" -#: View/Users/edit.ctp:227 +#: View/Users/edit.ctp:226 msgid "user_colors" msgstr "" -#: View/Users/edit.ctp:229 +#: View/Users/edit.ctp:228 msgid "user_color_new_postings_exp" msgstr "" -#: View/Users/edit.ctp:231 +#: View/Users/edit.ctp:230 msgid "user_color_old_postinings_exp" msgstr "" -#: View/Users/edit.ctp:233 +#: View/Users/edit.ctp:232 msgid "user_color_actual_posting_exp" msgstr "" -#: View/Users/edit.ctp:255 +#: View/Users/edit.ctp:254 msgid "inline_view_on_click" msgstr "" -#: View/Users/edit.ctp:258 +#: View/Users/edit.ctp:257 msgid "inline_view_on_click_exp" msgstr "" -#: View/Users/edit.ctp:262 +#: View/Users/edit.ctp:261 #, fuzzy msgid "user_show_thread_collapsed" msgstr "Show User's Entries" -#: View/Users/edit.ctp:265 +#: View/Users/edit.ctp:264 msgid "user_show_thread_collapsed_exp" msgstr "" -#: View/Users/edit.ctp:270 +#: View/Users/edit.ctp:269 msgid "user_pers_msg" msgstr "" -#: View/Users/edit.ctp:271 +#: View/Users/edit.ctp:270 msgid "user_pers_msg_exp" msgstr "" -#: View/Users/edit.ctp:278 +#: View/Users/edit.ctp:277 #, fuzzy msgid "user_category_override" msgstr "Category-Chooser" -#: View/Users/edit.ctp:283 +#: View/Users/edit.ctp:282 #, fuzzy msgid "user_category_override_exp" msgstr "Activates the category-chooser on the front-page." -#: View/Users/edit.ctp:291 +#: View/Users/edit.ctp:290 msgid "user_time_diff" msgstr "" -#: View/Users/edit.ctp:292 +#: View/Users/edit.ctp:291 msgid "user_time_diff_exp" msgstr "" -#: View/Users/edit.ctp:297 +#: View/Users/edit.ctp:296 msgid "admin_mod_notif" msgstr "" -#: View/Users/edit.ctp:299 +#: View/Users/edit.ctp:298 msgid "admin_mod_notif_exp" msgstr "" -#: View/Users/edit.ctp:300 +#: View/Users/edit.ctp:299 msgid "new_posting_notify_exp" msgstr "" -#: View/Users/edit.ctp:302 +#: View/Users/edit.ctp:301 msgid "new_user_notify_exp" msgstr "" -#: View/Users/edit.ctp:309 +#: View/Users/edit.ctp:308 msgid "user_font_size" msgstr "" -#: View/Users/edit.ctp:326 +#: View/Users/edit.ctp:325 msgid "user_font_size_exp" msgstr "" -#: View/Users/edit.ctp:341 View/Users/view.ctp:104 +#: View/Users/edit.ctp:339 View/Users/view.ctp:112 msgid "flattr" msgstr "" -#: View/Users/edit.ctp:350 +#: View/Users/edit.ctp:348 msgid "flattr_uid" msgstr "" -#: View/Users/edit.ctp:354 +#: View/Users/edit.ctp:352 msgid "flattr_allow_user" msgstr "" -#: View/Users/edit.ctp:355 +#: View/Users/edit.ctp:353 msgid "flattr_allow_user_exp" msgstr "" -#: View/Users/edit.ctp:358 +#: View/Users/edit.ctp:356 msgid "flattr_allow_posting" msgstr "" -#: View/Users/edit.ctp:359 +#: View/Users/edit.ctp:357 msgid "flattr_allow_posting_exp" msgstr "" -#: View/Users/edit.ctp:365 +#: View/Users/edit.ctp:363 msgid "button_save" msgstr "" -#: View/Users/index.ctp:1 +#: View/Users/index.ctp:5 msgid "reg_users_hl" msgstr "Registered Users" @@ -1511,7 +1178,7 @@ msgid "register_success_content" msgstr "" "Your registration is now finished. You are able to loggin now. Have fun!" -#: View/Users/register.ctp:23 Controller/EntriesController.php:330 +#: View/Users/register.ctp:23 Controller/EntriesController.php:305 msgid "js-required" msgstr "To use this forum please enable JavaScript." @@ -1523,47 +1190,51 @@ msgstr "Terms of Service" msgid "register_tos_label" msgstr "I agree to the %s" -#: View/Users/view.ctp:12 -msgid "user_show_entries" -msgstr "Show User's Entries" - -#: View/Users/view.ctp:33 +#: View/Users/view.ctp:32 #, fuzzy msgid "user_block" msgstr "Place" -#: View/Users/view.ctp:74 +#: View/Users/view.ctp:73 msgid "user_since" msgstr "Registered" -#: View/Users/view.ctp:75 +#: View/Users/view.ctp:74 msgid "date_short" msgstr "%b. %d, %Y" -#: View/Users/view.ctp:78 +#: View/Users/view.ctp:77 msgid "user_postings" msgstr "Number of Postings" -#: View/Users/view.ctp:146 +#: View/Users/view.ctp:82 +msgid "user_show_entries" +msgstr "Show User's Entries" + +#: View/Users/view.ctp:154 msgid "edit_userdata" msgstr "Edit" -#: View/Users/view.ctp:156 +#: View/Users/view.ctp:164 msgid "button_mod_panel_shp" msgstr "Moderator Menu" -#: View/Users/view.ctp:170 +#: View/Users/view.ctp:178 msgid "Unlock" msgstr "" -#: View/Users/view.ctp:170 +#: View/Users/view.ctp:178 msgid "Lock" msgstr "" -#: View/Users/view.ctp:236 +#: View/Users/view.ctp:241 msgid "No entries created yet." msgstr "" +#: View/Users/view.ctp:253 +msgid "Show all" +msgstr "" + #: Controller/AdminsController.php:45 msgid "New" msgstr "" @@ -1622,55 +1293,51 @@ msgstr "" msgid "Category deleted." msgstr "" -#: Controller/EntriesController.php:93 +#: Controller/EntriesController.php:88 msgid "page" msgstr "" -#: Controller/EntriesController.php:218 Controller/EntriesController.php:227 +#: Controller/EntriesController.php:187 Controller/EntriesController.php:196 msgid "Invalid post" msgstr "" -#: Controller/EntriesController.php:268 +#: Controller/EntriesController.php:238 msgid "new_entry_linktitle" msgstr "New Entry" -#: Controller/EntriesController.php:271 -msgid "logged_in_users_only" -msgstr "" - -#: Controller/EntriesController.php:321 Controller/EntriesController.php:443 +#: Controller/EntriesController.php:293 Controller/EntriesController.php:414 msgid "Something clogged the tubes. Could not save entry. Try again." msgstr "" -#: Controller/EntriesController.php:368 Controller/EntriesController.php:477 +#: Controller/EntriesController.php:344 Controller/EntriesController.php:454 msgid "back_to_posting_from_linkname" msgstr "Zurück zum Eintrag von %s" -#: Controller/EntriesController.php:379 +#: Controller/EntriesController.php:355 msgid "answer_marking" msgstr "Answer" -#: Controller/EntriesController.php:429 +#: Controller/EntriesController.php:420 msgid "notice_you_are_editing_as_mod" msgstr "You're now editing as moderator!" -#: Controller/EntriesController.php:512 +#: Controller/EntriesController.php:485 msgid "delete_tree_success" msgstr "Thread successfully removed." -#: Controller/EntriesController.php:515 +#: Controller/EntriesController.php:488 msgid "delete_subtree_success" msgstr "Subthread successfully removed." -#: Controller/EntriesController.php:519 +#: Controller/EntriesController.php:492 msgid "delete_tree_error" msgstr "" -#: Controller/EntriesController.php:721 +#: Controller/EntriesController.php:707 msgid "Error" msgstr "" -#: Controller/EntriesController.php:917 +#: Controller/EntriesController.php:924 msgid "Custom" msgstr "" @@ -1736,105 +1403,107 @@ msgstr "" msgid "Smily was not deleted" msgstr "" -#: Controller/ToolsController.php:30 +#: Controller/ToolsController.php:21 msgid "Caches have been emptied." msgstr "" -#: Controller/UploadsController.php:35 Controller/UploadsController.php:89 +#: Controller/UploadsController.php:35 msgid "upload_max_number_of_uploads_failed" msgstr "No more uploads possible (max: %s)." -#: Controller/UsersController.php:54 Controller/UsersController.php:310 +#: Controller/UsersController.php:37 Controller/UsersController.php:329 #, php-format msgid "User %s is locked." msgstr "" -#: Controller/UsersController.php:60 +#: Controller/UsersController.php:43 msgid "auth_loginerror" msgstr "Username or Password wrong." -#: Controller/UsersController.php:110 +#: Controller/UsersController.php:92 msgid "register_email_subject" msgstr "Welcome to %s!" -#: Controller/UsersController.php:196 Controller/UsersController.php:217 +#: Controller/UsersController.php:172 Controller/UsersController.php:195 +#: Controller/UsersController.php:233 msgid "Invalid user" msgstr "" -#: Controller/UsersController.php:251 -msgid "user_edit_success" -msgstr "Preferences were saved." +#: Controller/UsersController.php:224 +#, fuzzy +msgid "User :name" +msgstr "Username" -#: Controller/UsersController.php:264 +#: Controller/UsersController.php:278 msgid "The user could not be saved. Please, try again." msgstr "" -#: Controller/UsersController.php:292 Controller/UsersController.php:329 +#: Controller/UsersController.php:311 Controller/UsersController.php:348 #, fuzzy msgid "User not found." msgstr "Nothing found." -#: Controller/UsersController.php:300 +#: Controller/UsersController.php:320 msgid "You can't lock yourself." msgstr "" -#: Controller/UsersController.php:302 +#: Controller/UsersController.php:322 msgid "You can't lock administrators." msgstr "" -#: Controller/UsersController.php:312 +#: Controller/UsersController.php:331 #, php-format msgid "User %s is unlocked." msgstr "" -#: Controller/UsersController.php:316 +#: Controller/UsersController.php:335 msgid "Error while un/locking." msgstr "" -#: Controller/UsersController.php:335 +#: Controller/UsersController.php:355 msgid "You can't delete yourself." msgstr "" -#: Controller/UsersController.php:337 +#: Controller/UsersController.php:357 msgid "You can't delete the installation account." msgstr "" -#: Controller/UsersController.php:339 +#: Controller/UsersController.php:359 #, php-format msgid "User %s deleted." msgstr "" -#: Controller/UsersController.php:342 +#: Controller/UsersController.php:363 msgid "Couldn't delete user." msgstr "" -#: Controller/UsersController.php:367 +#: Controller/UsersController.php:387 msgid "change_password_success" msgstr "Password was changed." -#: Controller/UsersController.php:429 +#: Controller/UsersController.php:455 msgid "error_email_not-valid" msgstr "No valid email address." -#: Controller/UsersController.php:463 +#: Controller/UsersController.php:502 msgid "Message was send." msgstr "" -#: Controller/UsersController.php:466 -msgid "Error, message couldn't be send! " +#: Controller/UsersController.php:507 +msgid "Message couldn't be send! " msgstr "" -#: Controller/Component/CurrentUserComponent.php:258 +#: Controller/Component/CurrentUserComponent.php:308 msgid "auth_autherror" msgstr "Access to this function is restricted!" -#: Controller/Component/EmailNotificationComponent.php:53 -#: Controller/Component/EmailNotificationComponent.php:85 +#: Controller/Component/EmailNotificationComponent.php:47 +#: Controller/Component/EmailNotificationComponent.php:79 #, php-format msgid "New reply to \"%s\"" msgstr "" -#: Controller/Component/EmailNotificationComponent.php:114 +#: Controller/Component/EmailNotificationComponent.php:112 msgid "Successfull registration" msgstr "" @@ -1914,31 +1583,31 @@ msgstr "" msgid "Abend" msgstr "" -#: View/Helper/BbcodeHelper.php:486 +#: View/Helper/BbcodeHelper.php:580 msgid "" "Your browser does not support HTML5 video. Please updgrade to a " "modernbrowser. In order to watch this stream you need an HTML5 capable " "browser." msgstr "" -#: View/Helper/BbcodeHelper.php:504 +#: View/Helper/BbcodeHelper.php:601 msgid "" "Your browser does not support HTML5 audio. Please updgrade to a " "modernbrowser. In order to watch this stream you need an HTML5 capable " "browser." msgstr "" -#: View/Helper/BbcodeHelper.php:741 +#: View/Helper/BbcodeHelper.php:926 #, fuzzy msgid "[embed] tag not enabled." msgstr "Enable Embedly" -#: View/Helper/BbcodeHelper.php:849 +#: View/Helper/BbcodeHelper.php:1012 #, php-format msgid "Domain %s not allowed for embedding video." msgstr "" -#: View/Helper/BbcodeHelper.php:857 +#: View/Helper/BbcodeHelper.php:1020 msgid "Video domain is not allowed." msgstr "" @@ -1950,15 +1619,15 @@ msgstr "" msgid "Bookmark this entry" msgstr "" -#: View/Helper/EntryHHelper.php:193 +#: View/Helper/EntryHHelper.php:156 msgid "fixed" msgstr "" -#: View/Helper/EntryHHelper.php:208 +#: View/Helper/EntryHHelper.php:171 msgid "cateogry" msgstr "Category" -#: View/Helper/EntryHHelper.php:211 +#: View/Helper/EntryHHelper.php:174 msgid "error_category_empty" msgstr "Category must not be empty." @@ -2026,8 +1695,8 @@ msgstr "" msgid "geshi_picture_popup" msgstr "" -#: View/Helper/MarkitupEditorHelper.php:80 webroot/js/main-prod.js:79 -#: webroot/js/views/uploads.js:71 +#: View/Helper/MarkitupEditorHelper.php:80 webroot/js/main-prod.js:100 +#: webroot/js/views/uploadNew.js:99 webroot/js/views/uploads.js:71 #, fuzzy msgid "Upload" msgstr "Upload" @@ -2036,6 +1705,22 @@ msgstr "Upload" msgid "Media" msgstr "" +#: View/Helper/SettingHelper.php:66 +msgid "edit" +msgstr "" + +#: View/Helper/SettingHelper.php:77 +msgid "Key" +msgstr "" + +#: View/Helper/SettingHelper.php:78 +msgid "Value" +msgstr "" + +#: View/Helper/SettingHelper.php:79 +msgid "Explanation" +msgstr "" + #: View/Helper/TimeHHelper.php:100 View/Helper/TimeHHelper.php:119 msgid "yesterday" msgstr "" @@ -2060,19 +1745,31 @@ msgstr "" msgid "Nothing recognized." msgstr "" -#: webroot/js/templates/uploadNew.html:9 +#: webroot/js/templates/uploadNew.html:8 msgid "upload_new_title" msgstr "Drop Image Here" -#: webroot/js/templates/uploadNew.html:11 +#: webroot/js/templates/uploadNew.html:10 msgid "upload_info" msgstr "Max. size: :size; type: jpg, jpeg, png, gif" -#: webroot/js/main-prod.js:79 webroot/js/views/uploadNew.js:66 +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:69 msgid "upload_fileTypeNotAllowed" msgstr "File type not allowed." -#: webroot/js/main-prod.js:79 +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:73 +msgid "upload_fileToLarge" +msgstr "File :name to large." + +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:78 +msgid "upload_browserNotSupported" +msgstr "Browser not supported." + +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:81 +msgid "upload_toManyFiles" +msgstr "To many files." + +#: webroot/js/main-prod.js:100 msgid "" "Enter link or embedding code:') %>\n" " \n" @@ -2095,7 +1792,7 @@ msgid "" "\n" msgstr "" -#: webroot/js/main-prod.js:79 webroot/js/views/mediaInsert.js:68 +#: webroot/js/main-prod.js:100 webroot/js/views/mediaInsert.js:68 msgid "Multimedia" msgstr "" @@ -2107,48 +1804,49 @@ msgstr "" msgid "tr" msgstr "" -#: webroot/js/tests/lib/jquery.i18n.extendSpec.js:28 +#: webroot/js/tests/lib/jquery.i18n.extendSpec.js:30 msgid "token test" msgstr "" -#: webroot/js/views/uploadNew.js:69 -msgid "upload_fileToLarge" -msgstr "File :name to large." - -#: webroot/js/views/uploadNew.js:72 -msgid "upload_browserNotSupported" -msgstr "Browser not supported." +#~ msgid "forum_disabled" +#~ msgstr "Disable Forum" -#: webroot/js/views/uploadNew.js:75 -msgid "upload_toManyFiles" -msgstr "To many files." +#~ msgid "forum_disabled_exp" +#~ msgstr "Shows the maintenance page except for logged in admins." -#~ msgid "whole_thread_marking" -#~ msgstr "Whole Thread" +#~ msgid "forum_disabled_text" +#~ msgstr "Maintenance Notice" -#~ msgid "register_email_send_content" -#~ msgstr "" -#~ "Thanks for registering. A email with a link was send to you. Please click " -#~ "that link to finish the registration. Prior to that you're not able to " -#~ "login!" +#~ msgid "forum_disabled_text_exp" +#~ msgstr "Text notice shown on the forum disabled page." -#~ msgid "fixed_set_entry_link" -#~ msgstr "Pin" +#~ msgid "forum_name" +#~ msgstr "Forum Title" -#~ msgid "fixed_unset_entry_link" -#~ msgstr "Unpin" +#~ msgid "forum_name_exp" +#~ msgstr "The name of this forum." -#~ msgid "locked_set_entry_link" -#~ msgstr "Lock" +#~ msgid "timezone" +#~ msgstr "Timezone" -#~ msgid "locked_unset_entry_link" -#~ msgstr "Unlock" +#~ msgid "timezone_exp" +#~ msgstr "All time values displayed are offset to match this timezone." -#~ msgid "board_edited_marking" -#~ msgstr "edited by" +#~ msgid "forum_email" +#~ msgstr "Email" -#~ msgid "register_ip" -#~ msgstr "Register IP" +#~ msgid "forum_email_exp" +#~ msgstr "" +#~ "Email address used as sender address for emails send by the forum (e.g. " +#~ "when registering)." -#~ msgid "back_to_posting_linkname" -#~ msgstr "Back to Posting" +#~ msgid "email_admin_config_exp" +#~ msgstr "" +#~ "Additional settings are possible by providing a $saito " +#~ "config in app/config/email.php. You should use app/" +#~ "config/email.php.default as template. See " +#~ "also." + +#~ msgid "block_user_ui" +#~ msgstr "Lock Users" diff --git a/app/Locale/eng/LC_MESSAGES/nondynamic.po b/app/Locale/eng/LC_MESSAGES/nondynamic.po index 16722064b..1723b2c8a 100644 --- a/app/Locale/eng/LC_MESSAGES/nondynamic.po +++ b/app/Locale/eng/LC_MESSAGES/nondynamic.po @@ -82,3 +82,288 @@ msgstr "notice" # msgid "success" msgstr "Success" + +#******************************************************************************* +#* App Settings Start +#******************************************************************************/ + +# +msgid "forum_disabled" +msgstr "Disable Forum" + +# +msgid "forum_disabled_exp" +msgstr "Shows the maintenance page except for logged in admins." + +# +msgid "forum_disabled_text" +msgstr "Maintenance Notice" + +# +msgid "forum_disabled_text_exp" +msgstr "Text notice shown on the forum disabled page." + +# +msgid "forum_name" +msgstr "Forum Title" + +# +msgid "forum_name_exp" +msgstr "The name of this forum." + +# +msgid "timezone" +msgstr "Timezone" + +# +msgid "timezone_exp" +msgstr "All time values displayed are offset to match this timezone." + +# +msgid "forum_email" +msgstr "Email" + +# +msgid "forum_email_exp" +msgstr "Email address used as sender address for emails send by the forum (e.g. when registering)." + +# +msgid "block_user_ui" +msgstr "Lock Users" + +# +msgid "block_user_ui_exp" +msgstr "Moderators are allowed to block users." + +# +msgid "store_ip" +msgstr "Store IP" + +# +msgid "store_ip_exp" +msgstr "Stores the IP-address for new entries. The IP is shown do moderators." + +# +msgid "store_ip_anonymized" +msgstr "IP Anonymized" + +# +msgid "store_ip_anonymized_exp" +msgstr "Strore IP anonymized." + +# +msgid "tos_enabled" +msgstr "ToS Enabled" + +# +msgid "tos_enabled_exp" +msgstr "Puts a Term of Service checkbox and link on the user registration page." + +# +msgid "tos_url" +msgstr "ToS Url" + +# +msgid "tos_url_exp" +msgstr "Sets the url for the ToS link. If empty, use the build-in default pages." + +# +msgid "edit_period" +msgstr "Edit Period" + +# +msgid "edit_period_exp" +msgstr "Minutes a user can edit a new entry." + +# +msgid "edit_delay" +msgstr "Show Edit" + +# +msgid "edit_delay_exp" +msgstr "Minutes a new entry can be edited without being marked as edited." + +# +msgid "topics_per_page" +msgstr "Topics per Page" + +# +msgid "topics_per_page_exp" +msgstr "Number of topics shown on the frontpage." + +# +msgid "thread_depth_indent" +msgstr "Thread Indent Depth" + +# +msgid "thread_depth_indent_exp" +msgstr "Max. indent to the the right for threads in thread-view. After that threads appear on the same level." + +# +msgid "autolink" +msgstr "Autolink" + +# +msgid "autolink_exp" +msgstr "Detect and automatically generate links." + +# +msgid "bbcode_img" +msgstr "Images" + +# +msgid "bbcode_img_exp" +msgstr "Allow embedding of images." + +# +msgid "quote_symbol" +msgstr "Quote Symbol" + +# +msgid "quote_symbol_exp" +msgstr "Quote symbol." + +# +msgid "signature_separator" +msgstr "Signature Separator" + +# +msgid "signature_separator_exp" +msgstr "Signature separator." + +# +msgid "subject_maxlength" +msgstr "Subject Length" + +# +msgid "subject_maxlength_exp" +msgstr "Number of chars allowed in subject." + +# +msgid "text_word_maxlength" +msgstr "Word Length" + +# +msgid "text_word_maxlength_exp" +msgstr "Max numbers of chars allowed in one word." + +# +msgid "userranks_show" +msgstr "Enable Ranks" + +# +msgid "userranks_show_exp" +msgstr "Show user ranks." + +# +msgid "userranks_ranks" +msgstr "Ranks" + +# +msgid "userranks_ranks_exp" +msgstr "User ranks based on number of postings." + +# +msgid "video_domains_allowed" +msgstr "Embeddable Video Sources" + +# +msgid "video_domains_allowed_exp" +msgstr "Domain names embeddable for video/iframe. Asterisk * means all sources allowed (This could pose a security risk if malicious code is embedded!)." + +# +msgid "category_chooser_global" +msgstr "Default" + +# +msgid "category_chooser_global_exp" +msgstr "Always show the category chooser on the front-page." + +# +msgid "category_chooser_user_override" +msgstr "User Override" + +# +msgid "category_chooser_user_override_exp" +msgstr "If the category chooser is off be default this allows users to enable it in their user preferences." + +# +msgid "shoutbox_enabled" +msgstr "Enable" + +# +msgid "shoutbox_enabled_exp" +msgstr "Shoutbox is enabled." + +# +msgid "shoutbox_max_shouts" +msgstr "Number" + +# +msgid "shoutbox_max_shouts_exp" +msgstr "Number of entries shown in the Shoutbox." + +# +msgid "upload_max_img_size" +msgstr "Upload Max Image Size " + +# +msgid "upload_max_img_size_exp" +msgstr "Max image size in kB for uploaded content." + +# +msgid "upload_max_number_of_uploads" +msgstr "Max Number of Uploads" + +# +msgid "upload_max_number_of_uploads_exp" +msgstr "Max number of uploaded files for a single user." + +# +msgid "flattr_enabled" +msgstr "Flattr Enabled" + +# +msgid "flattr_enabled_exp" +msgstr "Allows users to add a flattr button to their profil and their postings." + +# +msgid "flattr_language" +msgstr "Flattr Language" + +# +msgid "flattr_language_exp" +msgstr "Flattr expects an language key for the content send. See flattr documentation for available language keys." + +# +msgid "flattr_category" +msgstr "Flattr Category" + +# +msgid "flattr_category_exp" +msgstr "See flattr documentation for available categories." + +# +msgid "embedly_enabled" +msgstr "Enable Embedly" + +# +msgid "embedly_enabled_exp" +msgstr "Enables the [embed] BBcode which parses content through Embed.ly" + +# +msgid "embedly_key" +msgstr "Embedly Key" + +# +msgid "embedly_key_exp" +msgstr "You have to register at Embed.ly to receive an API-key, which you enter here." + +# +msgid "stopwatch_get" +msgstr "Stopwatch" + +# +msgid "stopwatch_get_exp" +msgstr "Outputs stopwatch times in production mode for logged in users by appending /stopwatch:true/ to url." + diff --git a/app/View/Helper/SettingHelper.php b/app/View/Helper/SettingHelper.php index d0dd22739..32ddeaaa5 100644 --- a/app/View/Helper/SettingHelper.php +++ b/app/View/Helper/SettingHelper.php @@ -43,9 +43,9 @@ public function getHeaders() { public function tableRow($name, $Settings) { return $this->Html->tableCells( [ - __($name), + __d('nondynamic', $name), $Settings[$name], - "

" . __($name . '_exp') . "

", + "

" . __d('nondynamic', $name . '_exp') . "

", $this->Html->link( __('edit'), ['controller' => 'settings', 'action' => 'edit', $name], diff --git a/app/View/Settings/admin_index.ctp b/app/View/Settings/admin_index.ctp index e5951583d..d14afde11 100644 --- a/app/View/Settings/admin_index.ctp +++ b/app/View/Settings/admin_index.ctp @@ -103,7 +103,6 @@ Setting->getHeaders() as $key => $title): ?>
  • -
  • From d3255b479b842b4440beb70de25e2858af0fd4d1 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 16:58:32 +0200 Subject: [PATCH 57/72] fix i18n in Settings admin_edit --- app/View/Settings/admin_edit.ctp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/View/Settings/admin_edit.ctp b/app/View/Settings/admin_edit.ctp index bf1bc4cd1..a2ccc938e 100755 --- a/app/View/Settings/admin_edit.ctp +++ b/app/View/Settings/admin_edit.ctp @@ -1,8 +1,8 @@ Html->addCrumb(__('Settings'), '/admin/settings'); - $this->Html->addCrumb(__($this->request->data['Setting']['name']), '#'); + $this->Html->addCrumb(__d('nondynamic', $this->request->data['Setting']['name']), '#'); ?> -

    request->data['Setting']['name']); ?>

    +

    request->data['Setting']['name']); ?>

    Form->input( 'value', array( - 'label' => __($this->request->data['Setting']['name']), + 'label' => __d('nondynamic', $this->request->data['Setting']['name']), )); echo $this->Form->submit( __('Submit'), array( @@ -24,6 +24,6 @@ ?>
    -

    request->data['Setting']['name'] . '_exp'); ?>

    +

    request->data['Setting']['name'] . '_exp'); ?>

    \ No newline at end of file From 8aec1ed0b476692a8cc8dc3f03faefedbca023b9 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 17:14:22 +0200 Subject: [PATCH 58/72] admin settings: jump to setting category after setting edit --- app/Controller/SettingsController.php | 3 ++- app/View/Helper/SettingHelper.php | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Controller/SettingsController.php b/app/Controller/SettingsController.php index 88189323d..3e9ec36de 100755 --- a/app/Controller/SettingsController.php +++ b/app/Controller/SettingsController.php @@ -87,7 +87,8 @@ public function admin_edit($id = NULL) { $this->Setting->id = $id; if ( $this->Setting->save($this->request->data) ) { $this->Session->setFlash('Saved. @lo', 'flash/notice'); - $this->redirect(array( 'action' => 'index', $id )); + $this->redirect(['action' => 'index', '#' => $id]); + return; } else { $this->Session->setFlash('Something went wrong @lo', 'flash/error'); } diff --git a/app/View/Helper/SettingHelper.php b/app/View/Helper/SettingHelper.php index 32ddeaaa5..bf8b4ea63 100644 --- a/app/View/Helper/SettingHelper.php +++ b/app/View/Helper/SettingHelper.php @@ -17,14 +17,17 @@ public function table($table_name, array $setting_names, $Settings, array $optio $options += $defaults; $out = $this->tableHeaders(); + $anchors = ''; foreach ($setting_names as $name) { $out .= $this->tableRow($name, $Settings); + $anchors .= ''; } $key = $this->addHeader($options['nav-title']); $out = '' . $out . '
    '; $out = '' + . $anchors . '

    ' . $table_name . '

    ' . $out; return $out; From 160ea6ce098b6f8700ba48fde1132b02a0634dee Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 18:29:21 +0200 Subject: [PATCH 59/72] fixes no userranks in admin settings index shown --- app/Controller/SettingsController.php | 1 + app/Model/AppModel.php | 12 ++++++++++-- app/Model/Setting.php | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/Controller/SettingsController.php b/app/Controller/SettingsController.php index 3e9ec36de..15d1635e9 100755 --- a/app/Controller/SettingsController.php +++ b/app/Controller/SettingsController.php @@ -62,6 +62,7 @@ class SettingsController extends AppController { public function admin_index() { $settings = $this->request->data = $this->Setting->getSettings(); $settings = array_intersect_key($settings, $this->_currentlyUsedSettings); + $settings['userranks_ranks'] = $this->Setting->pipeMerger($settings['userranks_ranks']); $this->set('Settings', $settings); } diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index e98587d2e..cbba5ce15 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -77,6 +77,14 @@ public function toggle($key) { return $value; } + public function pipeMerger(array $data) { + $out = []; + foreach ($data as $key => $value) { + $out[] = "$key=$value"; + } + return implode(' | ', $out); + } + /** * Splits String 'a=b|c=d|e=f' into an array('a'=>'b', 'c'=>'d', 'e'=>'f') * @@ -85,10 +93,10 @@ public function toggle($key) { */ protected function _pipeSplitter($pipeString) { $unpipedArray = array(); - $ranks = explode("|", $pipeString); + $ranks = explode('|', $pipeString); foreach ( $ranks as $rank ) : $matches = array(); - $matched = preg_match('/(\d+)\s*=\s*(.*)/', trim($rank), $matches); + $matched = preg_match('/(\w+)\s*=\s*(.*)/', trim($rank), $matches); if ($matched) { $unpipedArray[$matches[1]] = $matches[2]; } diff --git a/app/Model/Setting.php b/app/Model/Setting.php index 5df5c09de..4458b32cc 100755 --- a/app/Model/Setting.php +++ b/app/Model/Setting.php @@ -27,7 +27,9 @@ public function getSettings() { $settings = $this->find('all'); $settings = $this->_compactKeyValue($settings); - $settings['userranks_ranks'] = $this->_pipeSplitter($settings['userranks_ranks']); + $ranks = $this->_pipeSplitter($settings['userranks_ranks']); + ksort($ranks); + $settings['userranks_ranks'] = $ranks; return $settings; } From 171106bc1febc9d7f33a9c91c0e6cc2ec7841872 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 29 Jul 2013 21:27:39 +0200 Subject: [PATCH 60/72] [doc] is_pinned api value --- docs/api-v1.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/api-v1.md b/docs/api-v1.md index 3b27bb242..fa3950fb2 100644 --- a/docs/api-v1.md +++ b/docs/api-v1.md @@ -166,7 +166,7 @@ Index of latest threads. - request method: `GET` - authorization: limited (different categories) - query parameter: - - `order` (optional) + - `order` (optional); pinned entries are always output first - values: - `time` (default) - `answer` @@ -181,7 +181,8 @@ Response { "id": 423739, "subject": "my subject", - "is_nt": true|false + "is_nt": true|false, + "is_pinned": false, "time": "2013-07-04T19:53:14+00:00", "last_answer": "2013-07-05T10:45:33+00:00", "user_id": 1, @@ -192,6 +193,10 @@ Response … ] +`is_pinned` +: boolean +: is true if a entry is pinned by a moderator to be shown "on top" + ### threads/#id GET ### From b7e0746ed245886767071b0984b9e2561516bc15 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 09:49:21 +0200 Subject: [PATCH 61/72] i10n subject max length error messages --- app/Locale/deu/LC_MESSAGES/default.po | 47 ++++++++++++++------------- app/Locale/eng/LC_MESSAGES/default.po | 47 ++++++++++++++------------- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/app/Locale/deu/LC_MESSAGES/default.po b/app/Locale/deu/LC_MESSAGES/default.po index 68ce5d235..acb263f76 100644 --- a/app/Locale/deu/LC_MESSAGES/default.po +++ b/app/Locale/deu/LC_MESSAGES/default.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-07-29 14:53+0100\n" -"PO-Revision-Date: 2013-07-29 14:53+0100\n" +"POT-Creation-Date: 2013-08-12 09:09+0100\n" +"PO-Revision-Date: 2013-08-12 09:09+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: de_DE\n" @@ -124,7 +124,7 @@ msgid "Bookmarks" msgstr "Lesezeichen" #: View/Bookmarks/index.ctp:24 View/Categories/view.ctp:64 -#: View/Entries/add.ctp:89 +#: View/Entries/add.ctp:92 msgid "Subject" msgstr "Betreff" @@ -160,7 +160,7 @@ msgstr "Absenden" #: View/Categories/index.ctp:51 View/Categories/view.ctp:37 #: View/Categories/view.ctp:73 View/SmileyCodes/admin_add.ctp:14 #: View/SmileyCodes/admin_index.ctp:12 View/Smilies/admin_add.ctp:18 -#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:64 +#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:67 msgid "Actions" msgstr "Aktion" @@ -630,35 +630,39 @@ msgstr "" "Um Ihr Benutzerkonto zu aktivieren, besuchen Sie bitte folgenden Verweis " "innerhalb der nächsten 24 Stunden: %s" -#: View/Entries/add.ctp:34 View/Entries/add.ctp:187 +#: View/Entries/add.ctp:34 View/Entries/add.ctp:190 msgid "preview" msgstr "Vorschau" -#: View/Entries/add.ctp:87 Controller/UsersController.php:477 +#: View/Entries/add.ctp:88 Controller/UsersController.php:477 msgid "error_subject_empty" msgstr "Betreff darf nicht leer sein." -#: View/Entries/add.ctp:130 +#: View/Entries/add.ctp:89 +msgid "error_subject_max_length" +msgstr "Betreff ist zu lang." + +#: View/Entries/add.ctp:133 msgid "Cite" msgstr "Zitieren" -#: View/Entries/add.ctp:155 View/Entries/add.ctp:175 View/Entries/add.ctp:289 +#: View/Entries/add.ctp:158 View/Entries/add.ctp:178 View/Entries/add.ctp:292 msgid "submit_button" msgstr "Eintragen" -#: View/Entries/add.ctp:202 +#: View/Entries/add.ctp:205 msgid "Notify on reply" msgstr "Beitrag abonnieren" -#: View/Entries/add.ctp:216 +#: View/Entries/add.ctp:219 msgid "Notify on thread replies" msgstr "Thread abonnieren" -#: View/Entries/add.ctp:225 View/Helper/EntryHHelper.php:159 +#: View/Entries/add.ctp:228 View/Helper/EntryHHelper.php:159 msgid "entry_nsfw_title" msgstr "Nicht bürosicher (NBS)" -#: View/Entries/add.ctp:238 +#: View/Entries/add.ctp:241 msgid "entry_flattr_this_posting" msgstr "Flattr-Knopf anzeigen" @@ -766,10 +770,6 @@ msgstr "Letzte Einträge" msgid "Last started threads" msgstr "Letzte Threads" -#: View/Settings/admin_edit.ctp:27 View/Settings/admin_timezone.ctp:24 -msgid "_exp" -msgstr "" - #: View/Settings/admin_index.ctp:7 msgid "Deactivate Forum" msgstr "Forum deaktivieren" @@ -810,6 +810,10 @@ msgstr "Hochladen" msgid "Debug" msgstr "" +#: View/Settings/admin_timezone.ctp:24 +msgid "_exp" +msgstr "" + #: View/SmileyCodes/admin_add.ctp:4 #, fuzzy msgid "Add Smiley Code" @@ -1766,22 +1770,22 @@ msgstr "Hochladen" msgid "Media" msgstr "Einbinden" -#: View/Helper/SettingHelper.php:50 +#: View/Helper/SettingHelper.php:53 msgid "edit" msgstr "Bearbeiten" # View/Settings/admin_index.ctp: -#: View/Helper/SettingHelper.php:61 +#: View/Helper/SettingHelper.php:64 msgid "Key" msgstr "Schlüssel" # View/Settings/admin_index.ctp: -#: View/Helper/SettingHelper.php:62 +#: View/Helper/SettingHelper.php:65 msgid "Value" msgstr "Wert" # View/Settings/admin_index.ctp: -#: View/Helper/SettingHelper.php:63 +#: View/Helper/SettingHelper.php:66 msgid "Explanation" msgstr "Beschreibung" @@ -2014,9 +2018,6 @@ msgstr "" #~ msgid "signature_separator_exp" #~ msgstr "Signaturtrenner" -#~ msgid "subject_maxlength" -#~ msgstr "Betrefflänge" - #~ msgid "subject_maxlength_exp" #~ msgstr "Maximale Zeichenanzahl in Betreff." diff --git a/app/Locale/eng/LC_MESSAGES/default.po b/app/Locale/eng/LC_MESSAGES/default.po index f13f3629c..50fdd1315 100644 --- a/app/Locale/eng/LC_MESSAGES/default.po +++ b/app/Locale/eng/LC_MESSAGES/default.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-07-29 14:47+0100\n" -"PO-Revision-Date: 2013-07-29 14:48+0100\n" +"POT-Creation-Date: 2013-08-12 09:10+0100\n" +"PO-Revision-Date: 2013-08-12 09:10+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: en_US\n" @@ -124,7 +124,7 @@ msgid "Bookmarks" msgstr "" #: View/Bookmarks/index.ctp:24 View/Categories/view.ctp:64 -#: View/Entries/add.ctp:89 +#: View/Entries/add.ctp:92 msgid "Subject" msgstr "" @@ -160,7 +160,7 @@ msgstr "" #: View/Categories/index.ctp:51 View/Categories/view.ctp:37 #: View/Categories/view.ctp:73 View/SmileyCodes/admin_add.ctp:14 #: View/SmileyCodes/admin_index.ctp:12 View/Smilies/admin_add.ctp:18 -#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:80 +#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:67 msgid "Actions" msgstr "" @@ -612,35 +612,39 @@ msgstr "" "To activate your account please click the following link: %s within the next " "24 hours." -#: View/Entries/add.ctp:34 View/Entries/add.ctp:187 +#: View/Entries/add.ctp:34 View/Entries/add.ctp:190 msgid "preview" msgstr "Preview" -#: View/Entries/add.ctp:87 Controller/UsersController.php:477 +#: View/Entries/add.ctp:88 Controller/UsersController.php:477 msgid "error_subject_empty" msgstr "Subject must not be empty." -#: View/Entries/add.ctp:130 +#: View/Entries/add.ctp:89 +msgid "error_subject_max_length" +msgstr "Subject is to long." + +#: View/Entries/add.ctp:133 msgid "Cite" msgstr "" -#: View/Entries/add.ctp:155 View/Entries/add.ctp:175 View/Entries/add.ctp:289 +#: View/Entries/add.ctp:158 View/Entries/add.ctp:178 View/Entries/add.ctp:292 msgid "submit_button" msgstr "Submit" -#: View/Entries/add.ctp:202 +#: View/Entries/add.ctp:205 msgid "Notify on reply" msgstr "" -#: View/Entries/add.ctp:216 +#: View/Entries/add.ctp:219 msgid "Notify on thread replies" msgstr "" -#: View/Entries/add.ctp:225 View/Helper/EntryHHelper.php:159 +#: View/Entries/add.ctp:228 View/Helper/EntryHHelper.php:159 msgid "entry_nsfw_title" msgstr "NSFW" -#: View/Entries/add.ctp:238 +#: View/Entries/add.ctp:241 msgid "entry_flattr_this_posting" msgstr "flattr-Button" @@ -711,7 +715,7 @@ msgid "Overview" msgstr "" #: View/Layouts/admin.ctp:41 View/Settings/admin_edit.ctp:2 -#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:114 +#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:113 #: View/Users/edit.ctp:163 msgid "Settings" msgstr "" @@ -748,11 +752,6 @@ msgstr "" msgid "Last started threads" msgstr "" -#: View/Settings/admin_edit.ctp:27 View/Settings/admin_timezone.ctp:24 -#: View/Helper/SettingHelper.php:48 View/Helper/SettingHelper.php:64 -msgid "_exp" -msgstr "" - #: View/Settings/admin_index.ctp:7 msgid "Deactivate Forum" msgstr "" @@ -790,6 +789,10 @@ msgstr "Upload" msgid "Debug" msgstr "" +#: View/Settings/admin_timezone.ctp:24 +msgid "_exp" +msgstr "" + #: View/SmileyCodes/admin_add.ctp:4 msgid "Add Smiley Code" msgstr "" @@ -1705,19 +1708,19 @@ msgstr "Upload" msgid "Media" msgstr "" -#: View/Helper/SettingHelper.php:66 +#: View/Helper/SettingHelper.php:53 msgid "edit" msgstr "" -#: View/Helper/SettingHelper.php:77 +#: View/Helper/SettingHelper.php:64 msgid "Key" msgstr "" -#: View/Helper/SettingHelper.php:78 +#: View/Helper/SettingHelper.php:65 msgid "Value" msgstr "" -#: View/Helper/SettingHelper.php:79 +#: View/Helper/SettingHelper.php:66 msgid "Explanation" msgstr "" From 1d29d9d36a9d0f6a62d98456380f0345a06e3e7a Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 09:50:06 +0200 Subject: [PATCH 62/72] [fix] can't save entries if subject is exactly max length --- app/Model/Entry.php | 2 +- app/Test/Case/Model/EntryTest.php | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/Model/Entry.php b/app/Model/Entry.php index 8ec86a31e..e990b04ca 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -1178,7 +1178,7 @@ public function validateEditingAllowed($check) { * @return bool */ public function validateSubjectMaxLength($check) { - return mb_strlen($check['subject']) < $this->_subjectMaxLenght; + return mb_strlen($check['subject']) <= $this->_subjectMaxLenght; } /** diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index 6cab8563b..371d3c6d9 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -11,6 +11,10 @@ class EntryMock extends Entry { public function prepareBbcode($string) { return $string; } + + public function getSubjectMaxLength() { + return $this->_subjectMaxLenght; + } } class EntryTest extends CakeTestCase { @@ -53,11 +57,10 @@ public function testCreateSuccess() { ->will($this->returnValue(1)); $this->Entry->_CurrentUser = $SaitoUser; - Configure::write('Saito.Settings.subject_maxlength', 75); - $data[$this->Entry->alias] = [ 'pid' => 0, - 'subject' => 'Sübject', + // +1 because str_pad calculates non ascii chars to a string length of 2 + 'subject' => str_pad('Sübject', $this->Entry->getSubjectMaxLength() + 1, '.'), 'text' => 'Täxt', 'category' => 1 ]; @@ -65,6 +68,8 @@ public function testCreateSuccess() { $this->Entry->createPosting($data); $result = $this->Entry->get($this->Entry->id, true); + $this->assertEmpty($this->Entry->validationErrors); + $expected = $data; $result = array_intersect_key($result, $expected); $result[$this->Entry->alias] = array_intersect_key( From d8ed0e2ef92eff76a611535b790dac145e3ced56 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 09:50:34 +0200 Subject: [PATCH 63/72] output subject max length error in frontend --- app/View/Entries/add.ctp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/View/Entries/add.ctp b/app/View/Entries/add.ctp index 5c9f1062d..5d6aaa3ef 100644 --- a/app/View/Entries/add.ctp +++ b/app/View/Entries/add.ctp @@ -84,7 +84,10 @@ ), 'label' => false, 'tabindex' => 2, - 'error' => ['notEmpty' => __('error_subject_empty')], + 'error' => [ + 'notEmpty' => __('error_subject_empty'), + 'maxLength' => __('error_subject_max_length') + ], 'div' => ['class' => 'required'], 'placeholder' => (!empty($citeSubject)) ? $citeSubject : __('Subject'), 'required' => ($posting_type === 'reply') ? false : "required" From 6a10d45559ec47cfa2224e2cfb9e0990c7eeb356 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 10:04:34 +0200 Subject: [PATCH 64/72] updates to CakePHP2.4-RC1 --- app/Controller/AppController.php | 2 +- .../Component/SaitoEmailComponent.php | 2 +- app/Lib/SaitoControllerTestCase.php | 2 +- app/View/Admins/admin_index.ctp | 2 +- lib/Cake/Cache/Engine/FileEngine.php | 71 ++- lib/Cake/Console/Command/BakeShell.php | 3 + lib/Cake/Console/Command/ConsoleShell.php | 462 +++++++++++------- lib/Cake/Console/Command/ServerShell.php | 4 +- .../Console/Command/Task/ControllerTask.php | 12 +- lib/Cake/Console/Command/TestsuiteShell.php | 3 +- lib/Cake/Console/ShellDispatcher.php | 10 +- .../default/actions/controller_actions.ctp | 10 +- .../Templates/skel/Config/bootstrap.php | 36 +- .../Console/Templates/skel/Config/core.php | 20 +- .../Templates/skel/View/Pages/home.ctp | 2 +- .../Console/Templates/skel/webroot/index.php | 3 +- .../Controller/Component/AuthComponent.php | 2 +- .../Controller/Component/CookieComponent.php | 2 +- .../Component/PaginatorComponent.php | 2 +- .../Component/SecurityComponent.php | 2 +- lib/Cake/Controller/ComponentCollection.php | 12 +- lib/Cake/Controller/Controller.php | 22 +- lib/Cake/Core/Configure.php | 24 +- lib/Cake/Core/Object.php | 4 +- lib/Cake/I18n/I18n.php | 9 +- lib/Cake/I18n/L10n.php | 84 ++-- lib/Cake/Log/Engine/FileLog.php | 4 + lib/Cake/Model/AclNode.php | 4 +- lib/Cake/Model/BehaviorCollection.php | 2 +- lib/Cake/Model/Model.php | 12 +- lib/Cake/Model/ModelValidator.php | 5 +- lib/Cake/Network/CakeRequest.php | 4 +- lib/Cake/Network/CakeResponse.php | 75 +-- lib/Cake/Routing/Route/CakeRoute.php | 3 + lib/Cake/Routing/Router.php | 35 +- lib/Cake/Test/Case/AllDbRelatedTest.php | 51 ++ lib/Cake/Test/Case/Cache/CacheTest.php | 6 + .../Test/Case/Cache/Engine/ApcEngineTest.php | 4 + .../Test/Case/Cache/Engine/FileEngineTest.php | 58 ++- .../Case/Cache/Engine/RedisEngineTest.php | 2 + .../Case/Console/Command/ApiShellTest.php | 2 +- .../Command/Task/ControllerTaskTest.php | 17 +- .../Component/AuthComponentTest.php | 63 +++ .../Component/CookieComponentTest.php | 20 + .../Component/PaginatorComponentTest.php | 2 + lib/Cake/Test/Case/Core/ConfigureTest.php | 17 + lib/Cake/Test/Case/Core/ObjectTest.php | 4 +- .../Test/Case/Error/ExceptionRendererTest.php | 2 +- lib/Cake/Test/Case/I18n/I18nTest.php | 10 + lib/Cake/Test/Case/I18n/L10nTest.php | 64 ++- .../Case/Model/Behavior/AclBehaviorTest.php | 1 + .../Model/Behavior/TreeBehaviorAfterTest.php | 1 + .../Model/Behavior/TreeBehaviorNumberTest.php | 51 ++ .../Model/Behavior/TreeBehaviorScopedTest.php | 7 + .../Model/Behavior/TreeBehaviorUuidTest.php | 8 + lib/Cake/Test/Case/Model/CakeSchemaTest.php | 18 - .../Model/Datasource/Database/MysqlTest.php | 8 +- .../Datasource/Database/PostgresTest.php | 2 + .../Datasource/Database/SqlserverTest.php | 2 + lib/Cake/Test/Case/Model/ModelDeleteTest.php | 1 + .../Test/Case/Model/ModelIntegrationTest.php | 12 +- lib/Cake/Test/Case/Model/ModelReadTest.php | 7 + .../Test/Case/Model/ModelValidationTest.php | 14 +- lib/Cake/Test/Case/Model/ModelWriteTest.php | 67 ++- .../Test/Case/Network/CakeRequestTest.php | 12 +- .../Test/Case/Network/CakeResponseTest.php | 120 ++++- .../Test/Case/Network/Email/CakeEmailTest.php | 6 +- .../Case/Network/Email/DebugTransportTest.php | 1 + .../Case/Network/Email/MailTransportTest.php | 1 + .../Case/Network/Email/SmtpTransportTest.php | 1 + .../Network/Http/DigestAuthenticationTest.php | 2 + .../Case/Network/Http/HttpResponseTest.php | 1 + .../Test/Case/Network/Http/HttpSocketTest.php | 14 +- lib/Cake/Test/Case/Routing/DispatcherTest.php | 2 + .../Routing/Filter/AssetDispatcherTest.php | 1 + .../Test/Case/Routing/Route/CakeRouteTest.php | 36 +- lib/Cake/Test/Case/Routing/RouterTest.php | 69 ++- .../Test/Case/TestSuite/CakeTestCaseTest.php | 45 ++ .../Case/TestSuite/CakeTestFixtureTest.php | 2 + lib/Cake/Test/Case/Utility/CakeNumberTest.php | 34 ++ lib/Cake/Test/Case/Utility/CakeTimeTest.php | 1 + lib/Cake/Test/Case/Utility/DebuggerTest.php | 5 + lib/Cake/Test/Case/Utility/SetTest.php | 28 +- lib/Cake/Test/Case/Utility/StringTest.php | 16 +- lib/Cake/Test/Case/Utility/ValidationTest.php | 24 +- .../Test/Case/View/Helper/FormHelperTest.php | 63 ++- .../Test/Case/View/Helper/HtmlHelperTest.php | 63 ++- .../Test/Case/View/Helper/JsHelperTest.php | 2 +- .../View/Helper/MootoolsEngineHelperTest.php | 2 +- .../Case/View/Helper/PaginatorHelperTest.php | 3 +- .../Test/Case/View/Helper/TimeHelperTest.php | 8 +- lib/Cake/Test/Case/View/HelperTest.php | 32 +- lib/Cake/Test/Case/View/JsonViewTest.php | 5 + lib/Cake/Test/Case/View/ViewTest.php | 49 +- lib/Cake/Test/Case/View/XmlViewTest.php | 5 + lib/Cake/Test/Fixture/AcoTwoFixture.php | 4 +- lib/Cake/Test/Fixture/AroTwoFixture.php | 4 +- lib/Cake/Test/Fixture/FlagTreeFixture.php | 10 +- lib/Cake/Test/Fixture/NumberTreeFixture.php | 8 +- .../Test/Fixture/NumberTreeTwoFixture.php | 8 +- .../Fixture/UnconventionalTreeFixture.php | 8 +- .../test_app/View/Posts/test_nocache_tags.ctp | 2 +- lib/Cake/TestSuite/CakeTestCase.php | 14 +- lib/Cake/TestSuite/CakeTestSuiteCommand.php | 18 - lib/Cake/TestSuite/ControllerTestCase.php | 1 + lib/Cake/TestSuite/Fixture/CakeTestModel.php | 26 +- lib/Cake/Utility/CakeNumber.php | 29 +- lib/Cake/Utility/Debugger.php | 34 +- lib/Cake/Utility/File.php | 9 +- lib/Cake/Utility/Sanitize.php | 1 + lib/Cake/Utility/String.php | 2 +- lib/Cake/Utility/Validation.php | 8 +- lib/Cake/VERSION.txt | 2 +- lib/Cake/View/Elements/sql_dump.ctp | 14 +- lib/Cake/View/Helper.php | 34 +- lib/Cake/View/Helper/CacheHelper.php | 3 +- lib/Cake/View/Helper/FormHelper.php | 34 +- lib/Cake/View/Helper/HtmlHelper.php | 32 +- lib/Cake/View/Helper/JsHelper.php | 2 +- lib/Cake/View/Helper/TimeHelper.php | 2 +- lib/Cake/View/HelperCollection.php | 2 +- lib/Cake/View/JsonView.php | 5 + lib/Cake/View/View.php | 12 +- lib/Cake/View/XmlView.php | 8 +- lib/Cake/bootstrap.php | 14 +- 125 files changed, 1787 insertions(+), 688 deletions(-) create mode 100755 lib/Cake/Test/Case/AllDbRelatedTest.php diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 266f78dce..e328f4163 100644 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -101,7 +101,7 @@ public function beforeFilter() { [ 'hashBaseUrl' => 'entries/view/', 'atBaseUrl' => 'users/name/', - 'server' => Router::baseURL(), + 'server' => Router::fullBaseUrl(), 'webroot' => $this->webroot ] ); diff --git a/app/Controller/Component/SaitoEmailComponent.php b/app/Controller/Component/SaitoEmailComponent.php index 64c24cc6f..01556a1dd 100644 --- a/app/Controller/Component/SaitoEmailComponent.php +++ b/app/Controller/Component/SaitoEmailComponent.php @@ -64,7 +64,7 @@ protected function _resetConfig() { protected function _config($options = array()) { $defaults = array( 'viewVars'=> array( - 'webroot' => Router::baseURL() . $this->_webroot, + 'webroot' => Router::fullBaseUrl() . $this->_webroot, ), ); extract(array_merge_recursive($defaults, $options)); diff --git a/app/Lib/SaitoControllerTestCase.php b/app/Lib/SaitoControllerTestCase.php index 3eb30c06d..f5d9b3aa1 100644 --- a/app/Lib/SaitoControllerTestCase.php +++ b/app/Lib/SaitoControllerTestCase.php @@ -113,7 +113,7 @@ protected function _loginUser($id) { public function assertRedirectedTo($url = '') { $this->assertEqual( - Router::baseURL() . $this->controller->request->webroot . $url, + Router::fullBaseUrl() . $this->controller->request->webroot . $url, $this->headers['Location'] ); } diff --git a/app/View/Admins/admin_index.ctp b/app/View/Admins/admin_index.ctp index 797d7b652..d905d7082 100755 --- a/app/View/Admins/admin_index.ctp +++ b/app/View/Admins/admin_index.ctp @@ -6,7 +6,7 @@ You are running Saito version: .

    - Saito is convinced it's running on the server: . + Saito is convinced it's running on the server: .

    Saito believes its base-URL is: request->webroot ?>. diff --git a/lib/Cake/Cache/Engine/FileEngine.php b/lib/Cake/Cache/Engine/FileEngine.php index d32f65191..f3d84989d 100755 --- a/lib/Cake/Cache/Engine/FileEngine.php +++ b/lib/Cake/Cache/Engine/FileEngine.php @@ -223,40 +223,77 @@ public function clear($check) { if (!$this->_init) { return false; } - $dir = dir($this->settings['path']); + $this->_File = null; + + $threshold = $now = false; if ($check) { $now = time(); $threshold = $now - $this->settings['duration']; } + + $this->_clearDirectory($this->settings['path'], $now, $threshold); + + $directory = new RecursiveDirectoryIterator($this->settings['path']); + $contents = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST); + $cleared = array(); + foreach ($contents as $path) { + if ($path->isFile()) { + continue; + } + + $path = $path->getRealPath() . DS; + if (!in_array($path, $cleared)) { + $this->_clearDirectory($path, $now, $threshold); + $cleared[] = $path; + } + } + return true; + } + +/** + * Used to clear a directory of matching files. + * + * @param string $path The path to search. + * @param integer $now The current timestamp + * @param integer $threshold Any file not modified after this value will be deleted. + * @return void + */ + protected function _clearDirectory($path, $now, $threshold) { $prefixLength = strlen($this->settings['prefix']); + + if (!is_dir($path)) { + return; + } + + $dir = dir($path); while (($entry = $dir->read()) !== false) { if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) { continue; } - if ($this->_setKey($entry) === false) { + $filePath = $path . $entry; + if (!file_exists($filePath) || is_dir($filePath)) { continue; } - if ($check) { - $mtime = $this->_File->getMTime(); + $file = new SplFileObject($path . $entry, 'r'); + + if ($threshold) { + $mtime = $file->getMTime(); if ($mtime > $threshold) { continue; } - - $expires = (int)$this->_File->current(); + $expires = (int)$file->current(); if ($expires > $now) { continue; } } - $path = $this->_File->getRealPath(); - $this->_File = null; - if (file_exists($path)) { - unlink($path); + if ($file->isFile()) { + $_path = $file->getRealPath(); + $file = null; + unlink($_path); } } - $dir->close(); - return true; } /** @@ -299,7 +336,7 @@ protected function _setKey($key, $createKey = false) { $dir = $this->settings['path'] . $groups; if (!is_dir($dir)) { - mkdir($dir, 0777, true); + mkdir($dir, 0775, true); } $path = new SplFileInfo($dir . $key); @@ -332,6 +369,12 @@ protected function _setKey($key, $createKey = false) { */ protected function _active() { $dir = new SplFileInfo($this->settings['path']); + if (Configure::read('debug')) { + $path = $dir->getPathname(); + if (!is_dir($path)) { + mkdir($path, 0775, true); + } + } if ($this->_init && !($dir->isDir() && $dir->isWritable())) { $this->_init = false; trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING); @@ -361,6 +404,7 @@ public function key($key) { * @return boolean success */ public function clearGroup($group) { + $this->_File = null; $directoryIterator = new RecursiveDirectoryIterator($this->settings['path']); $contents = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST); foreach ($contents as $object) { @@ -370,7 +414,6 @@ public function clearGroup($group) { unlink($object->getPathName()); } } - $this->_File = null; return true; } } diff --git a/lib/Cake/Console/Command/BakeShell.php b/lib/Cake/Console/Command/BakeShell.php index f8bafb1dd..4c06c74f4 100755 --- a/lib/Cake/Console/Command/BakeShell.php +++ b/lib/Cake/Console/Command/BakeShell.php @@ -66,6 +66,9 @@ public function startup() { $this->{$task}->connection = $this->params['connection']; } } + if (isset($this->params['connection'])) { + $this->connection = $this->params['connection']; + } } /** diff --git a/lib/Cake/Console/Command/ConsoleShell.php b/lib/Cake/Console/Command/ConsoleShell.php index 7b97f89db..b2a4431a2 100755 --- a/lib/Cake/Console/Command/ConsoleShell.php +++ b/lib/Cake/Console/Command/ConsoleShell.php @@ -19,6 +19,7 @@ * Provides a very basic 'interactive' console for CakePHP apps. * * @package Cake.Console.Command + * @deprecated Deprecated since version 2.4, will be removed in 3.0 */ class ConsoleShell extends AppShell { @@ -43,6 +44,35 @@ class ConsoleShell extends AppShell { */ public $models = array(); +/** + * _finished + * + * This shell is perpetual, setting this property to true exits the process + * + * @var mixed + */ + protected $_finished = false; + +/** + * _methodPatterns + * + * @var array + */ + protected $_methodPatterns = array( + 'help' => '/^(help|\?)/', + '_exit' => '/^(quit|exit)/', + '_models' => '/^models/i', + '_bind' => '/^(\w+) bind (\w+) (\w+)/', + '_unbind' => '/^(\w+) unbind (\w+) (\w+)/', + '_find' => '/.+->find/', + '_save' => '/.+->save/', + '_columns' => '/^(\w+) columns/', + '_routesReload' => '/^routes\s+reload/i', + '_routesShow' => '/^routes\s+show/i', + '_routeToString' => '/^route\s+(\(.*\))$/i', + '_routeToArray' => '/^route\s+(.*)$/i', + ); + /** * Override startup of the Shell * @@ -55,7 +85,6 @@ public function startup() { foreach ($this->models as $model) { $class = $model; - $this->models[$model] = $class; App::uses($class, 'Model'); $this->{$class} = new $class(); } @@ -75,6 +104,11 @@ public function startup() { } } +/** + * getOptionParser + * + * @return void + */ public function getOptionParser() { $description = array( 'The interactive console is a tool for testing parts of your', @@ -164,192 +198,290 @@ public function help() { * @return void */ public function main($command = null) { - while (true) { + $this->_finished = false; + while (!$this->_finished) { if (empty($command)) { $command = trim($this->in('')); } - switch ($command) { - case 'help': - $this->help(); - break; - case 'quit': - case 'exit': - return true; - case 'models': - $this->out(__d('cake_console', 'Model classes:')); - $this->hr(); - foreach ($this->models as $model) { - $this->out(" - {$model}"); - } - break; - case preg_match("/^(\w+) bind (\w+) (\w+)/", $command, $tmp): - foreach ($tmp as $data) { - $data = strip_tags($data); - $data = str_replace($this->badCommandChars, "", $data); - } + $method = $this->_method($command); - $modelA = $tmp[1]; - $association = $tmp[2]; - $modelB = $tmp[3]; + if ($method) { + $this->$method($command); + } else { + $this->out(__d('cake_console', "Invalid command")); + $this->out(); + } + $command = ''; + } + } - if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) { - $this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false); - $this->out(__d('cake_console', "Created %s association between %s and %s", - $association, $modelA, $modelB)); - } else { - $this->out(__d('cake_console', "Please verify you are using valid models and association types")); - } - break; - case preg_match("/^(\w+) unbind (\w+) (\w+)/", $command, $tmp): - foreach ($tmp as $data) { - $data = strip_tags($data); - $data = str_replace($this->badCommandChars, "", $data); - } +/** + * Determine the method to process the current command + * + * @param string $command + * @return string or false + */ + protected function _method($command) { + foreach ($this->_methodPatterns as $method => $pattern) { + if (preg_match($pattern, $command)) { + return $method; + } + } - $modelA = $tmp[1]; - $association = $tmp[2]; - $modelB = $tmp[3]; + return false; + } - // Verify that there is actually an association to unbind - $currentAssociations = $this->{$modelA}->getAssociated(); - $validCurrentAssociation = false; +/** + * Set the finiished property so that the loop in main method ends + * + * @return void + */ + protected function _exit() { + $this->_finished = true; + } - foreach ($currentAssociations as $model => $currentAssociation) { - if ($model == $modelB && $association == $currentAssociation) { - $validCurrentAssociation = true; - } - } +/** + * List all models + * + * @return void + */ + protected function _models() { + $this->out(__d('cake_console', 'Model classes:')); + $this->hr(); + foreach ($this->models as $model) { + $this->out(" - {$model}"); + } + } - if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) { - $this->{$modelA}->unbindModel(array($association => array($modelB))); - $this->out(__d('cake_console', "Removed %s association between %s and %s", - $association, $modelA, $modelB)); - } else { - $this->out(__d('cake_console', "Please verify you are using valid models, valid current association, and valid association types")); - } - break; - case (strpos($command, "->find") > 0): - // Remove any bad info - $command = strip_tags($command); - $command = str_replace($this->badCommandChars, "", $command); - - // Do we have a valid model? - list($modelToCheck, $tmp) = explode('->', $command); - - if ($this->_isValidModel($modelToCheck)) { - $findCommand = "\$data = \$this->$command;"; - //@codingStandardsIgnoreStart - @eval($findCommand); - //@codingStandardsIgnoreEnd - - if (is_array($data)) { - foreach ($data as $idx => $results) { - if (is_numeric($idx)) { // findAll() output - foreach ($results as $modelName => $result) { - $this->out("$modelName"); - - foreach ($result as $field => $value) { - if (is_array($value)) { - foreach ($value as $field2 => $value2) { - $this->out("\t$field2: $value2"); - } - - $this->out(); - } else { - $this->out("\t$field: $value"); - } - } - } - } else { // find() output - $this->out($idx); - - foreach ($results as $field => $value) { - if (is_array($value)) { - foreach ($value as $field2 => $value2) { - $this->out("\t$field2: $value2"); - } - - $this->out(); - } else { - $this->out("\t$field: $value"); - } +/** + * Bind an association + * + * @param mixed $command + * @return void + */ + protected function _bind($command) { + preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); + + foreach ($tmp as $data) { + $data = strip_tags($data); + $data = str_replace($this->badCommandChars, "", $data); + } + + $modelA = $tmp[1]; + $association = $tmp[2]; + $modelB = $tmp[3]; + + if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) { + $this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false); + $this->out(__d('cake_console', "Created %s association between %s and %s", + $association, $modelA, $modelB)); + } else { + $this->out(__d('cake_console', "Please verify you are using valid models and association types")); + } + } + +/** + * Unbind an association + * + * @param mixed $command + * @return void + */ + protected function _unbind($command) { + preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); + + foreach ($tmp as $data) { + $data = strip_tags($data); + $data = str_replace($this->badCommandChars, "", $data); + } + + $modelA = $tmp[1]; + $association = $tmp[2]; + $modelB = $tmp[3]; + + // Verify that there is actually an association to unbind + $currentAssociations = $this->{$modelA}->getAssociated(); + $validCurrentAssociation = false; + + foreach ($currentAssociations as $model => $currentAssociation) { + if ($model == $modelB && $association == $currentAssociation) { + $validCurrentAssociation = true; + } + } + + if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) { + $this->{$modelA}->unbindModel(array($association => array($modelB))); + $this->out(__d('cake_console', "Removed %s association between %s and %s", + $association, $modelA, $modelB)); + } else { + $this->out(__d('cake_console', "Please verify you are using valid models, valid current association, and valid association types")); + } + } + +/** + * Perform a find + * + * @param mixed $command + * @return void + */ + protected function _find($command) { + $command = strip_tags($command); + $command = str_replace($this->badCommandChars, "", $command); + + // Do we have a valid model? + list($modelToCheck, $tmp) = explode('->', $command); + + if ($this->_isValidModel($modelToCheck)) { + $findCommand = "\$data = \$this->$command;"; + //@codingStandardsIgnoreStart + @eval($findCommand); + //@codingStandardsIgnoreEnd + + if (is_array($data)) { + foreach ($data as $idx => $results) { + if (is_numeric($idx)) { // findAll() output + foreach ($results as $modelName => $result) { + $this->out("$modelName"); + + foreach ($result as $field => $value) { + if (is_array($value)) { + foreach ($value as $field2 => $value2) { + $this->out("\t$field2: $value2"); } + + $this->out(); + } else { + $this->out("\t$field: $value"); } } - } else { - $this->out(); - $this->out(__d('cake_console', "No result set found")); } - } else { - $this->out(__d('cake_console', "%s is not a valid model", $modelToCheck)); - } + } else { // find() output + $this->out($idx); - break; - case (strpos($command, '->save') > 0): - // Validate the model we're trying to save here - $command = strip_tags($command); - $command = str_replace($this->badCommandChars, "", $command); - list($modelToSave, $tmp) = explode("->", $command); - - if ($this->_isValidModel($modelToSave)) { - // Extract the array of data we are trying to build - list(, $data) = explode("->save", $command); - $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); - $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; - //@codingStandardsIgnoreStart - @eval($saveCommand); - //@codingStandardsIgnoreEnd - $this->out(__d('cake_console', 'Saved record for %s', $modelToSave)); - } - break; - case preg_match("/^(\w+) columns/", $command, $tmp): - $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1])); - - if ($this->_isValidModel($modelToCheck)) { - // Get the column info for this model - $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; - //@codingStandardsIgnoreStart - @eval($fieldsCommand); - //@codingStandardsIgnoreEnd - - if (is_array($data)) { - foreach ($data as $field => $type) { - $this->out("\t{$field}: {$type}"); + foreach ($results as $field => $value) { + if (is_array($value)) { + foreach ($value as $field2 => $value2) { + $this->out("\t$field2: $value2"); + } + + $this->out(); + } else { + $this->out("\t$field: $value"); } } - } else { - $this->out(__d('cake_console', "Please verify that you selected a valid model")); - } - break; - case preg_match("/^routes\s+reload/i", $command, $tmp): - if (!$this->_loadRoutes()) { - $this->err(__d('cake_console', "There was an error loading the routes config. Please check that the file exists and is free of parse errors.")); - break; } - $this->out(__d('cake_console', "Routes configuration reloaded, %d routes connected", count(Router::$routes))); - break; - case preg_match("/^routes\s+show/i", $command, $tmp): - $this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true)); - break; - case (preg_match("/^route\s+(\(.*\))$/i", $command, $tmp) == true): - //@codingStandardsIgnoreStart - if ($url = eval('return array' . $tmp[1] . ';')) { - //@codingStandardsIgnoreEnd - $this->out(Router::url($url)); - } - break; - case preg_match("/^route\s+(.*)/i", $command, $tmp): - $this->out(var_export(Router::parse($tmp[1]), true)); - break; - default: - $this->out(__d('cake_console', "Invalid command")); - $this->out(); + } + } else { + $this->out(); + $this->out(__d('cake_console', "No result set found")); } - $command = ''; + } else { + $this->out(__d('cake_console', "%s is not a valid model", $modelToCheck)); + } + } + +/** + * Save a record + * + * @param mixed $command + * @return void + */ + protected function _save($command) { + // Validate the model we're trying to save here + $command = strip_tags($command); + $command = str_replace($this->badCommandChars, "", $command); + list($modelToSave, $tmp) = explode("->", $command); + + if ($this->_isValidModel($modelToSave)) { + // Extract the array of data we are trying to build + list(, $data) = explode("->save", $command); + $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); + $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; + //@codingStandardsIgnoreStart + @eval($saveCommand); + //@codingStandardsIgnoreEnd + $this->out(__d('cake_console', 'Saved record for %s', $modelToSave)); } } +/** + * Show the columns for a model + * + * @param mixed $command + * @return void + */ + protected function _columns($command) { + preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); + + $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1])); + + if ($this->_isValidModel($modelToCheck)) { + // Get the column info for this model + $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; + //@codingStandardsIgnoreStart + @eval($fieldsCommand); + //@codingStandardsIgnoreEnd + + if (is_array($data)) { + foreach ($data as $field => $type) { + $this->out("\t{$field}: {$type}"); + } + } + } else { + $this->out(__d('cake_console', "Please verify that you selected a valid model")); + } + } + +/** + * Reload route definitions + * + * @return void + */ + protected function _routesReload() { + if (!$this->_loadRoutes()) { + $this->err(__d('cake_console', "There was an error loading the routes config. Please check that the file exists and is free of parse errors.")); + break; + } + $this->out(__d('cake_console', "Routes configuration reloaded, %d routes connected", count(Router::$routes))); + } + +/** + * Show all routes + * + * @return void + */ + protected function _routesShow() { + $this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true)); + } + +/** + * Parse an array url and show the equivalent url as a string + * + * @param mixed $command + * @return void + */ + protected function _routeToString($command) { + preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); + + //@codingStandardsIgnoreStart + if ($url = eval('return array' . $tmp[1] . ';')) { + //@codingStandardsIgnoreEnd + $this->out(Router::url($url)); + } + } + +/** + * Parse a string url and show as an array + * + * @param mixed $command + * @return void + */ + protected function _routeToArray($command) { + preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); + + $this->out(var_export(Router::parse($tmp[1]), true)); + } + /** * Tells if the specified model is included in the list of available models * diff --git a/lib/Cake/Console/Command/ServerShell.php b/lib/Cake/Console/Command/ServerShell.php index b850a6d31..492b7d48b 100755 --- a/lib/Cake/Console/Command/ServerShell.php +++ b/lib/Cake/Console/Command/ServerShell.php @@ -129,8 +129,8 @@ public function main() { $command = sprintf("php -S %s:%d -t %s %s", $this->_host, $this->_port, - $this->_documentRoot, - $this->_documentRoot . '/index.php' + escapeshellarg($this->_documentRoot), + escapeshellarg($this->_documentRoot . '/index.php') ); $port = ($this->_port == self::DEFAULT_PORT) ? '' : ':' . $this->_port; diff --git a/lib/Cake/Console/Command/Task/ControllerTask.php b/lib/Cake/Console/Command/Task/ControllerTask.php index aae11519b..8733f0606 100755 --- a/lib/Cake/Console/Command/Task/ControllerTask.php +++ b/lib/Cake/Console/Command/Task/ControllerTask.php @@ -328,6 +328,11 @@ public function bake($controllerName, $actions = '', $helpers = null, $component 'plugin' => $this->plugin, 'pluginPath' => empty($this->plugin) ? '' : $this->plugin . '.' )); + + if (!in_array('Paginator', (array)$components)) { + $components[] = 'Paginator'; + } + $this->Template->set(compact('controllerName', 'actions', 'helpers', 'components', 'isScaffold')); $contents = $this->Template->generate('classes', 'controller'); @@ -370,10 +375,11 @@ public function doHelpers() { * @return array Components the user wants to use. */ public function doComponents() { - return $this->_doPropertyChoices( - __d('cake_console', "Would you like this controller to use any components?"), + $components = array('Paginator'); + return array_merge($components, $this->_doPropertyChoices( + __d('cake_console', "Would you like this controller to use other components\nbesides PaginatorComponent?"), __d('cake_console', "Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'") - ); + )); } /** diff --git a/lib/Cake/Console/Command/TestsuiteShell.php b/lib/Cake/Console/Command/TestsuiteShell.php index 860662b9e..05572c91c 100755 --- a/lib/Cake/Console/Command/TestsuiteShell.php +++ b/lib/Cake/Console/Command/TestsuiteShell.php @@ -42,8 +42,7 @@ public function getOptionParser() { $parser = parent::getOptionParser(); $parser->description(array( __d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'), - __d('cake_console', 'This shell is for backwards-compatibility only'), - __d('cake_console', 'use the test shell instead') + __d('cake_console', "This shell is for backwards-compatibility only\nuse the test shell instead"), )); return $parser; diff --git a/lib/Cake/Console/ShellDispatcher.php b/lib/Cake/Console/ShellDispatcher.php index 0be88001c..4b99c5e6f 100755 --- a/lib/Cake/Console/ShellDispatcher.php +++ b/lib/Cake/Console/ShellDispatcher.php @@ -122,8 +122,10 @@ protected function _bootstrap() { define('ROOT', $this->params['root']); define('APP_DIR', $this->params['app']); define('APP', $this->params['working'] . DS); - define('WWW_ROOT', APP . $this->params['webroot'] . DS); - if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp') && !defined('TMP')) { + if (!defined('WWW_ROOT')) { + define('WWW_ROOT', APP . $this->params['webroot'] . DS); + } + if (!defined('TMP') && !is_dir(APP . 'tmp')) { define('TMP', CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'tmp' . DS); } $boot = file_exists(ROOT . DS . APP_DIR . DS . 'Config' . DS . 'bootstrap.php'); @@ -137,9 +139,9 @@ protected function _bootstrap() { $this->setErrorHandlers(); if (!defined('FULL_BASE_URL')) { - $url = Configure::read('App.fullBaseURL'); + $url = Configure::read('App.fullBaseUrl'); define('FULL_BASE_URL', $url ? $url : 'http://localhost'); - Configure::write('App.fullBaseURL', FULL_BASE_URL); + Configure::write('App.fullBaseUrl', FULL_BASE_URL); } return true; diff --git a/lib/Cake/Console/Templates/default/actions/controller_actions.ctp b/lib/Cake/Console/Templates/default/actions/controller_actions.ctp index 02249c1b0..e3df02186 100755 --- a/lib/Cake/Console/Templates/default/actions/controller_actions.ctp +++ b/lib/Cake/Console/Templates/default/actions/controller_actions.ctp @@ -26,7 +26,7 @@ */ public function index() { $this->->recursive = 0; - $this->set('', $this->paginate()); + $this->set('', $this->Paginator->paginate()); } /** @@ -56,7 +56,7 @@ if ($this->->save($this->request->data)) { $this->Session->setFlash(__('The has been saved')); - $this->redirect(array('action' => 'index')); + return $this->redirect(array('action' => 'index')); $this->flash(__(' saved.'), array('action' => 'index')); @@ -99,7 +99,7 @@ if ($this->->save($this->request->data)) { $this->Session->setFlash(__('The has been saved')); - $this->redirect(array('action' => 'index')); + return $this->redirect(array('action' => 'index')); $this->flash(__('The has been saved.'), array('action' => 'index')); @@ -145,7 +145,7 @@ if ($this->->delete()) { $this->Session->setFlash(__(' deleted')); - $this->redirect(array('action' => 'index')); + return $this->redirect(array('action' => 'index')); $this->flash(__(' deleted'), array('action' => 'index')); @@ -155,5 +155,5 @@ $this->flash(__(' was not deleted'), array('action' => 'index')); - $this->redirect(array('action' => 'index')); + return $this->redirect(array('action' => 'index')); } diff --git a/lib/Cake/Console/Templates/skel/Config/bootstrap.php b/lib/Cake/Console/Templates/skel/Config/bootstrap.php index fe1ec3840..99375397e 100755 --- a/lib/Cake/Console/Templates/skel/Config/bootstrap.php +++ b/lib/Cake/Console/Templates/skel/Config/bootstrap.php @@ -22,24 +22,24 @@ * The settings below can be used to set additional paths to models, views and controllers. * * App::build(array( - * 'Model' => array('/path/to/models', '/next/path/to/models'), - * 'Model/Behavior' => array('/path/to/behaviors', '/next/path/to/behaviors'), - * 'Model/Datasource' => array('/path/to/datasources', '/next/path/to/datasources'), - * 'Model/Datasource/Database' => array('/path/to/databases', '/next/path/to/database'), - * 'Model/Datasource/Session' => array('/path/to/sessions', '/next/path/to/sessions'), - * 'Controller' => array('/path/to/controllers', '/next/path/to/controllers'), - * 'Controller/Component' => array('/path/to/components', '/next/path/to/components'), - * 'Controller/Component/Auth' => array('/path/to/auths', '/next/path/to/auths'), - * 'Controller/Component/Acl' => array('/path/to/acls', '/next/path/to/acls'), - * 'View' => array('/path/to/views', '/next/path/to/views'), - * 'View/Helper' => array('/path/to/helpers', '/next/path/to/helpers'), - * 'Console' => array('/path/to/consoles', '/next/path/to/consoles'), - * 'Console/Command' => array('/path/to/commands', '/next/path/to/commands'), - * 'Console/Command/Task' => array('/path/to/tasks', '/next/path/to/tasks'), - * 'Lib' => array('/path/to/libs', '/next/path/to/libs'), - * 'Locale' => array('/path/to/locales', '/next/path/to/locales'), - * 'Vendor' => array('/path/to/vendors', '/next/path/to/vendors'), - * 'Plugin' => array('/path/to/plugins', '/next/path/to/plugins'), + * 'Model' => array('/path/to/models/', '/next/path/to/models/'), + * 'Model/Behavior' => array('/path/to/behaviors/', '/next/path/to/behaviors/'), + * 'Model/Datasource' => array('/path/to/datasources/', '/next/path/to/datasources/'), + * 'Model/Datasource/Database' => array('/path/to/databases/', '/next/path/to/database/'), + * 'Model/Datasource/Session' => array('/path/to/sessions/', '/next/path/to/sessions/'), + * 'Controller' => array('/path/to/controllers/', '/next/path/to/controllers/'), + * 'Controller/Component' => array('/path/to/components/', '/next/path/to/components/'), + * 'Controller/Component/Auth' => array('/path/to/auths/', '/next/path/to/auths/'), + * 'Controller/Component/Acl' => array('/path/to/acls/', '/next/path/to/acls/'), + * 'View' => array('/path/to/views/', '/next/path/to/views/'), + * 'View/Helper' => array('/path/to/helpers/', '/next/path/to/helpers/'), + * 'Console' => array('/path/to/consoles/', '/next/path/to/consoles/'), + * 'Console/Command' => array('/path/to/commands/', '/next/path/to/commands/'), + * 'Console/Command/Task' => array('/path/to/tasks/', '/next/path/to/tasks/'), + * 'Lib' => array('/path/to/libs/', '/next/path/to/libs/'), + * 'Locale' => array('/path/to/locales/', '/next/path/to/locales/'), + * 'Vendor' => array('/path/to/vendors/', '/next/path/to/vendors/'), + * 'Plugin' => array('/path/to/plugins/', '/next/path/to/plugins/'), * )); * */ diff --git a/lib/Cake/Console/Templates/skel/Config/core.php b/lib/Cake/Console/Templates/skel/Config/core.php index bda4331dc..22a80b71f 100755 --- a/lib/Cake/Console/Templates/skel/Config/core.php +++ b/lib/Cake/Console/Templates/skel/Config/core.php @@ -106,7 +106,25 @@ * will override the automatic detection of full base URL and can be * useful when generating links from the CLI (e.g. sending emails) */ - //Configure::write('App.fullBaseURL', 'http://example.com'); + //Configure::write('App.fullBaseUrl', 'http://example.com'); + +/** + * Web path to the public images directory under webroot. + * If not set defaults to 'img/' + */ + //Configure::write('App.imageBaseUrl', 'img/'); + +/** + * Web path to the CSS files directory under webroot. + * If not set defaults to 'css/' + */ + //Configure::write('App.cssBaseUrl', 'css/'); + +/** + * Web path to the js files directory under webroot. + * If not set defaults to 'js/' + */ + //Configure::write('App.jsBaseUrl', 'js/'); /** * Uncomment the define below to use CakePHP prefix routes. diff --git a/lib/Cake/Console/Templates/skel/View/Pages/home.ctp b/lib/Cake/Console/Templates/skel/View/Pages/home.ctp index e6990b3a3..673bb4c49 100755 --- a/lib/Cake/Console/Templates/skel/View/Pages/home.ctp +++ b/lib/Cake/Console/Templates/skel/View/Pages/home.ctp @@ -198,7 +198,7 @@ You can also add some CSS styles for your pages at: APP/webroot/css.');

    • -
    • +
    • CakePHP
    • diff --git a/lib/Cake/Console/Templates/skel/webroot/index.php b/lib/Cake/Console/Templates/skel/webroot/index.php index b4d15cc13..c17e95625 100755 --- a/lib/Cake/Console/Templates/skel/webroot/index.php +++ b/lib/Cake/Console/Templates/skel/webroot/index.php @@ -70,8 +70,7 @@ // for built-in server if (php_sapi_name() === 'cli-server') { - $uri = str_replace($_SERVER['SCRIPT_FILENAME'], WWW_ROOT, ''); - if ($_SERVER['REQUEST_URI'] !== '/' && file_exists(WWW_ROOT . $uri)) { + if ($_SERVER['REQUEST_URI'] !== '/' && file_exists(WWW_ROOT . $_SERVER['PHP_SELF'])) { return false; } $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); diff --git a/lib/Cake/Controller/Component/AuthComponent.php b/lib/Cake/Controller/Component/AuthComponent.php index a9c948bb2..b198ee8c7 100755 --- a/lib/Cake/Controller/Component/AuthComponent.php +++ b/lib/Cake/Controller/Component/AuthComponent.php @@ -733,7 +733,7 @@ public function redirectUrl($url = null) { $redir = '/'; } if (is_array($redir)) { - return Router::url($redir); + return Router::url($redir + array('base' => false)); } return $redir; } diff --git a/lib/Cake/Controller/Component/CookieComponent.php b/lib/Cake/Controller/Component/CookieComponent.php index afe48b787..7ae79afeb 100755 --- a/lib/Cake/Controller/Component/CookieComponent.php +++ b/lib/Cake/Controller/Component/CookieComponent.php @@ -421,7 +421,7 @@ protected function _write($name, $value) { 'httpOnly' => $this->httpOnly )); - if (!is_null($this->_reset)) { + if (!empty($this->_reset)) { $this->_expires = $this->_reset; $this->_reset = null; } diff --git a/lib/Cake/Controller/Component/PaginatorComponent.php b/lib/Cake/Controller/Component/PaginatorComponent.php index e5d5fb425..0acba7bdf 100755 --- a/lib/Cake/Controller/Component/PaginatorComponent.php +++ b/lib/Cake/Controller/Component/PaginatorComponent.php @@ -121,7 +121,7 @@ public function __construct(ComponentCollection $collection, $settings = array() * @param Model|string $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel') * @param string|array $scope Additional find conditions to use while paginating * @param array $whitelist List of allowed fields for ordering. This allows you to prevent ordering - * on non-indexed, or undesirable columns. See PaginatorComponent::validateSort() for additional details + * on non-indexed, or undesirable columns. See PaginatorComponent::validateSort() for additional details * on how the whitelisting and sort field validation works. * @return array Model query results * @throws MissingModelException diff --git a/lib/Cake/Controller/Component/SecurityComponent.php b/lib/Cake/Controller/Component/SecurityComponent.php index 6864605f0..2b1c69b93 100755 --- a/lib/Cake/Controller/Component/SecurityComponent.php +++ b/lib/Cake/Controller/Component/SecurityComponent.php @@ -233,7 +233,7 @@ public function startup(Controller $controller) { ); if ($this->_action == $this->blackHoleCallback) { - return $this->blackhole($controller, 'auth'); + return $this->blackHole($controller, 'auth'); } if (!in_array($this->_action, (array)$this->unlockedActions) && $isPost && $isNotRequestAction) { diff --git a/lib/Cake/Controller/ComponentCollection.php b/lib/Cake/Controller/ComponentCollection.php index caad0f839..8d92ff657 100755 --- a/lib/Cake/Controller/ComponentCollection.php +++ b/lib/Cake/Controller/ComponentCollection.php @@ -54,6 +54,16 @@ public function init(Controller $Controller) { } } +/** + * Set the controller associated with the collection. + * + * @param Controller $Controller Controller to set + * @return void + */ + public function setController(Controller $Controller) { + $this->_Controller = $Controller; + } + /** * Get the controller associated with the collection. * @@ -84,7 +94,7 @@ public function getController() { * @throws MissingComponentException when the component could not be found */ public function load($component, $settings = array()) { - if (is_array($settings) && isset($settings['className'])) { + if (isset($settings['className'])) { $alias = $component; $component = $settings['className']; } diff --git a/lib/Cake/Controller/Controller.php b/lib/Cake/Controller/Controller.php index aefc83f01..edf05e425 100755 --- a/lib/Cake/Controller/Controller.php +++ b/lib/Cake/Controller/Controller.php @@ -347,7 +347,7 @@ public function __construct($request = null, $response = null) { * Lazy loads models using the loadModel() method if declared in $uses * * @param string $name - * @return void + * @return boolean */ public function __isset($name) { switch ($name) { @@ -384,8 +384,8 @@ public function __isset($name) { * Provides backwards compatibility access to the request object properties. * Also provides the params alias. * - * @param string $name - * @return void + * @param string $name The name of the requested value + * @return mixed The requested value for valid variables/aliases else null */ public function __get($name) { switch ($name) { @@ -422,15 +422,19 @@ public function __set($name, $value) { case 'here': case 'webroot': case 'data': - return $this->request->{$name} = $value; + $this->request->{$name} = $value; + return; case 'action': - return $this->request->params['action'] = $value; + $this->request->params['action'] = $value; + return; case 'params': - return $this->request->params = $value; + $this->request->params = $value; + return; case 'paginate': - return $this->Components->load('Paginator')->settings = $value; + $this->Components->load('Paginator')->settings = $value; + return; } - return $this->{$name} = $value; + $this->{$name} = $value; } /** @@ -714,7 +718,7 @@ public function httpCodes($code = null) { * * @param string $modelClass Name of model class to load * @param integer|string $id Initial ID the instanced model class should have - * @return mixed true when single model found and instance created, error returned if model not found. + * @return bool True if the model was found * @throws MissingModelException if the model class cannot be found. */ public function loadModel($modelClass = null, $id = null) { diff --git a/lib/Cake/Core/Configure.php b/lib/Cake/Core/Configure.php index 8a9dcdef9..5e3f1cce9 100755 --- a/lib/Cake/Core/Configure.php +++ b/lib/Cake/Core/Configure.php @@ -67,19 +67,13 @@ class Configure { */ public static function bootstrap($boot = true) { if ($boot) { - self::write('App', array( - 'base' => false, - 'baseUrl' => false, - 'dir' => APP_DIR, - 'webroot' => WEBROOT_DIR, - 'www_root' => WWW_ROOT - )); + self::_appDefaults(); if (!include APP . 'Config' . DS . 'core.php') { trigger_error(__d('cake_dev', "Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", APP . 'Config' . DS), E_USER_ERROR); } - App::$bootstrapping = false; App::init(); + App::$bootstrapping = false; App::build(); $exception = array( @@ -109,6 +103,20 @@ class_exists('String'); } } +/** + * Set app's default configs + * @return void + */ + protected static function _appDefaults() { + self::write('App', (array)self::read('App') + array( + 'base' => false, + 'baseUrl' => false, + 'dir' => APP_DIR, + 'webroot' => WEBROOT_DIR, + 'www_root' => WWW_ROOT + )); + } + /** * Used to store a dynamic variable in Configure. * diff --git a/lib/Cake/Core/Object.php b/lib/Cake/Core/Object.php index 5af47e782..053c68f7c 100755 --- a/lib/Cake/Core/Object.php +++ b/lib/Cake/Core/Object.php @@ -88,8 +88,8 @@ public function requestAction($url, $extra = array()) { $data = isset($extra['data']) ? $extra['data'] : null; unset($extra['data']); - if (is_string($url) && strpos($url, Router::baseURL()) === 0) { - $url = Router::normalize(str_replace(Router::baseURL(), '', $url)); + if (is_string($url) && strpos($url, Router::fullBaseUrl()) === 0) { + $url = Router::normalize(str_replace(Router::fullBaseUrl(), '', $url)); } if (is_string($url)) { $request = new CakeRequest($url); diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index f81a11d3a..04a8ae795 100755 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -124,12 +124,14 @@ public static function getInstance() { * * @param string $singular String to translate * @param string $plural Plural string (if any) - * @param string $domain Domain The domain of the translation. Domains are often used by plugin translations + * @param string $domain Domain The domain of the translation. Domains are often used by plugin translations. + * If null, the default domain will be used. * @param string $category Category The integer value of the category to use. * @param integer $count Count Count is used with $plural to choose the correct plural form. * @param string $language Language to translate string to. - * If null it checks for language in session followed by Config.language configuration variable. + * If null it checks for language in session followed by Config.language configuration variable. * @return string translated string. + * @throws CakeException When '' is provided as a domain. */ public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null, $language = null) { $_this = I18n::getInstance(); @@ -162,6 +164,9 @@ public static function translate($singular, $plural = null, $domain = null, $cat if (is_null($domain)) { $domain = self::$defaultDomain; } + if ($domain === '') { + throw new CakeException(__d('cake_dev', 'You cannot use "" as a domain.')); + } $_this->domain = $domain . '_' . $_this->l10n->lang; diff --git a/lib/Cake/I18n/L10n.php b/lib/Cake/I18n/L10n.php index f31dca925..aba0afbf7 100755 --- a/lib/Cake/I18n/L10n.php +++ b/lib/Cake/I18n/L10n.php @@ -39,7 +39,7 @@ class L10n { * * @var array */ - public $languagePath = array('eng'); + public $languagePath = array('en_us', 'eng'); /** * ISO 639-3 for current locale @@ -56,9 +56,11 @@ class L10n { public $locale = 'en_us'; /** - * Default ISO 639-3 language. + * Default language. * - * DEFAULT_LANGUAGE is defined in an application this will be set as a fall back + * If config value 'Config.language' is set in an application this will be set + * as a fall back else if DEFAULT_LANGUAGE it defined it will be used. + * Constant DEFAULT_LANGUAGE has been deprecated in 2.4 * * @var string */ @@ -78,13 +80,6 @@ class L10n { */ public $direction = 'ltr'; -/** - * Set to true if a locale is found - * - * @var string - */ - public $found = false; - /** * Maps ISO 639-3 to I10n::_l10nCatalog * The terminological codes (first one per language) should be used if possible. @@ -337,6 +332,10 @@ public function __construct() { if (defined('DEFAULT_LANGUAGE')) { $this->default = DEFAULT_LANGUAGE; } + $default = Configure::read('Config.language'); + if ($default) { + $this->default = $default; + } } /** @@ -360,44 +359,44 @@ public function get($language = null) { /** * Sets the class vars to correct values for $language. - * If $language is null it will use the DEFAULT_LANGUAGE if defined + * If $language is null it will use the L10n::$default if defined * - * @param string $language Language (if null will use DEFAULT_LANGUAGE if defined) + * @param string $language Language (if null will use L10n::$default if defined) * @return mixed */ protected function _setLanguage($language = null) { - $langKey = null; - if ($language !== null && isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) { - $langKey = $this->_l10nMap[$language]; - } elseif ($language !== null && isset($this->_l10nCatalog[$language])) { - $langKey = $language; - } elseif (defined('DEFAULT_LANGUAGE')) { - $langKey = $language = DEFAULT_LANGUAGE; + $catalog = false; + if ($language !== null) { + $catalog = $this->catalog($language); } - if ($langKey !== null && isset($this->_l10nCatalog[$langKey])) { - $this->language = $this->_l10nCatalog[$langKey]['language']; - $this->languagePath = array( - $this->_l10nCatalog[$langKey]['locale'], - $this->_l10nCatalog[$langKey]['localeFallback'] - ); + if (!$catalog && $this->default) { + $language = $this->default; + $catalog = $this->catalog($language); + } + + if ($catalog) { + $this->language = $catalog['language']; + $this->languagePath = array_unique(array( + $catalog['locale'], + $catalog['localeFallback'] + )); $this->lang = $language; - $this->locale = $this->_l10nCatalog[$langKey]['locale']; - $this->charset = $this->_l10nCatalog[$langKey]['charset']; - $this->direction = $this->_l10nCatalog[$langKey]['direction']; - } else { + $this->locale = $catalog['locale']; + $this->charset = $catalog['charset']; + $this->direction = $catalog['direction']; + } elseif ($language) { $this->lang = $language; $this->languagePath = array($language); } - if ($this->default) { - if (isset($this->_l10nMap[$this->default]) && isset($this->_l10nCatalog[$this->_l10nMap[$this->default]])) { - $this->languagePath[] = $this->_l10nCatalog[$this->_l10nMap[$this->default]]['localeFallback']; - } elseif (isset($this->_l10nCatalog[$this->default])) { - $this->languagePath[] = $this->_l10nCatalog[$this->default]['localeFallback']; + if ($this->default && $language !== $this->default) { + $catalog = $this->catalog($this->default); + $fallback = $catalog['localeFallback']; + if (!in_array($fallback, $this->languagePath)) { + $this->languagePath[] = $fallback; } } - $this->found = true; if (Configure::read('Config.language') === null) { Configure::write('Config.language', $this->lang); @@ -419,7 +418,8 @@ protected function _autoLanguage() { if (isset($this->_l10nCatalog[$langKey])) { $this->_setLanguage($langKey); return true; - } elseif (strpos($langKey, '-') !== false) { + } + if (strpos($langKey, '-') !== false) { $langKey = substr($langKey, 0, 2); if (isset($this->_l10nCatalog[$langKey])) { $this->_setLanguage($langKey); @@ -446,10 +446,12 @@ public function map($mixed = null) { } } return $result; - } elseif (is_string($mixed)) { + } + if (is_string($mixed)) { if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap)) { return array_search($mixed, $this->_l10nMap); - } elseif (isset($this->_l10nMap[$mixed])) { + } + if (isset($this->_l10nMap[$mixed])) { return $this->_l10nMap[$mixed]; } return false; @@ -473,10 +475,12 @@ public function catalog($language = null) { } } return $result; - } elseif (is_string($language)) { + } + if (is_string($language)) { if (isset($this->_l10nCatalog[$language])) { return $this->_l10nCatalog[$language]; - } elseif (isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) { + } + if (isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) { return $this->_l10nCatalog[$this->_l10nMap[$language]]; } return false; diff --git a/lib/Cake/Log/Engine/FileLog.php b/lib/Cake/Log/Engine/FileLog.php index 489168467..071f12f8d 100755 --- a/lib/Cake/Log/Engine/FileLog.php +++ b/lib/Cake/Log/Engine/FileLog.php @@ -104,6 +104,10 @@ public function config($config = array()) { if (!empty($config['path'])) { $this->_path = $config['path']; } + if (Configure::read('debug') && !is_dir($this->_path)) { + mkdir($this->_path, 0775, true); + } + if (!empty($config['file'])) { $this->_file = $config['file']; if (substr($this->_file, -4) !== '.log') { diff --git a/lib/Cake/Model/AclNode.php b/lib/Cake/Model/AclNode.php index 6f3adc990..453b6f794 100755 --- a/lib/Cake/Model/AclNode.php +++ b/lib/Cake/Model/AclNode.php @@ -85,7 +85,7 @@ public function node($ref = null) { 'joins' => array(array( 'table' => $table, 'alias' => "{$type}0", - 'type' => 'LEFT', + 'type' => 'INNER', 'conditions' => array("{$type}0.alias" => $start) )), 'order' => $db->name("{$type}.lft") . ' DESC' @@ -97,7 +97,7 @@ public function node($ref = null) { $queryData['joins'][] = array( 'table' => $table, 'alias' => "{$type}{$i}", - 'type' => 'LEFT', + 'type' => 'INNER', 'conditions' => array( $db->name("{$type}{$i}.lft") . ' > ' . $db->name("{$type}{$j}.lft"), $db->name("{$type}{$i}.rght") . ' < ' . $db->name("{$type}{$j}.rght"), diff --git a/lib/Cake/Model/BehaviorCollection.php b/lib/Cake/Model/BehaviorCollection.php index 963ece11c..ab812d9d9 100755 --- a/lib/Cake/Model/BehaviorCollection.php +++ b/lib/Cake/Model/BehaviorCollection.php @@ -103,7 +103,7 @@ public function attach($behavior, $config = array()) { * @throws MissingBehaviorException when a behavior could not be found. */ public function load($behavior, $config = array()) { - if (is_array($config) && isset($config['className'])) { + if (isset($config['className'])) { $alias = $behavior; $behavior = $config['className']; } diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index ff5af1816..db7b0b3b4 100755 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -1637,9 +1637,10 @@ public function save($data = null, $validate = true, $fieldList = array()) { } if (!empty($options['fieldList'])) { - $this->whitelist = $options['fieldList']; if (!empty($options['fieldList'][$this->alias]) && is_array($options['fieldList'][$this->alias])) { $this->whitelist = $options['fieldList'][$this->alias]; + } elseif (Hash::dimensions($options['fieldList']) < 2) { + $this->whitelist = $options['fieldList']; } } elseif ($options['fieldList'] === null) { $this->whitelist = array(); @@ -2360,7 +2361,10 @@ protected function _addToWhiteList($key, $options) { $options['fieldList'][$this->alias][] = $key; return $options; } - if (!empty($options['fieldList']) && is_array($options['fieldList'])) { + if (!empty($options['fieldList']) && + is_array($options['fieldList']) && + Hash::dimensions($options['fieldList']) < 2 + ) { $options['fieldList'][] = $key; } return $options; @@ -2723,7 +2727,7 @@ public function find($type = 'first', $query = array()) { * * Model::_readDataSource() is used by all find() calls to read from the data source and can be overloaded to allow * caching of datasource calls. - * + * * {{{ * protected function _readDataSource($type, $query) { * $cacheName = md5(json_encode($query)); @@ -2737,7 +2741,7 @@ public function find($type = 'first', $query = array()) { * return $results; * } * }}} - * + * * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) * @return array diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index cddf27b5d..f85133881 100755 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -21,6 +21,7 @@ */ App::uses('CakeValidationSet', 'Model/Validator'); +App::uses('Hash', 'Utility'); /** * ModelValidator object encapsulates all methods related to data validations for a model @@ -394,11 +395,11 @@ protected function _validationList($fieldList = array()) { } unset($fieldList); - $validateList = array(); - if (empty($whitelist)) { + if (empty($whitelist) || Hash::dimensions($whitelist) > 1) { return $this->_fields; } + $validateList = array(); $this->validationErrors = array(); foreach ((array)$whitelist as $f) { if (!empty($this->_fields[$f])) { diff --git a/lib/Cake/Network/CakeRequest.php b/lib/Cake/Network/CakeRequest.php index 14c0a1cb1..fb7ccc04d 100755 --- a/lib/Cake/Network/CakeRequest.php +++ b/lib/Cake/Network/CakeRequest.php @@ -238,7 +238,7 @@ protected function _url() { if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) { $uri = $_SERVER['REQUEST_URI']; } else { - $uri = substr($_SERVER['REQUEST_URI'], strlen(Configure::read('App.fullBaseURL'))); + $uri = substr($_SERVER['REQUEST_URI'], strlen(Configure::read('App.fullBaseUrl'))); } } elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) { $uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']); @@ -424,7 +424,7 @@ public function referer($local = false) { $ref = $forwarded; } - $base = Configure::read('App.fullBaseURL') . $this->webroot; + $base = Configure::read('App.fullBaseUrl') . $this->webroot; if (!empty($ref) && !empty($base)) { if ($local && strpos($ref, $base) === 0) { $ref = substr($ref, strlen($base)); diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index ab3d36542..dda347315 100755 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -416,8 +416,10 @@ public function send() { $this->_setContent(); $this->_setContentLength(); $this->_setContentType(); - foreach ($this->_headers as $header => $value) { - $this->_sendHeader($header, $value); + foreach ($this->_headers as $header => $values) { + foreach ((array)$values as $value) { + $this->_sendHeader($header, $value); + } } if ($this->_file) { $this->_sendFile($this->_file, $this->_fileRange); @@ -556,31 +558,23 @@ protected function _sendContent($content) { * @param string|array $header. An array of header strings or a single header string * - an associative array of "header name" => "header value" is also accepted * - an array of string headers is also accepted - * @param string $value. The header value. + * @param string|array $value. The header value(s) * @return array list of headers to be sent */ public function header($header = null, $value = null) { if (is_null($header)) { return $this->_headers; } - if (is_array($header)) { - foreach ($header as $h => $v) { - if (is_numeric($h)) { - $this->header($v); - continue; - } - $this->_headers[$h] = trim($v); + $headers = is_array($header) ? $header : array($header => $value); + foreach ($headers as $header => $value) { + if (is_numeric($header)) { + list($header, $value) = array($value, null); } - return $this->_headers; - } - - if (!is_null($value)) { - $this->_headers[$header] = $value; - return $this->_headers; + if (is_null($value)) { + list($header, $value) = explode(':', $header, 2); + } + $this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value); } - - list($header, $value) = explode(':', $header, 2); - $this->_headers[$header] = trim($value); return $this->_headers; } @@ -618,7 +612,7 @@ public function body($content = null) { * Sets the HTTP status code to be sent * if $code is null the current code is returned * - * @param integer $code + * @param integer $code the HTTP status code * @return integer current status code * @throws CakeException When an unknown status code is reached. */ @@ -635,31 +629,47 @@ public function statusCode($code = null) { /** * Queries & sets valid HTTP response codes & messages. * - * @param integer|array $code If $code is an integer, then the corresponding code/message is - * returned if it exists, null if it does not exist. If $code is an array, - * then the 'code' and 'message' keys of each nested array are added to the default - * HTTP codes. Example: + * @param integer|array $code If $code is an integer, then the corresponding code/message is + * returned if it exists, null if it does not exist. If $code is an array, then the + * keys are used as codes and the values as messages to add to the default HTTP + * codes. The codes must be integers greater than 99 and less than 1000. Keep in + * mind that the HTTP specification outlines that status codes begin with a digit + * between 1 and 5, which defines the class of response the client is to expect. + * Example: * * httpCodes(404); // returns array(404 => 'Not Found') * * httpCodes(array( - * 701 => 'Unicorn Moved', - * 800 => 'Unexpected Minotaur' + * 381 => 'Unicorn Moved', + * 555 => 'Unexpected Minotaur' * )); // sets these new values, and returns true * + * httpCodes(array( + * 0 => 'Nothing Here', + * -1 => 'Reverse Infinity', + * 12345 => 'Universal Password', + * 'Hello' => 'World' + * )); // throws an exception due to invalid codes + * + * For more on HTTP status codes see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 + * * @return mixed associative array of the HTTP codes as keys, and the message * strings as values, or null of the given $code does not exist. + * @throws CakeException If an attempt is made to add an invalid status code */ public function httpCodes($code = null) { if (empty($code)) { return $this->_statusCodes; } - if (is_array($code)) { + $codes = array_keys($code); + $min = min($codes); + if (!is_int($min) || $min < 100 || max($codes) > 999) { + throw new CakeException(__d('cake_dev', 'Invalid status code')); + } $this->_statusCodes = $code + $this->_statusCodes; return true; } - if (!isset($this->_statusCodes[$code])) { return null; } @@ -1316,6 +1326,15 @@ protected function _fileRange($file, $httpRange) { $fileSize = $file->size(); $lastByte = $fileSize - 1; + + if ($start === '') { + $start = $fileSize - $end; + $end = $lastByte; + } + if ($end === '') { + $end = $lastByte; + } + if ($start > $end || $end > $lastByte || $start > $lastByte) { $this->statusCode(416); $this->header(array( diff --git a/lib/Cake/Routing/Route/CakeRoute.php b/lib/Cake/Routing/Route/CakeRoute.php index 55df787b2..72886003b 100755 --- a/lib/Cake/Routing/Route/CakeRoute.php +++ b/lib/Cake/Routing/Route/CakeRoute.php @@ -378,6 +378,9 @@ protected function _matchNamed($val, $rule, $context) { * @return array An array with persistent parameters applied. */ public function persistParams($url, $params) { + if (empty($this->options['persist']) || !is_array($this->options['persist'])) { + return $url; + } foreach ($this->options['persist'] as $persistKey) { if (array_key_exists($persistKey, $params) && !isset($url[$persistKey])) { $url[$persistKey] = $params[$persistKey]; diff --git a/lib/Cake/Routing/Router.php b/lib/Cake/Routing/Router.php index bdba40a45..e04ffc25f 100755 --- a/lib/Cake/Routing/Router.php +++ b/lib/Cake/Routing/Router.php @@ -62,7 +62,7 @@ class Router { * * @var string */ - protected static $_baseURL; + protected static $_fullBaseUrl; /** * List of action prefixes used in connected routes. @@ -559,7 +559,8 @@ public static function parse($url) { $url = '/' . $url; } if (strpos($url, '?') !== false) { - $url = substr($url, 0, strpos($url, '?')); + list($url, $queryParameters) = explode('?', $url, 2); + parse_str($queryParameters, $queryParameters); } extract(self::_parseExtension($url)); @@ -580,6 +581,10 @@ public static function parse($url) { if (!empty($ext) && !isset($out['ext'])) { $out['ext'] = $ext; } + + if (!empty($queryParameters) && !isset($out['?'])) { + $out['?'] = $queryParameters; + } return $out; } @@ -767,7 +772,7 @@ public static function promote($which = null) { * cake relative URLs are required when using requestAction. * - `?` - Takes an array of query string parameters * - `#` - Allows you to set URL hash fragments. - * - `full_base` - If true the `Router::baseURL()` value will be prepended to generated URLs. + * - `full_base` - If true the `Router::fullBaseUrl()` value will be prepended to generated URLs. * * @param string|array $url Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4" * or an array specifying any of the following: 'controller', 'action', @@ -805,7 +810,7 @@ public static function url($url = null, $full = false) { if (empty($url)) { $output = isset($path['here']) ? $path['here'] : '/'; if ($full) { - $output = self::baseURL() . $output; + $output = self::fullBaseUrl() . $output; } return $output; } elseif (is_array($url)) { @@ -856,9 +861,7 @@ public static function url($url = null, $full = false) { for ($i = 0, $len = count(self::$routes); $i < $len; $i++) { $originalUrl = $url; - if (isset(self::$routes[$i]->options['persist'], $params)) { - $url = self::$routes[$i]->persistParams($url, $params); - } + $url = self::$routes[$i]->persistParams($url, $params); if ($match = self::$routes[$i]->match($url)) { $output = trim($match, '/'); @@ -893,7 +896,7 @@ public static function url($url = null, $full = false) { $output = str_replace('//', '/', $base . '/' . $output); if ($full) { - $output = self::baseURL() . $output; + $output = self::fullBaseUrl() . $output; } if (!empty($extension)) { $output = rtrim($output, '/'); @@ -905,11 +908,11 @@ public static function url($url = null, $full = false) { /** * Sets the full base url that will be used as a prefix for generating * fully qualified URLs for this application. If not parameters are passed, - * the currently configured value is returned + * the currently configured value is returned. * * ## Note: * - * If you change during runtime the configuration value ``App.fullBaseURL`` + * If you change the configuration value ``App.fullBaseUrl`` during runtime * and expect the router to produce links using the new setting, you are * required to call this method passing such value again. * @@ -917,15 +920,15 @@ public static function url($url = null, $full = false) { * For example: ``http://example.com`` * @return string */ - public static function baseURL($base = null) { + public static function fullBaseUrl($base = null) { if ($base !== null) { - self::$_baseURL = $base; - Configure::write('App.fullBaseURL', $base); + self::$_fullBaseUrl = $base; + Configure::write('App.fullBaseUrl', $base); } - if (empty(self::$_baseURL)) { - self::$_baseURL = Configure::read('App.fullBaseURL'); + if (empty(self::$_fullBaseUrl)) { + self::$_fullBaseUrl = Configure::read('App.fullBaseUrl'); } - return self::$_baseURL; + return self::$_fullBaseUrl; } /** diff --git a/lib/Cake/Test/Case/AllDbRelatedTest.php b/lib/Cake/Test/Case/AllDbRelatedTest.php new file mode 100755 index 000000000..75ac442fb --- /dev/null +++ b/lib/Cake/Test/Case/AllDbRelatedTest.php @@ -0,0 +1,51 @@ +addTestFile($path . 'AllBehaviorsTest.php'); + $suite->addTestFile($path . 'Controller' . DS . 'Component' . DS . 'PaginatorComponentTest.php'); + $suite->addTestFile($path . 'AllDatabaseTest.php'); + $suite->addTestFile($path . 'Model' . DS . 'ModelTest.php'); + $suite->addTestFile($path . 'View' . DS . 'ViewTest.php'); + $suite->addTestFile($path . 'View' . DS . 'ScaffoldViewTest.php'); + $suite->addTestFile($path . 'View' . DS . 'HelperTest.php'); + $suite->addTestFile($path . 'View' . DS . 'Helper' . DS . 'FormHelperTest.php'); + $suite->addTestFile($path . 'View' . DS . 'Helper' . DS . 'PaginatorHelperTest.php'); + return $suite; + } +} diff --git a/lib/Cake/Test/Case/Cache/CacheTest.php b/lib/Cake/Test/Case/Cache/CacheTest.php index 8b6cfd2ad..13116c2f7 100755 --- a/lib/Cake/Test/Case/Cache/CacheTest.php +++ b/lib/Cake/Test/Case/Cache/CacheTest.php @@ -132,6 +132,10 @@ public function testConfigWithLibAndPluginEngines() { * @return void */ public function testInvalidConfig() { + // In debug mode it would auto create the folder. + $debug = Configure::read('debug'); + Configure::write('debug', 0); + Cache::config('invalid', array( 'engine' => 'File', 'duration' => '+1 year', @@ -141,6 +145,8 @@ public function testInvalidConfig() { 'random' => 'wii' )); Cache::read('Test', 'invalid'); + + Configure::write('debug', $debug); } /** diff --git a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php index 9e9c5fdfd..62b46b3b7 100755 --- a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php @@ -36,6 +36,10 @@ public function setUp() { parent::setUp(); $this->skipIf(!function_exists('apc_store'), 'Apc is not installed or configured properly.'); + if (php_sapi_name() === 'cli') { + $this->skipIf(!ini_get('apc.enable_cli'), 'APC is not enabled for the CLI.'); + } + $this->_cacheDisable = Configure::read('Cache.disable'); Configure::write('Cache.disable', false); Cache::config('apc', array('engine' => 'Apc', 'prefix' => 'cake_')); diff --git a/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php index e57e94212..9fc03ef64 100755 --- a/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php @@ -52,7 +52,7 @@ public function setUp() { */ public function tearDown() { parent::tearDown(); - Cache::clear(false, 'file_test'); + // Cache::clear(false, 'file_test'); Cache::drop('file_test'); Cache::drop('file_groups'); Cache::drop('file_groups2'); @@ -255,6 +255,41 @@ public function testClearWithPrefixes() { $FileTwo->clear(false); } +/** + * Test that clear() also removes files with group tags. + * + * @return void + */ + public function testClearWithGroups() { + $engine = new FileEngine(); + $engine->init(array( + 'prefix' => 'cake_test_', + 'duration' => DAY, + 'groups' => array('short', 'round') + )); + $key = 'cake_test_test_key'; + $engine->write($key, 'it works', DAY); + $engine->clear(false); + $this->assertFalse($engine->read($key), 'Key should have been removed'); + } + +/** + * Test that clear() also removes files with group tags. + * + * @return void + */ + public function testClearWithNoKeys() { + $engine = new FileEngine(); + $engine->init(array( + 'prefix' => 'cake_test_', + 'duration' => DAY, + 'groups' => array('one', 'two') + )); + $key = 'cake_test_test_key'; + $engine->clear(false); + $this->assertFalse($engine->read($key), 'No errors should be found'); + } + /** * testKeyPath method * @@ -338,20 +373,19 @@ public function testWriteQuotedString() { } /** - * check that FileEngine generates an error when a configured Path does not exist. + * check that FileEngine does not generate an error when a configured Path does not exist in debug mode. * - * @expectedException PHPUnit_Framework_Error_Warning * @return void */ - public function testErrorWhenPathDoesNotExist() { - $this->skipIf(is_dir(TMP . 'tests' . DS . 'file_failure'), 'Cannot run test directory exists.'); + public function testPathDoesNotExist() { + $this->skipIf(is_dir(TMP . 'tests' . DS . 'autocreate'), 'Cannot run if test directory exists.'); - Cache::config('failure', array( + Cache::config('autocreate', array( 'engine' => 'File', - 'path' => TMP . 'tests' . DS . 'file_failure' + 'path' => TMP . 'tests' . DS . 'autocreate' )); - Cache::drop('failure'); + Cache::drop('autocreate'); } /** @@ -366,7 +400,7 @@ public function testMaskSetting() { Cache::config('mask_test', array('engine' => 'File', 'path' => TMP . 'tests')); $data = 'This is some test content'; $write = Cache::write('masking_test', $data, 'mask_test'); - $result = substr(sprintf('%o',fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); + $result = substr(sprintf('%o', fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); $expected = '0664'; $this->assertEquals($expected, $result); Cache::delete('masking_test', 'mask_test'); @@ -374,7 +408,7 @@ public function testMaskSetting() { Cache::config('mask_test', array('engine' => 'File', 'mask' => 0666, 'path' => TMP . 'tests')); $write = Cache::write('masking_test', $data, 'mask_test'); - $result = substr(sprintf('%o',fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); + $result = substr(sprintf('%o', fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); $expected = '0666'; $this->assertEquals($expected, $result); Cache::delete('masking_test', 'mask_test'); @@ -382,7 +416,7 @@ public function testMaskSetting() { Cache::config('mask_test', array('engine' => 'File', 'mask' => 0644, 'path' => TMP . 'tests')); $write = Cache::write('masking_test', $data, 'mask_test'); - $result = substr(sprintf('%o',fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); + $result = substr(sprintf('%o', fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); $expected = '0644'; $this->assertEquals($expected, $result); Cache::delete('masking_test', 'mask_test'); @@ -390,7 +424,7 @@ public function testMaskSetting() { Cache::config('mask_test', array('engine' => 'File', 'mask' => 0640, 'path' => TMP . 'tests')); $write = Cache::write('masking_test', $data, 'mask_test'); - $result = substr(sprintf('%o',fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); + $result = substr(sprintf('%o', fileperms(TMP . 'tests' . DS . 'cake_masking_test')), -4); $expected = '0640'; $this->assertEquals($expected, $result); Cache::delete('masking_test', 'mask_test'); diff --git a/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php index 5b2e5c948..30f51d5af 100755 --- a/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php @@ -34,6 +34,7 @@ class RedisEngineTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $this->skipIf(!class_exists('Redis'), 'Redis is not installed or configured properly.'); $this->_cacheDisable = Configure::read('Cache.disable'); @@ -51,6 +52,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); Configure::write('Cache.disable', $this->_cacheDisable); Cache::drop(''); Cache::drop('redis_groups'); diff --git a/lib/Cake/Test/Case/Console/Command/ApiShellTest.php b/lib/Cake/Test/Case/Console/Command/ApiShellTest.php index 902e3e40f..f333b8528 100755 --- a/lib/Cake/Test/Case/Console/Command/ApiShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/ApiShellTest.php @@ -44,7 +44,7 @@ public function setUp() { $this->Shell = $this->getMock( 'ApiShell', array('in', 'out', 'createFile', 'hr', '_stop'), - array( $out, $out, $in) + array($out, $out, $in) ); } diff --git a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php index 5d85aa569..fcf86204b 100755 --- a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php @@ -224,7 +224,7 @@ public function testDoHelpersTrailingCommas() { public function testDoComponentsNo() { $this->Task->expects($this->any())->method('in')->will($this->returnValue('n')); $result = $this->Task->doComponents(); - $this->assertSame(array(), $result); + $this->assertSame(array('Paginator'), $result); } /** @@ -237,7 +237,7 @@ public function testDoComponentsTrailingSpaces() { $this->Task->expects($this->at(1))->method('in')->will($this->returnValue(' RequestHandler, Security ')); $result = $this->Task->doComponents(); - $expected = array('RequestHandler', 'Security'); + $expected = array('Paginator', 'RequestHandler', 'Security'); $this->assertEquals($expected, $result); } @@ -251,7 +251,7 @@ public function testDoComponentsTrailingCommas() { $this->Task->expects($this->at(1))->method('in')->will($this->returnValue(' RequestHandler, Security, , ')); $result = $this->Task->doComponents(); - $expected = array('RequestHandler', 'Security'); + $expected = array('Paginator', 'RequestHandler', 'Security'); $this->assertEquals($expected, $result); } @@ -286,8 +286,9 @@ public function testBake() { $this->assertContains(' * @property Article $Article', $result); $this->assertContains(' * @property AclComponent $Acl', $result); $this->assertContains(' * @property AuthComponent $Auth', $result); + $this->assertContains(' * @property PaginatorComponent $Paginator', $result); $this->assertContains('class ArticlesController extends AppController', $result); - $this->assertContains("public \$components = array('Acl', 'Auth')", $result); + $this->assertContains("public \$components = array('Acl', 'Auth', 'Paginator')", $result); $this->assertContains("public \$helpers = array('Js', 'Time')", $result); $this->assertContains("--actions--", $result); @@ -300,8 +301,8 @@ public function testBake() { $result = $this->Task->bake('Articles', '--actions--', array(), array()); $this->assertContains('class ArticlesController extends AppController', $result); - $this->assertSame(substr_count($result, '@property'), 1); - $this->assertNotContains('components', $result); + $this->assertSame(substr_count($result, '@property'), 2); + $this->assertContains("public \$components = array('Paginator')", $result); $this->assertNotContains('helpers', $result); $this->assertContains('--actions--', $result); } @@ -350,7 +351,7 @@ public function testBakeActionsUsingSessions() { $this->assertContains('function index() {', $result); $this->assertContains('$this->BakeArticle->recursive = 0;', $result); - $this->assertContains("\$this->set('bakeArticles', \$this->paginate());", $result); + $this->assertContains("\$this->set('bakeArticles', \$this->Paginator->paginate());", $result); $this->assertContains('function view($id = null)', $result); $this->assertContains("throw new NotFoundException(__('Invalid bake article'));", $result); @@ -388,7 +389,7 @@ public function testBakeActionsWithNoSessions() { $this->assertContains('function index() {', $result); $this->assertContains('$this->BakeArticle->recursive = 0;', $result); - $this->assertContains("\$this->set('bakeArticles', \$this->paginate());", $result); + $this->assertContains("\$this->set('bakeArticles', \$this->Paginator->paginate());", $result); $this->assertContains('function view($id = null)', $result); $this->assertContains("throw new NotFoundException(__('Invalid bake article'));", $result); diff --git a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php index 201262254..73366e017 100755 --- a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php @@ -815,6 +815,34 @@ public function testLoginRedirect() { $expected = Router::normalize('posts/index/29?print=true&refer=menu'); $this->assertEquals($expected, $this->Auth->Session->read('Auth.redirect')); + // Different base urls. + $appConfig = Configure::read('App'); + + $_GET = array(); + + Configure::write('App', array( + 'dir' => APP_DIR, + 'webroot' => WEBROOT_DIR, + 'base' => false, + 'baseUrl' => '/cake/index.php' + )); + + $this->Auth->Session->delete('Auth'); + + $url = '/posts/add'; + $this->Auth->request = $this->Controller->request = new CakeRequest($url); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->url = Router::normalize($url); + + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); + $this->Auth->startup($this->Controller); + $expected = Router::normalize('/posts/add'); + $this->assertEquals($expected, $this->Auth->Session->read('Auth.redirect')); + + $this->Auth->Session->delete('Auth'); + Configure::write('App', $appConfig); + $_GET = $_back; // External Authed Action @@ -1321,6 +1349,41 @@ public function testRedirectSessionReadEqualToLoginAction() { $this->assertFalse($this->Auth->Session->check('Auth.redirect')); } +/** + * test that the returned URL doesn't contain the base URL. + * + * @see https://cakephp.lighthouseapp.com/projects/42648/tickets/3922-authcomponentredirecturl-prepends-appbaseurl + * + * @return void This test method doesn't return anything. + */ + public function testRedirectUrlWithBaseSet() { + $App = Configure::read('App'); + + Configure::write('App', array( + 'dir' => APP_DIR, + 'webroot' => WEBROOT_DIR, + 'base' => false, + 'baseUrl' => '/cake/index.php' + )); + + $url = '/users/login'; + $this->Auth->request = $this->Controller->request = new CakeRequest($url); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->url = Router::normalize($url); + + Router::setRequestInfo($this->Auth->request); + + $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); + $this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'home'); + + $result = $this->Auth->redirectUrl(); + $this->assertEquals('/users/home', $result); + $this->assertFalse($this->Auth->Session->check('Auth.redirect')); + + Configure::write('App', $App); + Router::reload(); + } + /** * test password hashing * diff --git a/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php b/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php index aeb0e2503..12371d83e 100755 --- a/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php @@ -72,6 +72,7 @@ class CookieComponentTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $_COOKIE = array(); $this->Controller = new CookieComponentTestController(new CakeRequest(), new CakeResponse()); $this->Controller->constructClasses(); @@ -93,6 +94,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); $this->Cookie->destroy(); } @@ -201,6 +203,24 @@ public function testWriteSimple() { $this->assertEquals('value', $result); } +/** + * test that two write() calls use the expiry. + * + * @return void + */ + public function testWriteMultipleShareExpiry() { + $this->Cookie->write('key1', 'value1', false); + $this->Cookie->write('key2', 'value2', false); + + $name = $this->Cookie->name . '[key1]'; + $result = $this->Controller->response->cookie($name); + $this->assertWithinMargin(time() + 10, $result['expire'], 2, 'Expiry time is wrong'); + + $name = $this->Cookie->name . '[key2]'; + $result = $this->Controller->response->cookie($name); + $this->assertWithinMargin(time() + 10, $result['expire'], 2, 'Expiry time is wrong'); + } + /** * test write with distant future cookies * diff --git a/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php b/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php index df0cc9b21..cb5dcbef7 100755 --- a/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php @@ -320,6 +320,8 @@ public function testPaginate() { $Controller->request->query = array(); $Controller->constructClasses(); + $Controller->PaginatorControllerPost->order = null; + $Controller->Paginator->settings = array( 'order' => array('PaginatorControllerComment.id' => 'ASC') ); diff --git a/lib/Cake/Test/Case/Core/ConfigureTest.php b/lib/Cake/Test/Case/Core/ConfigureTest.php index 65e7717ca..407f18643 100755 --- a/lib/Cake/Test/Case/Core/ConfigureTest.php +++ b/lib/Cake/Test/Case/Core/ConfigureTest.php @@ -69,6 +69,23 @@ public function tearDown() { Configure::drop('test'); } +/** + * Test to ensure bootrapping doesn't overwrite prior configs set under 'App' key + * @return void + */ + public function testBootstrap() { + $expected = array( + 'foo' => 'bar' + ); + Configure::write('App', $expected); + + Configure::bootstrap(true); + $result = Configure::read('App'); + + $this->assertEquals($expected['foo'], $result['foo']); + $this->assertFalse($result['base']); + } + /** * testRead method * diff --git a/lib/Cake/Test/Case/Core/ObjectTest.php b/lib/Cake/Test/Case/Core/ObjectTest.php index e6b63819a..10b0cc7d7 100755 --- a/lib/Cake/Test/Case/Core/ObjectTest.php +++ b/lib/Cake/Test/Case/Core/ObjectTest.php @@ -456,7 +456,7 @@ public function testRequestAction() { $this->assertEquals($expected, $result); $result = $this->object->requestAction( - Configure::read('App.fullBaseURL') . '/request_action/test_request_action' + Configure::read('App.fullBaseUrl') . '/request_action/test_request_action' ); $expected = 'This is a test'; $this->assertEquals($expected, $result); @@ -600,7 +600,7 @@ public function testRequestActionParamParseAndPass() { $this->assertEquals(null, $result['plugin']); $result = $this->object->requestAction('/request_action/params_pass/sort:desc/limit:5'); - $expected = array('sort' => 'desc', 'limit' => 5,); + $expected = array('sort' => 'desc', 'limit' => 5); $this->assertEquals($expected, $result['named']); $result = $this->object->requestAction( diff --git a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php index 86b88740b..40c357053 100755 --- a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php +++ b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php @@ -528,7 +528,7 @@ public static function testProvider() { 404 ), array( - new PrivateActionException(array('controller' => 'PostsController' , 'action' => '_secretSauce')), + new PrivateActionException(array('controller' => 'PostsController', 'action' => '_secretSauce')), array( '/

      Private Method in PostsController<\/h2>/', '/PostsController::<\/em>_secretSauce\(\)<\/em>/' diff --git a/lib/Cake/Test/Case/I18n/I18nTest.php b/lib/Cake/Test/Case/I18n/I18nTest.php index c22c3fd7c..aae136cf5 100755 --- a/lib/Cake/Test/Case/I18n/I18nTest.php +++ b/lib/Cake/Test/Case/I18n/I18nTest.php @@ -1854,6 +1854,16 @@ public function testTranslateLanguageParam() { $this->assertEquals($expected, $result); } +/** + * Test that the '' domain causes exceptions. + * + * @expectedException CakeException + * @return void + */ + public function testTranslateEmptyDomain() { + I18n::translate('Plural Rule 1', null, ''); + } + /** * Singular method * diff --git a/lib/Cake/Test/Case/I18n/L10nTest.php b/lib/Cake/Test/Case/I18n/L10nTest.php index bbb84cab4..0a6468146 100755 --- a/lib/Cake/Test/Case/I18n/L10nTest.php +++ b/lib/Cake/Test/Case/I18n/L10nTest.php @@ -27,6 +27,16 @@ */ class L10nTest extends CakeTestCase { +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + Configure::delete('Config.language'); + } + /** * testGet method * @@ -40,14 +50,14 @@ public function testGet() { $this->assertEquals('en', $lang); $this->assertEquals('English', $localize->language); - $this->assertEquals(array('eng', 'eng'), $localize->languagePath); + $this->assertEquals(array('eng'), $localize->languagePath); $this->assertEquals('eng', $localize->locale); // Map Entry $localize->get('eng'); $this->assertEquals('English', $localize->language); - $this->assertEquals(array('eng', 'eng'), $localize->languagePath); + $this->assertEquals(array('eng'), $localize->languagePath); $this->assertEquals('eng', $localize->locale); // Catalog Entry @@ -58,8 +68,7 @@ public function testGet() { $this->assertEquals('en_ca', $localize->locale); // Default Entry - define('DEFAULT_LANGUAGE', 'en-us'); - + $localize->default = 'en-us'; $lang = $localize->get('use_default'); $this->assertEquals('en-us', $lang); @@ -70,14 +79,6 @@ public function testGet() { $localize->get('es'); $localize->get(''); $this->assertEquals('en-us', $localize->lang); - - // Using $this->default - $localize = new L10n(); - - $localize->get('use_default'); - $this->assertEquals('English (United States)', $localize->language); - $this->assertEquals(array('en_us', 'eng', 'eng'), $localize->languagePath); - $this->assertEquals('en_us', $localize->locale); } /** @@ -94,7 +95,7 @@ public function testGetAutoLanguage() { $this->assertEquals('en-ca', $lang); $this->assertEquals('English (Canadian)', $localize->language); - $this->assertEquals(array('en_ca', 'eng', 'eng'), $localize->languagePath); + $this->assertEquals(array('en_ca', 'eng'), $localize->languagePath); $this->assertEquals('en_ca', $localize->locale); $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'es_mx'; @@ -102,19 +103,52 @@ public function testGetAutoLanguage() { $this->assertEquals('es-mx', $lang); $this->assertEquals('Spanish (Mexican)', $localize->language); - $this->assertEquals(array('es_mx', 'spa', 'eng'), $localize->languagePath); + $this->assertEquals(array('es_mx', 'spa'), $localize->languagePath); $this->assertEquals('es_mx', $localize->locale); + $localize = new L10n(); + $localize->default = 'en-us'; + $lang = $localize->get(); + $this->assertEquals(array('es_mx', 'spa', 'eng'), $localize->languagePath); + $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en_xy,en_ca'; $localize->get(); $this->assertEquals('English', $localize->language); - $this->assertEquals(array('eng', 'eng', 'eng'), $localize->languagePath); + $this->assertEquals(array('eng'), $localize->languagePath); $this->assertEquals('eng', $localize->locale); $_SERVER = $serverBackup; } +/** + * testGet method with deprecated constant DEFAULT_LANGUAGE + * + * @return void + */ + public function testGetWithDeprecatedConstant() { + $this->skipIf(defined('DEFAULT_LANGUAGE'), 'Cannot re-define already defined constant.'); + + define('DEFAULT_LANGUAGE', 'en-us'); + $localize = new L10n(); + + $lang = $localize->get('use_default'); + + $this->assertEquals('en-us', $lang); + $this->assertEquals('English (United States)', $localize->language); + $this->assertEquals(array('en_us', 'eng'), $localize->languagePath); + $this->assertEquals('en_us', $localize->locale); + + $localize = new L10n(); + + $lang = $localize->get(); + + $this->assertEquals('en-us', $lang); + $this->assertEquals('English (United States)', $localize->language); + $this->assertEquals(array('en_us', 'eng'), $localize->languagePath); + $this->assertEquals('en_us', $localize->locale); + } + /** * testMap method * diff --git a/lib/Cake/Test/Case/Model/Behavior/AclBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/AclBehaviorTest.php index a971fb507..c4e4e1c85 100755 --- a/lib/Cake/Test/Case/Model/Behavior/AclBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/AclBehaviorTest.php @@ -226,6 +226,7 @@ public function tearDown() { * @return void */ public function testSetup() { + parent::setUp(); $User = new AclUser(); $this->assertTrue(isset($User->Behaviors->Acl->settings['User'])); $this->assertEquals('requester', $User->Behaviors->Acl->settings['User']['type']); diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php index a15214289..b0e845e74 100755 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php @@ -62,6 +62,7 @@ class TreeBehaviorAfterTest extends CakeTestCase { */ public function testAftersaveCallback() { $this->Tree = new AfterTree(); + $this->Tree->order = null; $expected = array('AfterTree' => array('name' => 'Six and One Half Changed in AfterTree::afterSave() but not in database', 'parent_id' => 6, 'lft' => 11, 'rght' => 12)); $result = $this->Tree->save(array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6))); diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php index 4072962fa..ae7dc9812 100755 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php @@ -65,6 +65,7 @@ class TreeBehaviorNumberTest extends CakeTestCase { public function testInitialize() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->find('count'); @@ -82,6 +83,7 @@ public function testInitialize() { public function testDetectInvalidLeft() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->findByName('1.1'); @@ -108,6 +110,7 @@ public function testDetectInvalidLeft() { public function testDetectInvalidRight() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->findByName('1.1'); @@ -134,6 +137,7 @@ public function testDetectInvalidRight() { public function testDetectInvalidParent() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->findByName('1.1'); @@ -159,6 +163,7 @@ public function testDetectInvalidParent() { public function testDetectNoneExistentParent() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->findByName('1.1'); @@ -182,6 +187,7 @@ public function testDetectNoneExistentParent() { public function testRecoverUsingParentMode() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->Behaviors->disable('Tree'); $this->Tree->save(array('name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0)); @@ -233,6 +239,7 @@ public function testRecoverUsingParentMode() { public function testRecoverUsingParentModeAndDelete() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->Behaviors->disable('Tree'); $this->Tree->save(array('name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0)); @@ -301,6 +308,7 @@ public function testRecoverUsingParentModeAndDelete() { public function testRecoverFromMissingParent() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->findByName('1.1'); @@ -324,6 +332,7 @@ public function testRecoverFromMissingParent() { public function testDetectInvalidParents() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->updateAll(array($parentField => null)); @@ -346,6 +355,7 @@ public function testDetectInvalidParents() { public function testDetectInvalidLftsRghts() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->updateAll(array($leftField => 0, $rightField => 0)); @@ -367,6 +377,7 @@ public function testDetectInvalidLftsRghts() { public function testDetectEqualLftsRghts() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 3); $result = $this->Tree->findByName('1.1'); @@ -394,6 +405,7 @@ public function testDetectEqualLftsRghts() { public function testAddOrphan() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->save(array($modelClass => array('name' => 'testAddOrphan', $parentField => null))); @@ -413,6 +425,7 @@ public function testAddOrphan() { public function testAddMiddle() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.1'))); @@ -444,6 +457,7 @@ public function testAddMiddle() { public function testAddWithPreSpecifiedId() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array( @@ -474,6 +488,7 @@ public function testAddWithPreSpecifiedId() { public function testAddInvalid() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -498,6 +513,7 @@ public function testAddInvalid() { public function testAddNotIndexedByModel() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->save(array('name' => 'testAddNotIndexed', $parentField => null)); @@ -517,6 +533,7 @@ public function testAddNotIndexedByModel() { public function testMovePromote() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -543,6 +560,7 @@ public function testMovePromote() { public function testMoveWithWhitelist() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -570,6 +588,7 @@ public function testMoveWithWhitelist() { public function testInsertWithWhitelist() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->whitelist = array('name', $parentField); @@ -588,6 +607,7 @@ public function testInsertWithWhitelist() { public function testMoveBefore() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -616,6 +636,7 @@ public function testMoveBefore() { public function testMoveAfter() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -644,6 +665,7 @@ public function testMoveAfter() { public function testMoveDemoteInvalid() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -676,6 +698,7 @@ public function testMoveDemoteInvalid() { public function testMoveInvalid() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -700,6 +723,7 @@ public function testMoveInvalid() { public function testMoveSelfInvalid() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -725,6 +749,7 @@ public function testMoveSelfInvalid() { public function testMoveUpSuccess() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.2'))); @@ -746,6 +771,7 @@ public function testMoveUpSuccess() { public function testMoveUpFail() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1.1'))); @@ -768,6 +794,7 @@ public function testMoveUpFail() { public function testMoveUp2() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 10); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.5'))); @@ -798,6 +825,7 @@ public function testMoveUp2() { public function testMoveUpFirst() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 10); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.5'))); @@ -828,6 +856,7 @@ public function testMoveUpFirst() { public function testMoveDownSuccess() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.1'))); @@ -849,6 +878,7 @@ public function testMoveDownSuccess() { public function testMoveDownFail() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1.2'))); @@ -870,6 +900,7 @@ public function testMoveDownFail() { public function testMoveDownLast() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 10); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.5'))); @@ -900,6 +931,7 @@ public function testMoveDownLast() { public function testMoveDown2() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 10); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.5'))); @@ -930,6 +962,7 @@ public function testMoveDown2() { public function testSaveNoMove() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 10); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.5'))); @@ -960,6 +993,7 @@ public function testSaveNoMove() { public function testMoveToRootAndMoveUp() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(1, 1); $data = $this->Tree->find('first', array('fields' => array('id'), 'conditions' => array($modelClass . '.name' => '1.1'))); $this->Tree->id = $data[$modelClass]['id']; @@ -984,6 +1018,7 @@ public function testMoveToRootAndMoveUp() { public function testDelete() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -1019,6 +1054,7 @@ public function testDelete() { public function testDeleteDoesNotExist() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->delete(99999); } @@ -1031,6 +1067,7 @@ public function testDeleteDoesNotExist() { public function testRemove() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); $result = $this->Tree->findByName('1.1'); @@ -1063,6 +1100,7 @@ public function testRemove() { public function testRemoveLastTopParent() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -1096,6 +1134,7 @@ public function testRemoveLastTopParent() { public function testRemoveNoChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -1130,6 +1169,7 @@ public function testRemoveNoChildren() { public function testRemoveAndDelete() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -1164,6 +1204,7 @@ public function testRemoveAndDelete() { public function testRemoveAndDeleteNoChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -1196,6 +1237,7 @@ public function testRemoveAndDeleteNoChildren() { public function testChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1. Root'))); @@ -1226,6 +1268,7 @@ public function testChildren() { public function testCountChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1. Root'))); @@ -1251,6 +1294,7 @@ public function testCountChildren() { public function testGetParentNode() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1.2.2'))); @@ -1269,6 +1313,7 @@ public function testGetParentNode() { public function testGetPath() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1.2.2'))); @@ -1289,6 +1334,7 @@ public function testGetPath() { public function testNoAmbiguousColumn() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->bindModel(array('belongsTo' => array('Dummy' => array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false); $this->Tree->initialize(2, 2); @@ -1321,6 +1367,7 @@ public function testNoAmbiguousColumn() { public function testReorderTree() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(3, 3); $nodes = $this->Tree->find('list', array('order' => $leftField)); @@ -1352,6 +1399,7 @@ public function testReorderTree() { public function testReorderBigTreeWithQueryCaching() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 10); $original = $this->Tree->cacheQueries; @@ -1369,6 +1417,7 @@ public function testReorderBigTreeWithQueryCaching() { public function testGenerateTreeListWithSelfJoin() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->bindModel(array('belongsTo' => array('Dummy' => array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false); $this->Tree->initialize(2, 2); @@ -1386,6 +1435,7 @@ public function testGenerateTreeListWithSelfJoin() { public function testGenerateTreeListFormatting() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $result = $this->Tree->generateTreeList( @@ -1406,6 +1456,7 @@ public function testGenerateTreeListFormatting() { public function testArraySyntax() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(3, 3); $this->assertSame($this->Tree->childCount(2), $this->Tree->childCount(array('id' => 2))); $this->assertSame($this->Tree->getParentNode(2), $this->Tree->getParentNode(array('id' => 2))); diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php index b830d7dd2..ce452fbac 100755 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php @@ -64,6 +64,7 @@ class TreeBehaviorScopedTest extends CakeTestCase { */ public function testStringScope() { $this->Tree = new FlagTree(); + $this->Tree->order = null; $this->Tree->initialize(2, 3); $this->Tree->id = 1; @@ -100,6 +101,7 @@ public function testStringScope() { */ public function testArrayScope() { $this->Tree = new FlagTree(); + $this->Tree->order = null; $this->Tree->initialize(2, 3); $this->Tree->id = 1; @@ -136,6 +138,7 @@ public function testArrayScope() { */ public function testMoveUpWithScope() { $this->Ad = new Ad(); + $this->Ad->order = null; $this->Ad->Behaviors->attach('Tree', array('scope' => 'Campaign')); $this->Ad->moveUp(6); @@ -152,6 +155,7 @@ public function testMoveUpWithScope() { */ public function testMoveDownWithScope() { $this->Ad = new Ad(); + $this->Ad->order = null; $this->Ad->Behaviors->attach('Tree', array('scope' => 'Campaign')); $this->Ad->moveDown(6); @@ -169,6 +173,7 @@ public function testMoveDownWithScope() { */ public function testTranslatingTree() { $this->Tree = new FlagTree(); + $this->Tree->order = null; $this->Tree->cacheQueries = false; $this->Tree->Behaviors->attach('Translate', array('title')); @@ -286,9 +291,11 @@ public function testTranslatingTree() { public function testAliasesWithScopeInTwoTreeAssociations() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->TreeTwo = new NumberTreeTwo(); + $this->TreeTwo->order = null; $record = $this->Tree->find('first'); diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php index 2cf11e68d..4766b1519 100755 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php @@ -66,6 +66,7 @@ class TreeBehaviorUuidTest extends CakeTestCase { public function testAddWithPreSpecifiedId() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array( @@ -97,6 +98,7 @@ public function testAddWithPreSpecifiedId() { public function testMovePromote() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -123,6 +125,7 @@ public function testMovePromote() { public function testMoveWithWhitelist() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->id = null; @@ -150,6 +153,7 @@ public function testMoveWithWhitelist() { public function testRemoveNoChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -184,6 +188,7 @@ public function testRemoveNoChildren() { public function testRemoveAndDeleteNoChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $initialCount = $this->Tree->find('count'); @@ -216,6 +221,7 @@ public function testRemoveAndDeleteNoChildren() { public function testChildren() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1. Root'))); @@ -244,6 +250,7 @@ public function testChildren() { public function testNoAmbiguousColumn() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->initialize(2, 2); $this->Tree->bindModel(array('belongsTo' => array('Dummy' => @@ -280,6 +287,7 @@ public function testNoAmbiguousColumn() { public function testGenerateTreeListWithSelfJoin() { extract($this->settings); $this->Tree = new $modelClass(); + $this->Tree->order = null; $this->Tree->bindModel(array('belongsTo' => array('Dummy' => array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false); $this->Tree->initialize(2, 2); diff --git a/lib/Cake/Test/Case/Model/CakeSchemaTest.php b/lib/Cake/Test/Case/Model/CakeSchemaTest.php index 66bc3dd0e..07afe7fee 100755 --- a/lib/Cake/Test/Case/Model/CakeSchemaTest.php +++ b/lib/Cake/Test/Case/Model/CakeSchemaTest.php @@ -77,24 +77,6 @@ class MyAppSchema extends CakeSchema { */ protected $_foo = array('bar'); -/** - * setup method - * - * @param mixed $version - * @return void - */ - public function setup($version) { - } - -/** - * teardown method - * - * @param mixed $version - * @return void - */ - public function teardown($version) { - } - /** * getVar method * diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index 2fd7bed05..d34b80d78 100755 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -62,6 +62,7 @@ class MysqlTest extends CakeTestCase { * */ public function setUp() { + parent::setUp(); $this->Dbo = ConnectionManager::getDataSource('test'); if (!($this->Dbo instanceof Mysql)) { $this->markTestSkipped('The MySQL extension is not available.'); @@ -76,6 +77,7 @@ public function setUp() { * */ public function tearDown() { + parent::tearDown(); unset($this->model); ClassRegistry::flush(); Configure::write('debug', $this->_debug); @@ -984,7 +986,7 @@ public function testCreateSchemaAutoPrimaryKey() { 'primary_flag_has_index' => array( 'id' => array('type' => 'integer', 'null' => false, 'key' => 'primary'), 'data' => array('type' => 'integer', 'null' => false), - 'indexes' => array ( + 'indexes' => array( 'some_index' => array('column' => 'data', 'unique' => 1) ), ) @@ -2405,7 +2407,7 @@ public function testArrayConditionsParsing() { $this->assertEquals($expected, $result); $result = $this->Dbo->conditions(array( - 'NOT' => array('Course.id' => null, 'Course.vet' => 'N', 'level_of_education_id' => array(912,999)), + 'NOT' => array('Course.id' => null, 'Course.vet' => 'N', 'level_of_education_id' => array(912, 999)), 'Enrollment.yearcompleted >' => '0') ); $this->assertRegExp('/^\s*WHERE\s+\(NOT\s+\(`Course`\.`id` IS NULL\)\s+AND NOT\s+\(`Course`\.`vet`\s+=\s+\'N\'\)\s+AND NOT\s+\(`level_of_education_id` IN \(912, 999\)\)\)\s+AND\s+`Enrollment`\.`yearcompleted`\s+>\s+\'0\'\s*$/', $result); @@ -3265,7 +3267,7 @@ public function testVirtualFieldsInConditions() { $expected = '(1 + 1) = 2'; $this->assertEquals($expected, $result); - $conditions = array('this_moment BETWEEN ? AND ?' => array(1,2)); + $conditions = array('this_moment BETWEEN ? AND ?' => array(1, 2)); $expected = 'NOW() BETWEEN 1 AND 2'; $result = $this->Dbo->conditions($conditions, true, false, $Article); $this->assertEquals($expected, $result); diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php index dbb8976ff..2d95cc341 100755 --- a/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php @@ -215,6 +215,7 @@ class PostgresTest extends CakeTestCase { * */ public function setUp() { + parent::setUp(); Configure::write('Cache.disable', true); $this->Dbo = ConnectionManager::getDataSource('test'); $this->skipIf(!($this->Dbo instanceof Postgres)); @@ -227,6 +228,7 @@ public function setUp() { * */ public function tearDown() { + parent::tearDown(); Configure::write('Cache.disable', false); unset($this->Dbo2); } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index 464bafc1f..be0a98feb 100755 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -266,6 +266,7 @@ class SqlserverTest extends CakeTestCase { * */ public function setUp() { + parent::setUp(); $this->Dbo = ConnectionManager::getDataSource('test'); if (!($this->Dbo instanceof Sqlserver)) { $this->markTestSkipped('Please configure the test datasource to use SQL Server.'); @@ -280,6 +281,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); unset($this->Dbo); unset($this->model); } diff --git a/lib/Cake/Test/Case/Model/ModelDeleteTest.php b/lib/Cake/Test/Case/Model/ModelDeleteTest.php index a4e1681c9..63853920e 100755 --- a/lib/Cake/Test/Case/Model/ModelDeleteTest.php +++ b/lib/Cake/Test/Case/Model/ModelDeleteTest.php @@ -570,6 +570,7 @@ public function testDeleteLinksWithPLuginJoinModel() { 'Tag' => array('with' => 'TestPlugin.ArticlesTag') )), false); + $Article->ArticlesTag->order = null; $this->assertTrue($Article->delete(1)); } diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index de05b5193..1ab298c04 100755 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -836,16 +836,16 @@ public function testHABTMKeepExistingWithThreeDbs() { $this->assertEquals('test_database_three', $Player->ArmorsPlayer->useDbConfig); $players = $Player->find('all'); - $this->assertEquals(4 , count($players)); + $this->assertEquals(4, count($players)); $playersGuilds = Hash::extract($players, '{n}.Guild.{n}.GuildsPlayer'); - $this->assertEquals(3 , count($playersGuilds)); + $this->assertEquals(3, count($playersGuilds)); $playersArmors = Hash::extract($players, '{n}.Armor.{n}.ArmorsPlayer'); - $this->assertEquals(3 , count($playersArmors)); + $this->assertEquals(3, count($playersArmors)); unset($players); $larry = $Player->findByName('larry'); $larrysArmor = Hash::extract($larry, 'Armor.{n}.ArmorsPlayer'); - $this->assertEquals(1 , count($larrysArmor)); + $this->assertEquals(1, count($larrysArmor)); $larry['Guild']['Guild'] = array(1, 3); // larry joins another guild $larry['Armor']['Armor'] = array(2, 3); // purchases chainmail @@ -854,9 +854,9 @@ public function testHABTMKeepExistingWithThreeDbs() { $larry = $Player->findByName('larry'); $larrysGuild = Hash::extract($larry, 'Guild.{n}.GuildsPlayer'); - $this->assertEquals(2 , count($larrysGuild)); + $this->assertEquals(2, count($larrysGuild)); $larrysArmor = Hash::extract($larry, 'Armor.{n}.ArmorsPlayer'); - $this->assertEquals(2 , count($larrysArmor)); + $this->assertEquals(2, count($larrysArmor)); $Player->ArmorsPlayer->id = 3; $Player->ArmorsPlayer->saveField('broken', true); // larry's cloak broke diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 3992074da..365da93ac 100755 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -6302,6 +6302,7 @@ public function testBuildQuery() { $this->loadFixtures('User'); $TestModel = new User(); $TestModel->cacheQueries = false; + $TestModel->order = null; $expected = array( 'conditions' => array( @@ -6849,6 +6850,8 @@ public function testFindField() { )); $this->assertEquals('mariano', $result); + $TestModel->order = null; + $result = $TestModel->field('COUNT(*) AS count', true); $this->assertEquals(4, $result); @@ -6904,7 +6907,9 @@ public function testFindCount() { $this->assertNotRegExp('/ORDER\s+BY/', $log['log'][0]['query']); $Article = new Article(); + $Article->order = null; $Article->recursive = -1; + $expected = count($Article->find('all', array( 'fields' => array('Article.user_id'), 'group' => 'Article.user_id') @@ -7761,6 +7766,8 @@ public function testVirtualFields() { )); $this->assertEquals(2, $result['Post']['id']); + $Post->order = null; + $Post->virtualFields = array('other_field' => 'Post.id + 1'); $result = $Post->find('all', array( 'fields' => array($dbo->calculate($Post, 'max', array('other_field'))) diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 5dd9fe2c9..031a9436f 100755 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1522,7 +1522,7 @@ public function testSaveAllNotDeepValidateOnly() { * @return void */ public function testValidateAssociated() { - $this->loadFixtures('Comment', 'Attachment'); + $this->loadFixtures('Comment', 'Attachment', 'Article', 'User'); $TestModel = new Comment(); $TestModel->Attachment->validate = array('attachment' => 'notEmpty'); @@ -1539,6 +1539,18 @@ public function testValidateAssociated() { $result = $TestModel->validateAssociated($data); $this->assertFalse($result); + $fieldList = array( + 'Attachment' => array('comment_id') + ); + $result = $TestModel->saveAll($data, array( + 'fieldList' => $fieldList, 'validate' => 'only' + )); + $this->assertTrue($result); + $this->assertEmpty($TestModel->validationErrors); + $result = $TestModel->validateAssociated($data, array('fieldList' => $fieldList)); + $this->assertTrue($result); + $this->assertEmpty($TestModel->validationErrors); + $TestModel->validate = array('comment' => 'notEmpty'); $record = array( 'Comment' => array( diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 9cbf831b3..2239ddf2d 100755 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -2520,6 +2520,7 @@ public function testHabtmUuidWithUuidId() { public function testHabtmSavingWithNoPrimaryKeyUuidJoinTable() { $this->loadFixtures('UuidTag', 'Fruit', 'FruitsUuidTag'); $Fruit = new Fruit(); + $Fruit->FruitsUuidTag->order = null; $data = array( 'Fruit' => array( 'color' => 'Red', @@ -4962,7 +4963,7 @@ public function testSaveAssociatedAtomicFalseValidateFirstWithErrors() { $expected = array( 'Comment' => array( array( - 'comment' => array( 'This field cannot be left blank' ) + 'comment' => array('This field cannot be left blank') ) ) ); @@ -6557,7 +6558,7 @@ public function testSaveAllFieldListValidateBelongsTo() { 'order' => 'Post.id ASC', )); $expected = array( - 'Post' => array ( + 'Post' => array( 'id' => '4', 'author_id' => '5', 'title' => 'Post without body', @@ -6566,7 +6567,7 @@ public function testSaveAllFieldListValidateBelongsTo() { 'created' => self::date(), 'updated' => self::date(), ), - 'Author' => array ( + 'Author' => array( 'id' => '5', 'user' => 'bob', 'password' => null, @@ -6580,6 +6581,66 @@ public function testSaveAllFieldListValidateBelongsTo() { $this->assertEquals('', $result[3]['Post']['body']); $this->assertEquals('working', $result[3]['Author']['test']); + $fieldList = array( + 'Post' => array('title') + ); + $data = array( + 'Post' => array( + 'title' => 'Post without body 2', + 'body' => 'This will not be saved' + ), + 'Author' => array( + 'user' => 'jack' + ) + ); + $TestModel->saveAll($data, array('fieldList' => $fieldList)); + $result = $TestModel->find('all', array( + 'order' => 'Post.id ASC', + )); + $this->assertNull($result[4]['Post']['body']); + + $fieldList = array( + 'Author' => array('password') + ); + $data = array( + 'Post' => array( + 'id' => '5', + 'title' => 'Post title', + 'body' => 'Post body' + ), + 'Author' => array( + 'id' => '6', + 'user' => 'will not change', + 'password' => 'foobar' + ) + ); + $result = $TestModel->saveAll($data, array('fieldList' => $fieldList)); + $this->assertTrue($result); + + $result = $TestModel->find('all', array( + 'order' => 'Post.id ASC', + )); + $expected = array( + 'Post' => array( + 'id' => '5', + 'author_id' => '6', + 'title' => 'Post title', + 'body' => 'Post body', + 'published' => 'N', + 'created' => self::date(), + 'updated' => self::date() + ), + 'Author' => array( + 'id' => '6', + 'user' => 'jack', + 'password' => 'foobar', + 'created' => self::date(), + 'updated' => self::date(), + 'test' => 'working' + ), + ); + $this->assertEquals($expected, $result[4]); + // test multirecord $this->db->truncate($TestModel); diff --git a/lib/Cake/Test/Case/Network/CakeRequestTest.php b/lib/Cake/Test/Case/Network/CakeRequestTest.php index 27a6f8b6b..06e4f8151 100755 --- a/lib/Cake/Test/Case/Network/CakeRequestTest.php +++ b/lib/Cake/Test/Case/Network/CakeRequestTest.php @@ -157,7 +157,7 @@ public function testQueryStringAndNamedParams() { $request = new CakeRequest(); $this->assertEquals('some/path', $request->url); - $_SERVER['REQUEST_URI'] = Configure::read('App.fullBaseURL') . '/other/path?url=http://cakephp.org'; + $_SERVER['REQUEST_URI'] = Configure::read('App.fullBaseUrl') . '/other/path?url=http://cakephp.org'; $request = new CakeRequest(); $this->assertEquals('other/path', $request->url); } @@ -689,19 +689,19 @@ public function testReferer() { $result = $request->referer(); $this->assertSame($result, '/'); - $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseURL') . '/some/path'; + $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseUrl') . '/some/path'; $result = $request->referer(true); $this->assertSame($result, '/some/path'); - $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseURL') . '/some/path'; + $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseUrl') . '/some/path'; $result = $request->referer(false); - $this->assertSame($result, Configure::read('App.fullBaseURL') . '/some/path'); + $this->assertSame($result, Configure::read('App.fullBaseUrl') . '/some/path'); - $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseURL') . '/some/path'; + $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseUrl') . '/some/path'; $result = $request->referer(true); $this->assertSame($result, '/some/path'); - $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseURL') . '/recipes/add'; + $_SERVER['HTTP_REFERER'] = Configure::read('App.fullBaseUrl') . '/recipes/add'; $result = $request->referer(true); $this->assertSame($result, '/recipes/add'); diff --git a/lib/Cake/Test/Case/Network/CakeResponseTest.php b/lib/Cake/Test/Case/Network/CakeResponseTest.php index 5bc04b759..94d240047 100755 --- a/lib/Cake/Test/Case/Network/CakeResponseTest.php +++ b/lib/Cake/Test/Case/Network/CakeResponseTest.php @@ -30,6 +30,7 @@ class CakeResponseTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); ob_start(); } @@ -39,6 +40,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); ob_end_clean(); } @@ -135,41 +137,45 @@ public function testType() { public function testHeader() { $response = new CakeResponse(); $headers = array(); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); $response->header('Location', 'http://example.com'); $headers += array('Location' => 'http://example.com'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); //Headers with the same name are overwritten $response->header('Location', 'http://example2.com'); $headers = array('Location' => 'http://example2.com'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); $response->header(array('WWW-Authenticate' => 'Negotiate')); $headers += array('WWW-Authenticate' => 'Negotiate'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); $response->header(array('WWW-Authenticate' => 'Not-Negotiate')); $headers['WWW-Authenticate'] = 'Not-Negotiate'; - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); $response->header(array('Age' => 12, 'Allow' => 'GET, HEAD')); $headers += array('Age' => 12, 'Allow' => 'GET, HEAD'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); // String headers are allowed $response->header('Content-Language: da'); $headers += array('Content-Language' => 'da'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); $response->header('Content-Language: da'); $headers += array('Content-Language' => 'da'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); $response->header(array('Content-Encoding: gzip', 'Vary: *', 'Pragma' => 'no-cache')); $headers += array('Content-Encoding' => 'gzip', 'Vary' => '*', 'Pragma' => 'no-cache'); - $this->assertEquals($response->header(), $headers); + $this->assertEquals($headers, $response->header()); + + $response->header('Access-Control-Allow-Origin', array('domain1', 'domain2')); + $headers += array('Access-Control-Allow-Origin' => array('domain1', 'domain2')); + $this->assertEquals($headers, $response->header()); } /** @@ -180,7 +186,8 @@ public function testSend() { $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); $response->header(array( 'Content-Language' => 'es', - 'WWW-Authenticate' => 'Negotiate' + 'WWW-Authenticate' => 'Negotiate', + 'Access-Control-Allow-Origin' => array('domain1', 'domain2'), )); $response->body('the response body'); $response->expects($this->once())->method('_sendContent')->with('the response body'); @@ -192,8 +199,12 @@ public function testSend() { $response->expects($this->at(3)) ->method('_sendHeader')->with('WWW-Authenticate', 'Negotiate'); $response->expects($this->at(4)) - ->method('_sendHeader')->with('Content-Length', 17); + ->method('_sendHeader')->with('Access-Control-Allow-Origin', 'domain1'); $response->expects($this->at(5)) + ->method('_sendHeader')->with('Access-Control-Allow-Origin', 'domain2'); + $response->expects($this->at(6)) + ->method('_sendHeader')->with('Content-Length', 17); + $response->expects($this->at(7)) ->method('_sendHeader')->with('Content-Type', 'text/html; charset=UTF-8'); $response->send(); } @@ -359,6 +370,7 @@ public function testCompress() { /** * Tests the httpCodes method * + * @expectedException CakeException */ public function testHttpCodes() { $response = new CakeResponse(); @@ -370,16 +382,16 @@ public function testHttpCodes() { $this->assertEquals($expected, $result); $codes = array( - 1337 => 'Undefined Unicorn', - 1729 => 'Hardy-Ramanujan Located' + 381 => 'Unicorn Moved', + 555 => 'Unexpected Minotaur' ); $result = $response->httpCodes($codes); $this->assertTrue($result); $this->assertEquals(42, count($response->httpCodes())); - $result = $response->httpCodes(1337); - $expected = array(1337 => 'Undefined Unicorn'); + $result = $response->httpCodes(381); + $expected = array(381 => 'Unicorn Moved'); $this->assertEquals($expected, $result); $codes = array(404 => 'Sorry Bro'); @@ -390,6 +402,14 @@ public function testHttpCodes() { $result = $response->httpCodes(404); $expected = array(404 => 'Sorry Bro'); $this->assertEquals($expected, $result); + + //Throws exception + $response->httpCodes(array( + 0 => 'Nothing Here', + -1 => 'Reverse Infinity', + 12345 => 'Universal Password', + 'Hello' => 'World' + )); } /** @@ -1385,6 +1405,76 @@ public function testFileExtensionNotSet() { $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS . 'test_2.JPG'); } +/** + * A data provider for testing various ranges + * + * @return array + */ + public static function rangeProvider() { + return array( + // suffix-byte-range + array( + 'bytes=-25', 25, 'bytes 13-37/38' + ), + + array( + 'bytes=0-', 38, 'bytes 0-37/38' + ), + array( + 'bytes=10-', 28, 'bytes 10-37/38' + ), + array( + 'bytes=10-20', 11, 'bytes 10-20/38' + ), + ); + } + +/** + * Test the various range offset types. + * + * @dataProvider rangeProvider + * @return void + */ + public function testFileRangeOffsets($range, $length, $offsetResponse) { + $_SERVER['HTTP_RANGE'] = $range; + $response = $this->getMock('CakeResponse', array( + 'header', + 'type', + '_sendHeader', + '_isActive', + '_clearBuffer', + '_flushBuffer' + )); + + $response->expects($this->at(1)) + ->method('header') + ->with('Content-Disposition', 'attachment; filename="test_asset.css"'); + + $response->expects($this->at(2)) + ->method('header') + ->with('Accept-Ranges', 'bytes'); + + $response->expects($this->at(3)) + ->method('header') + ->with(array( + 'Content-Length' => $length, + 'Content-Range' => $offsetResponse, + )); + + $response->expects($this->any()) + ->method('_isActive') + ->will($this->returnValue(true)); + + $response->file( + CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', + array('download' => true) + ); + + ob_start(); + $result = $response->send(); + ob_get_clean(); + } + /** * Test fetching ranges from a file. * diff --git a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php index 59f27bcab..c1cbf199f 100755 --- a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php +++ b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php @@ -1800,7 +1800,7 @@ public function testBodyEncoding() { $email->to('someone@example.com')->from('someone@example.com'); $result = $email->send('ってテーブルを作ってやってたらう'); $this->assertContains('Content-Type: text/plain; charset=ISO-2022-JP', $result['headers']); - $this->assertContains(mb_convert_encoding('ってテーブルを作ってやってたらう','ISO-2022-JP'), $result['message']); + $this->assertContains(mb_convert_encoding('ってテーブルを作ってやってたらう', 'ISO-2022-JP'), $result['message']); } /** @@ -1824,7 +1824,7 @@ public function testBodyEncodingIso2022Jp() { $result = $email->send('①㈱'); $this->assertTextContains("Content-Type: text/plain; charset=ISO-2022-JP", $result['headers']); $this->assertTextNotContains("Content-Type: text/plain; charset=ISO-2022-JP-MS", $result['headers']); // not charset=iso-2022-jp-ms - $this->assertTextNotContains(mb_convert_encoding('①㈱','ISO-2022-JP-MS'), $result['message']); + $this->assertTextNotContains(mb_convert_encoding('①㈱', 'ISO-2022-JP-MS'), $result['message']); } /** @@ -1848,7 +1848,7 @@ public function testBodyEncodingIso2022JpMs() { $result = $email->send('①㈱'); $this->assertTextContains("Content-Type: text/plain; charset=ISO-2022-JP", $result['headers']); $this->assertTextNotContains("Content-Type: text/plain; charset=iso-2022-jp-ms", $result['headers']); // not charset=iso-2022-jp-ms - $this->assertContains(mb_convert_encoding('①㈱','ISO-2022-JP-MS'), $result['message']); + $this->assertContains(mb_convert_encoding('①㈱', 'ISO-2022-JP-MS'), $result['message']); } protected function _checkContentTransferEncoding($message, $charset) { diff --git a/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php b/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php index 4edf0c696..efd62ca93 100755 --- a/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php @@ -34,6 +34,7 @@ class DebugTransportTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $this->DebugTransport = new DebugTransport(); } diff --git a/lib/Cake/Test/Case/Network/Email/MailTransportTest.php b/lib/Cake/Test/Case/Network/Email/MailTransportTest.php index 9a971d0d8..c255ccd34 100755 --- a/lib/Cake/Test/Case/Network/Email/MailTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/MailTransportTest.php @@ -34,6 +34,7 @@ class MailTransportTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $this->MailTransport = $this->getMock('MailTransport', array('_mail')); $this->MailTransport->config(array('additionalParameters' => '-f')); } diff --git a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php index ab4e1dbca..0904bb062 100755 --- a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php @@ -82,6 +82,7 @@ class SmtpTransportTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); if (!class_exists('MockSocket')) { $this->getMock('CakeSocket', array('read', 'write', 'connect', 'enableCrypto'), array(), 'MockSocket'); } diff --git a/lib/Cake/Test/Case/Network/Http/DigestAuthenticationTest.php b/lib/Cake/Test/Case/Network/Http/DigestAuthenticationTest.php index 69cfb4331..f172e4a7e 100755 --- a/lib/Cake/Test/Case/Network/Http/DigestAuthenticationTest.php +++ b/lib/Cake/Test/Case/Network/Http/DigestAuthenticationTest.php @@ -73,6 +73,7 @@ class DigestAuthenticationTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $this->HttpSocket = new DigestHttpSocket(); $this->HttpSocket->request['method'] = 'GET'; $this->HttpSocket->request['uri']['path'] = '/'; @@ -84,6 +85,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); unset($this->HttpSocket); } diff --git a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php index 9f5fbf255..1273bf1ff 100755 --- a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php @@ -93,6 +93,7 @@ class HttpResponseTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $this->HttpResponse = new TestHttpResponse(); } diff --git a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php index 99c729970..a910bcd14 100755 --- a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php @@ -193,6 +193,7 @@ class HttpSocketTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); if (!class_exists('MockHttpSocket')) { $this->getMock('TestHttpSocket', array('read', 'write', 'connect'), array(), 'MockHttpSocket'); $this->getMock('TestHttpSocket', array('read', 'write', 'connect', 'request'), array(), 'MockHttpSocketRequests'); @@ -208,6 +209,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); unset($this->Socket, $this->RequestSocket); } @@ -1545,8 +1547,8 @@ public function testParseQuery() { 'name' => 'jim', 'items' => array( 'personal' => array( - 'book' - , 'pen' + 'book', + 'pen' ), 'ball' ) @@ -1633,10 +1635,10 @@ public function testTokenEscapeChars() { $this->Socket->reset(); $expected = array( - '\x22','\x28','\x29','\x3c','\x3e','\x40','\x2c','\x3b','\x3a','\x5c','\x2f','\x5b','\x5d','\x3f','\x3d','\x7b', - '\x7d','\x20','\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d', - '\x0e','\x0f','\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d', - '\x1e','\x1f','\x7f' + '\x22', '\x28', '\x29', '\x3c', '\x3e', '\x40', '\x2c', '\x3b', '\x3a', '\x5c', '\x2f', '\x5b', '\x5d', '\x3f', '\x3d', '\x7b', + '\x7d', '\x20', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', + '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', + '\x1e', '\x1f', '\x7f' ); $r = $this->Socket->tokenEscapeChars(); $this->assertEquals($expected, $r); diff --git a/lib/Cake/Test/Case/Routing/DispatcherTest.php b/lib/Cake/Test/Case/Routing/DispatcherTest.php index 8727b78f6..2d10fa241 100755 --- a/lib/Cake/Test/Case/Routing/DispatcherTest.php +++ b/lib/Cake/Test/Case/Routing/DispatcherTest.php @@ -511,6 +511,7 @@ class DispatcherTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $this->_get = $_GET; $_GET = array(); $this->_post = $_POST; @@ -538,6 +539,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); $_GET = $this->_get; $_POST = $this->_post; $_FILES = $this->_files; diff --git a/lib/Cake/Test/Case/Routing/Filter/AssetDispatcherTest.php b/lib/Cake/Test/Case/Routing/Filter/AssetDispatcherTest.php index e8f028301..61f1ee87a 100755 --- a/lib/Cake/Test/Case/Routing/Filter/AssetDispatcherTest.php +++ b/lib/Cake/Test/Case/Routing/Filter/AssetDispatcherTest.php @@ -30,6 +30,7 @@ class AssetDispatcherTest extends CakeTestCase { * @return void */ public function tearDown() { + parent::tearDown(); Configure::write('Dispatcher.filters', array()); } diff --git a/lib/Cake/Test/Case/Routing/Route/CakeRouteTest.php b/lib/Cake/Test/Case/Routing/Route/CakeRouteTest.php index 93fa7c514..298d648c8 100755 --- a/lib/Cake/Test/Case/Routing/Route/CakeRouteTest.php +++ b/lib/Cake/Test/Case/Routing/Route/CakeRouteTest.php @@ -484,6 +484,40 @@ public function testPersistParams() { $this->assertEquals('red', $result['color']); } +/** + * test persist with a non array value + * + * @return void + */ + public function testPersistParamsNonArray() { + $url = array('controller' => 'posts', 'action' => 'index'); + $params = array('lang' => 'en', 'color' => 'blue'); + + $route = new CakeRoute( + '/:lang/:color/blog/:action', + array('controller' => 'posts') + // No persist options + ); + $result = $route->persistParams($url, $params); + $this->assertEquals($url, $result); + + $route = new CakeRoute( + '/:lang/:color/blog/:action', + array('controller' => 'posts'), + array('persist' => false) + ); + $result = $route->persistParams($url, $params); + $this->assertEquals($url, $result); + + $route = new CakeRoute( + '/:lang/:color/blog/:action', + array('controller' => 'posts'), + array('persist' => 'derp') + ); + $result = $route->persistParams($url, $params); + $this->assertEquals($url, $result); + } + /** * test the parse method of CakeRoute. * @@ -889,7 +923,7 @@ public function testParseTrailingUTF8() { public function testUTF8PatternOnSection() { $route = new CakeRoute( '/:section', - array('plugin' => 'blogs', 'controller' => 'posts' , 'action' => 'index' ), + array('plugin' => 'blogs', 'controller' => 'posts', 'action' => 'index'), array( 'persist' => array('section'), 'section' => 'آموزش|weblog' diff --git a/lib/Cake/Test/Case/Routing/RouterTest.php b/lib/Cake/Test/Case/Routing/RouterTest.php index d5379e536..57e480978 100755 --- a/lib/Cake/Test/Case/Routing/RouterTest.php +++ b/lib/Cake/Test/Case/Routing/RouterTest.php @@ -49,16 +49,16 @@ public function setUp() { public function tearDown() { parent::tearDown(); CakePlugin::unload(); - Router::baseURL(''); - Configure::write('App.fullBaseURL', 'http://localhost'); + Router::fullBaseUrl(''); + Configure::write('App.fullBaseUrl', 'http://localhost'); } /** - * testFullBaseURL method + * testFullBaseUrl method * * @return void */ - public function testFullBaseURL() { + public function testFullBaseUrl() { $this->assertRegExp('/^http(s)?:\/\//', Router::url('/', true)); $this->assertRegExp('/^http(s)?:\/\//', Router::url(null, true)); $this->assertRegExp('/^http(s)?:\/\//', Router::url(array('full_base' => true))); @@ -66,18 +66,18 @@ public function testFullBaseURL() { } /** - * Tests that the base URL can be changed at runtime + * Tests that the base URL can be changed at runtime. * * @return void */ - public function testBaseURL() { - $this->assertEquals(FULL_BASE_URL, Router::baseUrl()); - Router::baseURL('http://example.com'); + public function testBaseUrl() { + $this->assertEquals(FULL_BASE_URL, Router::fullBaseUrl()); + Router::fullBaseUrl('http://example.com'); $this->assertEquals('http://example.com/', Router::url('/', true)); - $this->assertEquals('http://example.com', Configure::read('App.fullBaseURL')); - Router::baseURL('https://example.com'); + $this->assertEquals('http://example.com', Configure::read('App.fullBaseUrl')); + Router::fullBaseUrl('https://example.com'); $this->assertEquals('https://example.com/', Router::url('/', true)); - $this->assertEquals('https://example.com', Configure::read('App.fullBaseURL')); + $this->assertEquals('https://example.com', Configure::read('App.fullBaseUrl')); } /** @@ -957,7 +957,7 @@ public function testCanLeavePlugin() { public function testUrlParsing() { extract(Router::getNamedExpressions()); - Router::connect('/posts/:value/:somevalue/:othervalue/*', array('controller' => 'posts', 'action' => 'view'), array('value','somevalue', 'othervalue')); + Router::connect('/posts/:value/:somevalue/:othervalue/*', array('controller' => 'posts', 'action' => 'view'), array('value', 'somevalue', 'othervalue')); $result = Router::parse('/posts/2007/08/01/title-of-post-here'); $expected = array('value' => '2007', 'somevalue' => '08', 'othervalue' => '01', 'controller' => 'posts', 'action' => 'view', 'plugin' => '', 'pass' => array('0' => 'title-of-post-here'), 'named' => array()); $this->assertEquals($expected, $result); @@ -1066,6 +1066,12 @@ public function testUrlParsing() { $result = Router::parse('/posts/view/foo:bar/routing:fun/answer:42'); $expected = array('pass' => array(), 'named' => array('foo' => 'bar', 'routing' => 'fun', 'answer' => '42'), 'plugin' => null, 'controller' => 'posts', 'action' => 'view'); $this->assertEquals($expected, $result); + + Router::reload(); + Router::connect('/posts/view/*', array('controller' => 'posts', 'action' => 'view'), array('named' => array('foo', 'answer'), 'greedyNamed' => true)); + $result = Router::parse('/posts/view/foo:bar/routing:fun/answer:42?id=123&tab=abc'); + $expected = array('pass' => array(), 'named' => array('foo' => 'bar', 'routing' => 'fun', 'answer' => '42'), 'plugin' => null, 'controller' => 'posts', 'action' => 'view', '?' => array('id' => '123', 'tab' => 'abc')); + $this->assertEquals($expected, $result); } /** @@ -1078,13 +1084,13 @@ public function testPersistentParameters() { Router::connect( '/:lang/:color/posts/view/*', array('controller' => 'posts', 'action' => 'view'), - array('persist' => array('lang', 'color') - )); + array('persist' => array('lang', 'color')) + ); Router::connect( '/:lang/:color/posts/index', array('controller' => 'posts', 'action' => 'index'), - array('persist' => array('lang') - )); + array('persist' => array('lang')) + ); Router::connect('/:lang/:color/posts/edit/*', array('controller' => 'posts', 'action' => 'edit')); Router::connect('/about', array('controller' => 'pages', 'action' => 'view', 'about')); Router::parse('/en/red/posts/view/5'); @@ -1182,6 +1188,33 @@ public function testRouteSymmetry() { $this->assertEquals($expected, $result); } +/** + * Test parse and reverse symmetry + * + * @return void + * @dataProvider parseReverseSymmetryData + */ + public function testParseReverseSymmetry($url) { + $this->assertSame($url, Router::reverse(Router::parse($url) + array('url' => array()))); + } + +/** + * Data for parse and reverse test + * + * @return array + */ + public function parseReverseSymmetryData() { + return array( + array('/'), + array('/controller/action'), + array('/controller/action/param'), + array('/controller/action?param1=value1¶m2=value2'), + array('/controller/action/param?param1=value1'), + array('/controller/action/named1:nv1'), + array('/controller/action/named1:nv1?param1=value1') + ); + } + /** * Test that Routing.prefixes are used when a Router instance is created * or reset @@ -1332,9 +1365,11 @@ public function testExtensionParsing() { $this->assertEquals($expected, $result); $result = Router::parse('/posts/view/1.rss?query=test'); + $expected['?'] = array('query' => 'test'); $this->assertEquals($expected, $result); $result = Router::parse('/posts/view/1.atom'); + unset($expected['?']); $expected['ext'] = 'atom'; $this->assertEquals($expected, $result); @@ -1348,7 +1383,7 @@ public function testExtensionParsing() { $this->assertEquals($expected, $result); $result = Router::parse('/posts.atom?hello=goodbye'); - $expected = array('plugin' => null, 'controller' => 'posts.atom', 'action' => 'index', 'pass' => array(), 'named' => array()); + $expected = array('plugin' => null, 'controller' => 'posts.atom', 'action' => 'index', 'pass' => array(), 'named' => array(), '?' => array('hello' => 'goodbye')); $this->assertEquals($expected, $result); Router::reload(); diff --git a/lib/Cake/Test/Case/TestSuite/CakeTestCaseTest.php b/lib/Cake/Test/Case/TestSuite/CakeTestCaseTest.php index e5827089d..462ba78d3 100755 --- a/lib/Cake/Test/Case/TestSuite/CakeTestCaseTest.php +++ b/lib/Cake/Test/Case/TestSuite/CakeTestCaseTest.php @@ -353,6 +353,11 @@ public function testAssertTextNotContains() { * @return void */ public function testGetMockForModel() { + App::build(array( + 'Model' => array( + CAKE . 'Test' . DS . 'test_app' . DS . 'Model' . DS + ) + ), App::RESET); $Post = $this->getMockForModel('Post'); $this->assertInstanceOf('Post', $Post); @@ -372,6 +377,13 @@ public function testGetMockForModel() { * @return void */ public function testGetMockForModelWithPlugin() { + App::build(array( + 'Plugin' => array( + CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS + ) + ), App::RESET); + CakePlugin::load('TestPlugin'); + $this->getMockForModel('TestPlugin.TestPluginAppModel'); $TestPluginComment = $this->getMockForModel('TestPlugin.TestPluginComment'); $result = ClassRegistry::init('TestPlugin.TestPluginComment'); @@ -389,4 +401,37 @@ public function testGetMockForModelWithPlugin() { $this->assertTrue($TestPluginComment->save(array())); $this->assertFalse($TestPluginComment->save(array())); } + +/** + * testGetMockForModelModel + * + * @return void + */ + public function testGetMockForModelModel() { + $Mock = $this->getMockForModel('Model', array('save'), array('name' => 'Comment')); + + $result = ClassRegistry::init('Comment'); + $this->assertInstanceOf('Model', $result); + + $Mock->expects($this->at(0)) + ->method('save') + ->will($this->returnValue(true)); + $Mock->expects($this->at(1)) + ->method('save') + ->will($this->returnValue(false)); + + $this->assertTrue($Mock->save(array())); + $this->assertFalse($Mock->save(array())); + } + +/** + * testGetMockForModelDoesNotExist + * + * @expectedException MissingModelException + * @expectedExceptionMessage Model IDoNotExist could not be found + * @return void + */ + public function testGetMockForModelDoesNotExist() { + $this->getMockForModel('IDoNotExist'); + } } diff --git a/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php b/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php index 906503273..23cca570d 100755 --- a/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php +++ b/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php @@ -189,6 +189,7 @@ class CakeTestFixtureTest extends CakeTestCase { * @return void */ public function setUp() { + parent::setUp(); $methods = array_diff(get_class_methods('DboSource'), array('enabled')); $methods[] = 'connect'; @@ -204,6 +205,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); unset($this->criticDb); $this->db->config = $this->_backupConfig; } diff --git a/lib/Cake/Test/Case/Utility/CakeNumberTest.php b/lib/Cake/Test/Case/Utility/CakeNumberTest.php index fafbe8a19..4aac5d564 100755 --- a/lib/Cake/Test/Case/Utility/CakeNumberTest.php +++ b/lib/Cake/Test/Case/Utility/CakeNumberTest.php @@ -301,6 +301,40 @@ public function testCurrency() { $this->assertEquals($expected, $result); } +/** + * Test currency format with places and fraction exponents. + * Places should only matter for non fraction values and vice versa. + * + * @return void + */ + public function testCurrencyWithFractionAndPlaces() { + $result = $this->Number->currency('1.23', 'GBP', array('places' => 3)); + $expected = '£1.230'; + $this->assertEquals($expected, $result); + + $result = $this->Number->currency('0.23', 'GBP', array('places' => 3)); + $expected = '23p'; + $this->assertEquals($expected, $result); + + $result = $this->Number->currency('0.001', 'GBP', array('places' => 3)); + $expected = '0p'; + $this->assertEquals($expected, $result); + + $this->Number->addFormat('BHD', array('before' => 'BD ', 'fractionSymbol' => ' fils', + 'fractionExponent' => 3)); + $result = $this->Number->currency('1.234', 'BHD', array('places' => 2)); + $expected = 'BD 1.23'; + $this->assertEquals($expected, $result); + + $result = $this->Number->currency('0.234', 'BHD', array('places' => 2)); + $expected = '234 fils'; + $this->assertEquals($expected, $result); + + $result = $this->Number->currency('0.001', 'BHD', array('places' => 2)); + $expected = '1 fils'; + $this->assertEquals($expected, $result); + } + /** * Test adding currency format options to the number helper * diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index 52bd2d1bd..ce96d8ccb 100755 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -52,6 +52,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); unset($this->Time); $this->_restoreSystemTimezone(); } diff --git a/lib/Cake/Test/Case/Utility/DebuggerTest.php b/lib/Cake/Test/Case/Utility/DebuggerTest.php index 43e9f1d23..a1b5fcb54 100755 --- a/lib/Cake/Test/Case/Utility/DebuggerTest.php +++ b/lib/Cake/Test/Case/Utility/DebuggerTest.php @@ -408,6 +408,11 @@ public function testExportVar() { false TEXT; $this->assertTextEquals($expected, $result); + + $file = fopen('php://output', 'w'); + fclose($file); + $result = Debugger::exportVar($file); + $this->assertTextEquals('unknown', $result); } /** diff --git a/lib/Cake/Test/Case/Utility/SetTest.php b/lib/Cake/Test/Case/Utility/SetTest.php index e4ecca3bc..ad5e76040 100755 --- a/lib/Cake/Test/Case/Utility/SetTest.php +++ b/lib/Cake/Test/Case/Utility/SetTest.php @@ -256,7 +256,7 @@ public function testSort() { $a = array( 0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))), - 1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay'))), + 1 => array('Person' => array('name' => 'Tracy'), 'Friend' => array(array('name' => 'Lindsay'))), 2 => array('Person' => array('name' => 'Adam'), 'Friend' => array(array('name' => 'Bob'))) ); $b = array( @@ -268,15 +268,15 @@ public function testSort() { $this->assertEquals($a, $b); $a = array( - array(7,6,4), - array(3,4,5), - array(3,2,1), + array(7, 6, 4), + array(3, 4, 5), + array(3, 2, 1), ); $b = array( - array(3,2,1), - array(3,4,5), - array(7,6,4), + array(3, 2, 1), + array(3, 4, 5), + array(7, 6, 4), ); $a = Set::sort($a, '{n}.{n}', 'asc'); @@ -884,7 +884,7 @@ public function testExtract() { $r = Set::extract('/file/.[type=application/x-zip-compressed]', $f); $this->assertEquals($expected, $r); - $expected = array(array('name' => 'zipfile.zip', 'type' => 'application/zip','tmp_name' => '/tmp/php178.tmp', 'error' => 0, 'size' => '564647')); + $expected = array(array('name' => 'zipfile.zip', 'type' => 'application/zip', 'tmp_name' => '/tmp/php178.tmp', 'error' => 0, 'size' => '564647')); $r = Set::extract('/file/.[type=application/zip]', $f); $this->assertEquals($expected, $r); @@ -922,8 +922,8 @@ public function testExtract() { $this->assertEquals($expected, $r); $expected = array( - array('name' => 'zipfile.zip','type' => 'application/zip', 'tmp_name' => '/tmp/php178.tmp', 'error' => 0, 'size' => '564647'), - array('name' => 'zipfile2.zip','type' => 'application/x zip compressed', 'tmp_name' => '/tmp/php179.tmp', 'error' => 0, 'size' => '354784') + array('name' => 'zipfile.zip', 'type' => 'application/zip', 'tmp_name' => '/tmp/php178.tmp', 'error' => 0, 'size' => '564647'), + array('name' => 'zipfile2.zip', 'type' => 'application/x zip compressed', 'tmp_name' => '/tmp/php179.tmp', 'error' => 0, 'size' => '354784') ); $r = Set::extract('/file/.[tmp_name=/tmp\/php17/]', $f); $this->assertEquals($expected, $r); @@ -1110,7 +1110,7 @@ public function testExtractParentSelector() { ) ); - $expected = array(7,2,1); + $expected = array(7, 2, 1); $r = Set::extract('/CallType[name=Internal Voice]/../x/hour', $multiple); $this->assertEquals($expected, $r); @@ -1856,7 +1856,7 @@ public function testCombine() { $a = array( array('User' => array('id' => 2, 'group_id' => 1, - 'Data' => array('user' => 'mariano.iglesias','name' => 'Mariano Iglesias'))), + 'Data' => array('user' => 'mariano.iglesias', 'name' => 'Mariano Iglesias'))), array('User' => array('id' => 14, 'group_id' => 2, 'Data' => array('user' => 'phpnut', 'name' => 'Larry E. Masters'))), array('User' => array('id' => 25, 'group_id' => 1, @@ -2470,8 +2470,8 @@ public function testNestedMappedData() { array( 'Post' => array('id' => '1', 'author_id' => '1', 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), 'Author' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31', 'test' => 'working'), - ) - , array( + ), + array( 'Post' => array('id' => '2', 'author_id' => '3', 'title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), 'Author' => array('id' => '3', 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31', 'test' => 'working'), ) diff --git a/lib/Cake/Test/Case/Utility/StringTest.php b/lib/Cake/Test/Case/Utility/StringTest.php index 9d0ba7aa2..efe89f033 100755 --- a/lib/Cake/Test/Case/Utility/StringTest.php +++ b/lib/Cake/Test/Case/Utility/StringTest.php @@ -180,7 +180,7 @@ public function testInsert() { $expected = "this is a long string with a few? params you know"; $this->assertEquals($expected, $result); - $result = String::insert('update saved_urls set url = :url where id = :id', array('url' => 'http://www.testurl.com/param1:url/param2:id','id' => 1)); + $result = String::insert('update saved_urls set url = :url where id = :id', array('url' => 'http://www.testurl.com/param1:url/param2:id', 'id' => 1)); $expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1"; $this->assertEquals($expected, $result); @@ -330,14 +330,12 @@ public function testWrap() { $this->assertTextEquals($expected, $result, 'Text not wrapped.'); $result = String::wrap($text, array('width' => 20, 'wordWrap' => false)); - $expected = <<assertTextEquals($expected, $result, 'Text not wrapped.'); } diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index f36937abc..04d0884e5 100755 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -2129,8 +2129,28 @@ public function testPhone() { $this->assertFalse(Validation::phone('(055) 999-9999')); $this->assertFalse(Validation::phone('(155) 999-9999')); $this->assertFalse(Validation::phone('(595) 999-9999')); - $this->assertFalse(Validation::phone('(555) 099-9999')); - $this->assertFalse(Validation::phone('(555) 199-9999')); + $this->assertFalse(Validation::phone('(213) 099-9999')); + $this->assertFalse(Validation::phone('(213) 199-9999')); + + // invalid area-codes + $this->assertFalse(Validation::phone('1-(511)-999-9999')); + $this->assertFalse(Validation::phone('1-(379)-999-9999')); + $this->assertFalse(Validation::phone('1-(962)-999-9999')); + $this->assertFalse(Validation::phone('1-(295)-999-9999')); + $this->assertFalse(Validation::phone('1-(555)-999-9999')); + + // invalid exhange + $this->assertFalse(Validation::phone('1-(222)-511-9999')); + + // invalid phone number + $this->assertFalse(Validation::phone('1-(222)-555-0199')); + $this->assertFalse(Validation::phone('1-(222)-555-0122')); + + // valid phone numbers + $this->assertTrue(Validation::phone('1-(369)-333-4444')); + $this->assertTrue(Validation::phone('1-(973)-333-4444')); + $this->assertTrue(Validation::phone('1-(313)-555-9999')); + $this->assertTrue(Validation::phone('1-(222)-555-0299')); $this->assertTrue(Validation::phone('1 (222) 333 4444')); $this->assertTrue(Validation::phone('+1 (222) 333 4444')); diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 6fd690eb3..ba7792f73 100755 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -1388,7 +1388,7 @@ public function testDisableSecurityUsingForm() { $this->Form->input('Addresses.first_name', array('secure' => false)); $this->Form->input('Addresses.city', array('type' => 'textarea', 'secure' => false)); $this->Form->input('Addresses.zip', array( - 'type' => 'select', 'options' => array(1,2), 'secure' => false + 'type' => 'select', 'options' => array(1, 2), 'secure' => false )); $result = $this->Form->fields; @@ -1719,7 +1719,7 @@ public function testFormValidationAssociatedSecondLevel() { $result = $this->Form->create('ValidateUser', array('type' => 'post', 'action' => 'add')); $encoding = strtolower(Configure::read('App.encoding')); $expected = array( - 'form' => array('method' => 'post', 'action' => '/validate_users/add', 'id','accept-charset' => $encoding), + 'form' => array('method' => 'post', 'action' => '/validate_users/add', 'id', 'accept-charset' => $encoding), 'div' => array('style' => 'display:none;'), 'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'), '/div' @@ -2267,8 +2267,8 @@ public function testInputTime() { $this->assertRegExp('##', $result[1]); $result = $this->Form->input('prueba', array( - 'type' => 'time', 'timeFormat' => 24 , 'dateFormat' => 'DMY' , 'minYear' => 2008, - 'maxYear' => date('Y') + 1 , 'interval' => 15 + 'type' => 'time', 'timeFormat' => 24, 'dateFormat' => 'DMY', 'minYear' => 2008, + 'maxYear' => date('Y') + 1, 'interval' => 15 )); $result = explode(':', $result); $this->assertNotRegExp('##', $result[1]); @@ -5877,6 +5877,33 @@ public function testDateTime() { $this->assertRegExp('/]+value="35"[^<>]+selected="selected"[^>]*>35<\/option>/', $result); } +/** + * Test dateTime with rounding + * + * @return void + */ + public function testDateTimeRounding() { + $this->Form->request->data['Contact'] = array( + 'date' => array( + 'day' => '13', + 'month' => '12', + 'year' => '2010', + 'hour' => '04', + 'min' => '19', + 'meridian' => 'AM' + ) + ); + + $result = $this->Form->dateTime('Contact.date', 'DMY', '12', array('interval' => 15)); + $this->assertTextContains('', $result); + + $result = $this->Form->dateTime('Contact.date', 'DMY', '12', array('interval' => 15, 'round' => 'up')); + $this->assertTextContains('', $result); + + $result = $this->Form->dateTime('Contact.date', 'DMY', '12', array('interval' => 5, 'round' => 'down')); + $this->assertTextContains('', $result); + } + /** * Test that empty values don't trigger errors. * @@ -7018,7 +7045,7 @@ public function testPostLink() { ), 'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'), '/form', - 'a' => array('href' => '#', 'onclick' => 'preg:/if \(confirm\('Confirm\?'\)\) \{ document\.post_\w+\.submit\(\); \} event\.returnValue = false; return false;/'), + 'a' => array('href' => '#', 'onclick' => 'preg:/if \(confirm\("Confirm\?"\)\) \{ document\.post_\w+\.submit\(\); \} event\.returnValue = false; return false;/'), 'Delete', '/a' )); @@ -8054,6 +8081,32 @@ public function testFormInputRequiredDetection() { $this->assertTags($result, $expected); } +/** + * Test that required fields are created when only using ModelValidator::add(). + * + * @return void + */ + public function testFormInputRequiredDetectionModelValidator() { + ClassRegistry::getObject('ContactTag')->validator()->add('iwillberequired', 'required', array('rule' => 'notEmpty')); + + $this->Form->create('ContactTag'); + $result = $this->Form->input('ContactTag.iwillberequired'); + $expected = array( + 'div' => array('class' => 'input text required'), + 'label' => array('for' => 'ContactTagIwillberequired'), + 'Iwillberequired', + '/label', + 'input' => array( + 'name' => 'data[ContactTag][iwillberequired]', + 'type' => 'text', + 'id' => 'ContactTagIwillberequired', + 'required' => 'required' + ), + '/div' + ); + $this->assertTags($result, $expected); + } + /** * testFormMagicInput method * diff --git a/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php b/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php index d8be24ab1..7ca4e109b 100755 --- a/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php @@ -210,12 +210,12 @@ public function testLink() { Router::reload(); $result = $this->Html->link('Posts', array('controller' => 'posts', 'action' => 'index', 'full_base' => true)); - $expected = array('a' => array('href' => Router::baseURL() . '/posts'), 'Posts', '/a'); + $expected = array('a' => array('href' => Router::fullBaseUrl() . '/posts'), 'Posts', '/a'); $this->assertTags($result, $expected); $result = $this->Html->link('Home', '/home', array('confirm' => 'Are you sure you want to do this?')); $expected = array( - 'a' => array('href' => '/home', 'onclick' => 'return confirm('Are you sure you want to do this?');'), + 'a' => array('href' => '/home', 'onclick' => 'if (confirm("Are you sure you want to do this?")) { return true; } return false;'), 'Home', '/a' ); @@ -400,6 +400,9 @@ public function testImageTag() { $result = $this->Html->image('http://google.com/logo.gif'); $this->assertTags($result, array('img' => array('src' => 'http://google.com/logo.gif', 'alt' => ''))); + $result = $this->Html->image('//google.com/logo.gif'); + $this->assertTags($result, array('img' => array('src' => '//google.com/logo.gif', 'alt' => ''))); + $result = $this->Html->image(array('controller' => 'test', 'action' => 'view', 1, 'ext' => 'gif')); $this->assertTags($result, array('img' => array('src' => '/test/view/1.gif', 'alt' => ''))); @@ -408,6 +411,21 @@ public function testImageTag() { $result = $this->Html->image('test.gif?one=two&three=four'); $this->assertTags($result, array('img' => array('src' => 'img/test.gif?one=two&three=four', 'alt' => ''))); + + $result = $this->Html->image('test.gif', array('pathPrefix' => '/my/custom/path/')); + $this->assertTags($result, array('img' => array('src' => '/my/custom/path/test.gif', 'alt' => ''))); + + $result = $this->Html->image('test.gif', array('pathPrefix' => 'http://cakephp.org/assets/img/')); + $this->assertTags($result, array('img' => array('src' => 'http://cakephp.org/assets/img/test.gif', 'alt' => ''))); + + $result = $this->Html->image('test.gif', array('pathPrefix' => '//cakephp.org/assets/img/')); + $this->assertTags($result, array('img' => array('src' => '//cakephp.org/assets/img/test.gif', 'alt' => ''))); + + $previousConfig = Configure::read('App.imageBaseUrl'); + Configure::write('App.imageBaseUrl', '//cdn.cakephp.org/img/'); + $result = $this->Html->image('test.gif'); + $this->assertTags($result, array('img' => array('src' => '//cdn.cakephp.org/img/test.gif', 'alt' => ''))); + Configure::write('App.imageBaseUrl', $previousConfig); } /** @@ -593,6 +611,21 @@ public function testCssLink() { $expected['link']['href'] = 'preg:/http:\/\/.*\/screen\.css\?1234/'; $this->assertTags($result, $expected); + $result = $this->Html->css('cake.generic', array('pathPrefix' => '/my/custom/path/')); + $expected['link']['href'] = '/my/custom/path/cake.generic.css'; + $this->assertTags($result, $expected); + + $result = $this->Html->css('cake.generic', array('pathPrefix' => 'http://cakephp.org/assets/css/')); + $expected['link']['href'] = 'http://cakephp.org/assets/css/cake.generic.css'; + $this->assertTags($result, $expected); + + $previousConfig = Configure::read('App.cssBaseUrl'); + Configure::write('App.cssBaseUrl', '//cdn.cakephp.org/css/'); + $result = $this->Html->css('cake.generic'); + $expected['link']['href'] = '//cdn.cakephp.org/css/cake.generic.css'; + $this->assertTags($result, $expected); + Configure::write('App.cssBaseUrl', $previousConfig); + Configure::write('Asset.filter.css', 'css.php'); $result = $this->Html->css('cake.generic'); $expected['link']['href'] = 'preg:/.*ccss\/cake\.generic\.css/'; @@ -926,6 +959,27 @@ public function testScript() { ); $this->assertTags($result, $expected); + $result = $this->Html->script('foo2', array('pathPrefix' => '/my/custom/path/')); + $expected = array( + 'script' => array('type' => 'text/javascript', 'src' => '/my/custom/path/foo2.js') + ); + $this->assertTags($result, $expected); + + $result = $this->Html->script('foo3', array('pathPrefix' => 'http://cakephp.org/assets/js/')); + $expected = array( + 'script' => array('type' => 'text/javascript', 'src' => 'http://cakephp.org/assets/js/foo3.js') + ); + $this->assertTags($result, $expected); + + $previousConfig = Configure::read('App.jsBaseUrl'); + Configure::write('App.jsBaseUrl', '//cdn.cakephp.org/js/'); + $result = $this->Html->script('foo4'); + $expected = array( + 'script' => array('type' => 'text/javascript', 'src' => '//cdn.cakephp.org/js/foo4.js') + ); + $this->assertTags($result, $expected); + Configure::write('App.jsBaseUrl', $previousConfig); + $result = $this->Html->script('foo'); $this->assertNull($result, 'Script returned upon duplicate inclusion %s'); @@ -1778,9 +1832,6 @@ public function testTag() { $result = $this->Html->tag('div', 'text'); $this->assertTags($result, 'Html->tag('div', '', 'class-name'); - $this->assertTags($result, array('div' => array('class' => 'class-name'), 'preg://', '/div')); - $result = $this->Html->tag('div', '', array('class' => 'class-name', 'escape' => true)); $this->assertTags($result, array('div' => array('class' => 'class-name'), '<text>', '/div')); @@ -1877,7 +1928,7 @@ public function testMedia() { array('pathPrefix' => 'videos/', 'poster' => 'poster.jpg', 'text' => 'Your browser does not support the HTML5 Video element.') ); $expected = array( - 'video' => array('poster' => IMAGES_URL . 'poster.jpg'), + 'video' => array('poster' => Configure::read('App.imageBaseUrl') . 'poster.jpg'), array('source' => array('src' => 'videos/video.webm', 'type' => 'video/webm')), array('source' => array('src' => 'videos/video.ogv', 'type' => 'video/ogg; codecs='theora, vorbis'')), 'Your browser does not support the HTML5 Video element.', diff --git a/lib/Cake/Test/Case/View/Helper/JsHelperTest.php b/lib/Cake/Test/Case/View/Helper/JsHelperTest.php index 1214330fd..9171db832 100755 --- a/lib/Cake/Test/Case/View/Helper/JsHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/JsHelperTest.php @@ -873,7 +873,7 @@ public function testObject() { $object = new JsEncodingObject(); $object->title = 'New thing'; - $object->indexes = array(5,6,7,8); + $object->indexes = array(5, 6, 7, 8); $result = $this->JsEngine->object($object); $this->assertEquals($expected, $result); diff --git a/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php b/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php index 34c6165f6..543665a11 100755 --- a/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php @@ -266,7 +266,7 @@ public function testDrag() { 'start' => 'onStart', 'drag' => 'onDrag', 'stop' => 'onStop', - 'snapGrid' => array(10,10), + 'snapGrid' => array(10, 10), 'wrapCallbacks' => false )); $expected = '$("drag-me").makeDraggable({onComplete:onStop, onDrag:onDrag, onStart:onStart, snap:[10,10]});'; diff --git a/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php b/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php index 88d5425b1..294c81367 100755 --- a/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php @@ -79,6 +79,7 @@ public function setUp() { * @return void */ public function tearDown() { + parent::tearDown(); unset($this->View, $this->Paginator); } @@ -289,7 +290,7 @@ public function testSortLinksUsingDirectionOption() { Router::setRequestInfo(array( array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(), 'url' => array('url' => 'accounts/', 'mod_rewrite' => 'true')), - array('base' => '/', 'here' => '/accounts/', 'webroot' => '/',) + array('base' => '/', 'here' => '/accounts/', 'webroot' => '/') )); $this->Paginator->options(array('url' => array('param'))); diff --git a/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php b/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php index 3fb7c13ef..248317f20 100755 --- a/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php @@ -87,7 +87,7 @@ public function testTimeHelperProxyMethodCalls() { 'nice', 'niceShort', 'daysAsSql', 'dayAsSql', 'isToday', 'isThisMonth', 'isThisYear', 'wasYesterday', 'isTomorrow', 'toQuarter', 'toUnix', 'toAtom', 'toRSS', - 'timeAgoInWords', 'wasWithinLast', 'gmt', 'format', 'i18nFormat', + 'wasWithinLast', 'gmt', 'format', 'i18nFormat', ); $CakeTime = $this->getMock('CakeTimeMock', $methods); $Time = new TimeHelperTestObject($this->View, array('engine' => 'CakeTimeMock')); @@ -96,6 +96,12 @@ public function testTimeHelperProxyMethodCalls() { $CakeTime->expects($this->at(0))->method($method); $Time->{$method}('who', 'what', 'when', 'where', 'how'); } + + $CakeTime = $this->getMock('CakeTimeMock', array('timeAgoInWords')); + $Time = new TimeHelperTestObject($this->View, array('engine' => 'CakeTimeMock')); + $Time->attach($CakeTime); + $CakeTime->expects($this->at(0))->method('timeAgoInWords'); + $Time->timeAgoInWords('who', array('what'), array('when'), array('where'), array('how')); } /** diff --git a/lib/Cake/Test/Case/View/HelperTest.php b/lib/Cake/Test/Case/View/HelperTest.php index a35dcac3d..96648e508 100755 --- a/lib/Cake/Test/Case/View/HelperTest.php +++ b/lib/Cake/Test/Case/View/HelperTest.php @@ -600,8 +600,8 @@ public function testUrlConversion() { public function testAssetTimestamp() { Configure::write('Foo.bar', 'test'); Configure::write('Asset.timestamp', false); - $result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css'); - $this->assertEquals(CSS_URL . 'cake.generic.css', $result); + $result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css'); + $this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css', $result); Configure::write('Asset.timestamp', true); Configure::write('debug', 0); @@ -609,25 +609,25 @@ public function testAssetTimestamp() { $result = $this->Helper->assetTimestamp('/%3Cb%3E/cake.generic.css'); $this->assertEquals('/%3Cb%3E/cake.generic.css', $result); - $result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css'); - $this->assertEquals(CSS_URL . 'cake.generic.css', $result); + $result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css'); + $this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css', $result); Configure::write('Asset.timestamp', true); Configure::write('debug', 2); - $result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css'); - $this->assertRegExp('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result); + $result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css'); + $this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result); Configure::write('Asset.timestamp', 'force'); Configure::write('debug', 0); - $result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css'); - $this->assertRegExp('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result); + $result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css'); + $this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result); - $result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css?someparam'); - $this->assertEquals(CSS_URL . 'cake.generic.css?someparam', $result); + $result = $this->Helper->assetTimestamp(Configure::read('App.cssBaseUrl') . 'cake.generic.css?someparam'); + $this->assertEquals(Configure::read('App.cssBaseUrl') . 'cake.generic.css?someparam', $result); $this->Helper->request->webroot = '/some/dir/'; - $result = $this->Helper->assetTimestamp('/some/dir/' . CSS_URL . 'cake.generic.css'); - $this->assertRegExp('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result); + $result = $this->Helper->assetTimestamp('/some/dir/' . Configure::read('App.cssBaseUrl') . 'cake.generic.css'); + $this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result); } /** @@ -644,13 +644,13 @@ public function testAssetUrl() { ), array('fullBase' => true) ); - $this->assertEquals(Router::baseURL() . '/js/post.js', $result); + $this->assertEquals(Router::fullBaseUrl() . '/js/post.js', $result); $result = $this->Helper->assetUrl('foo.jpg', array('pathPrefix' => 'img/')); $this->assertEquals('img/foo.jpg', $result); $result = $this->Helper->assetUrl('foo.jpg', array('fullBase' => true)); - $this->assertEquals(Router::baseURL() . '/foo.jpg', $result); + $this->assertEquals(Router::fullBaseUrl() . '/foo.jpg', $result); $result = $this->Helper->assetUrl('style', array('ext' => '.css')); $this->assertEquals('style.css', $result); @@ -708,8 +708,8 @@ public function testAssetUrlTimestampForce() { $this->Helper->webroot = ''; Configure::write('Asset.timestamp', 'force'); - $result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => CSS_URL)); - $this->assertRegExp('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result); + $result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => Configure::read('App.cssBaseUrl'))); + $this->assertRegExp('/' . preg_quote(Configure::read('App.cssBaseUrl') . 'cake.generic.css?', '/') . '[0-9]+/', $result); } /** diff --git a/lib/Cake/Test/Case/View/JsonViewTest.php b/lib/Cake/Test/Case/View/JsonViewTest.php index 01547024a..db30e3e85 100755 --- a/lib/Cake/Test/Case/View/JsonViewTest.php +++ b/lib/Cake/Test/Case/View/JsonViewTest.php @@ -30,6 +30,11 @@ */ class JsonViewTest extends CakeTestCase { + public function setUp() { + parent::setUp(); + Configure::write('debug', 0); + } + /** * testRenderWithoutView method * diff --git a/lib/Cake/Test/Case/View/ViewTest.php b/lib/Cake/Test/Case/View/ViewTest.php index ea3aedd5c..fb20bc1b9 100755 --- a/lib/Cake/Test/Case/View/ViewTest.php +++ b/lib/Cake/Test/Case/View/ViewTest.php @@ -106,7 +106,7 @@ class TestThemeView extends View { * * @param string $name * @param array $params - * @return void + * @return string The given name */ public function renderElement($name, $params = array()) { return $name; @@ -115,8 +115,8 @@ public function renderElement($name, $params = array()) { /** * getViewFileName method * - * @param string $name - * @return void + * @param string $name Controller action to find template filename for + * @return string Template filename */ public function getViewFileName($name = null) { return $this->_getViewFileName($name); @@ -125,8 +125,8 @@ public function getViewFileName($name = null) { /** * getLayoutFileName method * - * @param string $name - * @return void + * @param string $name The name of the layout to find. + * @return string Filename for layout file (.ctp). */ public function getLayoutFileName($name = null) { return $this->_getLayoutFileName($name); @@ -144,8 +144,8 @@ class TestView extends View { /** * getViewFileName method * - * @param string $name - * @return void + * @param string $name Controller action to find template filename for + * @return string Template filename */ public function getViewFileName($name = null) { return $this->_getViewFileName($name); @@ -154,8 +154,8 @@ public function getViewFileName($name = null) { /** * getLayoutFileName method * - * @param string $name - * @return void + * @param string $name The name of the layout to find. + * @return string Filename for layout file (.ctp). */ public function getLayoutFileName($name = null) { return $this->_getLayoutFileName($name); @@ -164,9 +164,9 @@ public function getLayoutFileName($name = null) { /** * paths method * - * @param string $plugin - * @param boolean $cached - * @return void + * @param string $plugin Optional plugin name to scan for view files. + * @param boolean $cached Set to true to force a refresh of view paths. + * @return array paths */ public function paths($plugin = null, $cached = true) { return $this->_paths($plugin, $cached); @@ -200,6 +200,7 @@ class TestAfterHelper extends Helper { /** * beforeLayout method * + * @param string $viewFile * @return void */ public function beforeLayout($viewFile) { @@ -209,6 +210,7 @@ public function beforeLayout($viewFile) { /** * afterLayout method * + * @param string $layoutFile * @return void */ public function afterLayout($layoutFile) { @@ -529,7 +531,7 @@ public function testMissingView() { $View = new TestView($this->Controller); ob_start(); - $result = $View->getViewFileName('does_not_exist'); + $View->getViewFileName('does_not_exist'); $this->ThemeController->plugin = null; $this->ThemeController->name = 'Pages'; @@ -557,8 +559,8 @@ public function testMissingLayout() { $View = new TestView($this->Controller); ob_start(); - $result = $View->getLayoutFileName(); - $expected = ob_get_clean(); + $View->getLayoutFileName(); + ob_get_clean(); $this->ThemeController->plugin = null; $this->ThemeController->name = 'Posts'; @@ -567,7 +569,7 @@ public function testMissingLayout() { $this->ThemeController->theme = 'my_theme'; $View = new TestThemeView($this->ThemeController); - $result = $View->getLayoutFileName(); + $View->getLayoutFileName(); } /** @@ -762,7 +764,7 @@ public function testElementCache() { $result = Cache::read('element__test_element_cache_callbacks_param_foo', 'test_view'); $this->assertEquals($expected, $result); - $result = $View->element('test_element', array( + $View->element('test_element', array( 'param' => 'one', 'foo' => 'two' ), array( @@ -772,7 +774,7 @@ public function testElementCache() { $this->assertEquals($expected, $result); $View->elementCache = 'default'; - $result = $View->element('test_element', array( + $View->element('test_element', array( 'param' => 'one', 'foo' => 'two' ), array( @@ -1310,6 +1312,17 @@ public function testBlockSet() { $this->assertEquals('Block content', $result); } +/** + * Test resetting a block's content. + * + * @return void + */ + public function testBlockReset() { + $this->View->assign('test', ''); + $result = $this->View->fetch('test', 'This should not be returned'); + $this->assertSame('', $result); + } + /** * Test appending to a block with append. * diff --git a/lib/Cake/Test/Case/View/XmlViewTest.php b/lib/Cake/Test/Case/View/XmlViewTest.php index 8d150e737..b6f860947 100755 --- a/lib/Cake/Test/Case/View/XmlViewTest.php +++ b/lib/Cake/Test/Case/View/XmlViewTest.php @@ -30,6 +30,11 @@ */ class XmlViewTest extends CakeTestCase { + public function setUp() { + parent::setUp(); + Configure::write('debug', 0); + } + /** * testRenderWithoutView method * diff --git a/lib/Cake/Test/Fixture/AcoTwoFixture.php b/lib/Cake/Test/Fixture/AcoTwoFixture.php index 376a5c0fa..02d5485d7 100755 --- a/lib/Cake/Test/Fixture/AcoTwoFixture.php +++ b/lib/Cake/Test/Fixture/AcoTwoFixture.php @@ -47,13 +47,13 @@ class AcoTwoFixture extends CakeTestFixture { */ public $records = array( array('parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'ROOT', 'lft' => 1, 'rght' => 20), - array('parent_id' => 1, 'model' => null, 'foreign_key' => null, 'alias' => 'tpsReports','lft' => 2, 'rght' => 9), + array('parent_id' => 1, 'model' => null, 'foreign_key' => null, 'alias' => 'tpsReports', 'lft' => 2, 'rght' => 9), array('parent_id' => 2, 'model' => null, 'foreign_key' => null, 'alias' => 'view', 'lft' => 3, 'rght' => 6), array('parent_id' => 3, 'model' => null, 'foreign_key' => null, 'alias' => 'current', 'lft' => 4, 'rght' => 5), array('parent_id' => 2, 'model' => null, 'foreign_key' => null, 'alias' => 'update', 'lft' => 7, 'rght' => 8), array('parent_id' => 1, 'model' => null, 'foreign_key' => null, 'alias' => 'printers', 'lft' => 10, 'rght' => 19), array('parent_id' => 6, 'model' => null, 'foreign_key' => null, 'alias' => 'print', 'lft' => 11, 'rght' => 14), - array('parent_id' => 7, 'model' => null, 'foreign_key' => null, 'alias' => 'lettersize','lft' => 12, 'rght' => 13), + array('parent_id' => 7, 'model' => null, 'foreign_key' => null, 'alias' => 'lettersize', 'lft' => 12, 'rght' => 13), array('parent_id' => 6, 'model' => null, 'foreign_key' => null, 'alias' => 'refill', 'lft' => 15, 'rght' => 16), array('parent_id' => 6, 'model' => null, 'foreign_key' => null, 'alias' => 'smash', 'lft' => 17, 'rght' => 18), ); diff --git a/lib/Cake/Test/Fixture/AroTwoFixture.php b/lib/Cake/Test/Fixture/AroTwoFixture.php index 576b07bda..33ce93efd 100755 --- a/lib/Cake/Test/Fixture/AroTwoFixture.php +++ b/lib/Cake/Test/Fixture/AroTwoFixture.php @@ -51,8 +51,8 @@ class AroTwoFixture extends CakeTestFixture { array('parent_id' => 1, 'model' => 'Group', 'foreign_key' => '2', 'alias' => 'managers', 'lft' => '6', 'rght' => '9'), array('parent_id' => 1, 'model' => 'Group', 'foreign_key' => '3', 'alias' => 'users', 'lft' => '10', 'rght' => '19'), array('parent_id' => 2, 'model' => 'User', 'foreign_key' => '1', 'alias' => 'Bobs', 'lft' => '3', 'rght' => '4'), - array('parent_id' => 3, 'model' => 'User', 'foreign_key' => '2', 'alias' => 'Lumbergh', 'lft' => '7' , 'rght' => '8'), - array('parent_id' => 4, 'model' => 'User', 'foreign_key' => '3', 'alias' => 'Samir', 'lft' => '11' , 'rght' => '12'), + array('parent_id' => 3, 'model' => 'User', 'foreign_key' => '2', 'alias' => 'Lumbergh', 'lft' => '7', 'rght' => '8'), + array('parent_id' => 4, 'model' => 'User', 'foreign_key' => '3', 'alias' => 'Samir', 'lft' => '11', 'rght' => '12'), array('parent_id' => 4, 'model' => 'User', 'foreign_key' => '4', 'alias' => 'Micheal', 'lft' => '13', 'rght' => '14'), array('parent_id' => 4, 'model' => 'User', 'foreign_key' => '5', 'alias' => 'Peter', 'lft' => '15', 'rght' => '16'), array('parent_id' => 4, 'model' => 'User', 'foreign_key' => '6', 'alias' => 'Milton', 'lft' => '17', 'rght' => '18'), diff --git a/lib/Cake/Test/Fixture/FlagTreeFixture.php b/lib/Cake/Test/Fixture/FlagTreeFixture.php index 8d3268e30..d4f4735d4 100755 --- a/lib/Cake/Test/Fixture/FlagTreeFixture.php +++ b/lib/Cake/Test/Fixture/FlagTreeFixture.php @@ -35,11 +35,11 @@ class FlagTreeFixture extends CakeTestFixture { * @var array */ public $fields = array( - 'id' => array('type' => 'integer','key' => 'primary'), - 'name' => array('type' => 'string','null' => false), + 'id' => array('type' => 'integer', 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false), 'parent_id' => 'integer', - 'lft' => array('type' => 'integer','null' => false), - 'rght' => array('type' => 'integer','null' => false), - 'flag' => array('type' => 'integer','null' => false, 'length' => 1, 'default' => 0) + 'lft' => array('type' => 'integer', 'null' => false), + 'rght' => array('type' => 'integer', 'null' => false), + 'flag' => array('type' => 'integer', 'null' => false, 'length' => 1, 'default' => 0) ); } diff --git a/lib/Cake/Test/Fixture/NumberTreeFixture.php b/lib/Cake/Test/Fixture/NumberTreeFixture.php index 6c3b37c82..251da0533 100755 --- a/lib/Cake/Test/Fixture/NumberTreeFixture.php +++ b/lib/Cake/Test/Fixture/NumberTreeFixture.php @@ -35,10 +35,10 @@ class NumberTreeFixture extends CakeTestFixture { * @var array */ public $fields = array( - 'id' => array('type' => 'integer','key' => 'primary'), - 'name' => array('type' => 'string','null' => false), + 'id' => array('type' => 'integer', 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false), 'parent_id' => 'integer', - 'lft' => array('type' => 'integer','null' => false), - 'rght' => array('type' => 'integer','null' => false) + 'lft' => array('type' => 'integer', 'null' => false), + 'rght' => array('type' => 'integer', 'null' => false) ); } diff --git a/lib/Cake/Test/Fixture/NumberTreeTwoFixture.php b/lib/Cake/Test/Fixture/NumberTreeTwoFixture.php index 8a14ab158..04d28887e 100755 --- a/lib/Cake/Test/Fixture/NumberTreeTwoFixture.php +++ b/lib/Cake/Test/Fixture/NumberTreeTwoFixture.php @@ -35,11 +35,11 @@ class NumberTreeTwoFixture extends CakeTestFixture { * @var array */ public $fields = array( - 'id' => array('type' => 'integer','key' => 'primary'), - 'name' => array('type' => 'string','null' => false), + 'id' => array('type' => 'integer', 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false), 'number_tree_id' => array('type' => 'integer', 'null' => false), 'parent_id' => 'integer', - 'lft' => array('type' => 'integer','null' => false), - 'rght' => array('type' => 'integer','null' => false) + 'lft' => array('type' => 'integer', 'null' => false), + 'rght' => array('type' => 'integer', 'null' => false) ); } diff --git a/lib/Cake/Test/Fixture/UnconventionalTreeFixture.php b/lib/Cake/Test/Fixture/UnconventionalTreeFixture.php index f260ba6e5..dfe7c760f 100755 --- a/lib/Cake/Test/Fixture/UnconventionalTreeFixture.php +++ b/lib/Cake/Test/Fixture/UnconventionalTreeFixture.php @@ -34,10 +34,10 @@ class UnconventionalTreeFixture extends CakeTestFixture { * @var array */ public $fields = array( - 'id' => array('type' => 'integer','key' => 'primary'), - 'name' => array('type' => 'string','null' => false), + 'id' => array('type' => 'integer', 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false), 'join' => 'integer', - 'left' => array('type' => 'integer','null' => false), - 'right' => array('type' => 'integer','null' => false), + 'left' => array('type' => 'integer', 'null' => false), + 'right' => array('type' => 'integer', 'null' => false), ); } diff --git a/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp b/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp index d91db313d..1b1815e44 100755 --- a/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp +++ b/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp @@ -61,7 +61,7 @@

      diff --git a/lib/Cake/TestSuite/CakeTestCase.php b/lib/Cake/TestSuite/CakeTestCase.php index 467b6ba10..55a0d0b4c 100755 --- a/lib/Cake/TestSuite/CakeTestCase.php +++ b/lib/Cake/TestSuite/CakeTestCase.php @@ -689,17 +689,21 @@ protected function skipUnless($condition, $message = '') { * * @param string $model * @param mixed $methods - * @param mixed $config + * @param array $config + * @throws MissingModelException * @return Model */ - public function getMockForModel($model, $methods = array(), $config = null) { - if (is_null($config)) { - $config = ClassRegistry::config('Model'); - } + public function getMockForModel($model, $methods = array(), $config = array()) { + $config += ClassRegistry::config('Model'); list($plugin, $name) = pluginSplit($model, true); App::uses($name, $plugin . 'Model'); $config = array_merge((array)$config, array('name' => $name)); + + if (!class_exists($name)) { + throw new MissingModelException(array($model)); + } + $mock = $this->getMock($name, $methods, array($config)); ClassRegistry::removeObject($name); ClassRegistry::addObject($name, $mock); diff --git a/lib/Cake/TestSuite/CakeTestSuiteCommand.php b/lib/Cake/TestSuite/CakeTestSuiteCommand.php index ab8fca7c2..fd365fdee 100755 --- a/lib/Cake/TestSuite/CakeTestSuiteCommand.php +++ b/lib/Cake/TestSuite/CakeTestSuiteCommand.php @@ -75,24 +75,6 @@ public function run(array $argv, $exit = true) { ); } - if (!count($suite)) { - $skeleton = new PHPUnit_Util_Skeleton_Test( - $suite->getName(), - $this->arguments['testFile'] - ); - - $result = $skeleton->generate(true); - - if (!$result['incomplete']) { - //@codingStandardsIgnoreStart - eval(str_replace(array(''), '', $result['code'])); - //@codingStandardsIgnoreEnd - $suite = new PHPUnit_Framework_TestSuite( - $this->arguments['test'] . 'Test' - ); - } - } - if ($this->arguments['listGroups']) { PHPUnit_TextUI_TestRunner::printVersionString(); diff --git a/lib/Cake/TestSuite/ControllerTestCase.php b/lib/Cake/TestSuite/ControllerTestCase.php index 4d224f5f0..28227a437 100755 --- a/lib/Cake/TestSuite/ControllerTestCase.php +++ b/lib/Cake/TestSuite/ControllerTestCase.php @@ -334,6 +334,7 @@ public function generate($controller, $mocks = array()) { $request = $this->getMock('CakeRequest'); $response = $this->getMock('CakeResponse', array('_sendHeader')); $controllerObj->__construct($request, $response); + $controllerObj->Components->setController($controllerObj); $config = ClassRegistry::config('Model'); foreach ($mocks['models'] as $model => $methods) { diff --git a/lib/Cake/TestSuite/Fixture/CakeTestModel.php b/lib/Cake/TestSuite/Fixture/CakeTestModel.php index 6e456c1cf..bd23336c4 100755 --- a/lib/Cake/TestSuite/Fixture/CakeTestModel.php +++ b/lib/Cake/TestSuite/Fixture/CakeTestModel.php @@ -32,27 +32,15 @@ class CakeTestModel extends Model { * incorrect order when no order has been defined in the finds. * Postgres can return the results in any order it considers appropriate if none is specified * - * @param array $queryData - * @return array $queryData + * @param integer|string|array $id Set this ID for this model on startup, can also be an array of options, see above. + * @param string $table Name of database table to use. + * @param string $ds DataSource connection name. */ - public function beforeFind($queryData) { - $pk = $this->primaryKey; - $aliasedPk = $this->alias . '.' . $this->primaryKey; - switch (true) { - case !$pk: - case !$this->useTable: - case !$this->schema('id'): - case !empty($queryData['order'][0]): - case !empty($queryData['group']): - case - (is_string($queryData['fields']) && !($queryData['fields'] == $pk || $queryData['fields'] == $aliasedPk)) || - (is_array($queryData['fields']) && !(array_key_exists($pk, $queryData['fields']) || array_key_exists($aliasedPk, $queryData['fields']))): - break; - default: - $queryData['order'] = array($this->alias . '.' . $this->primaryKey => 'ASC'); - } - return $queryData; + public function __construct($id = false, $table = null, $ds = null) { + parent::__construct($id, $table, $ds); + $this->order = array($this->alias . '.' . $this->primaryKey => 'ASC'); } + /** * Overriding save() to set CakeTestSuiteDispatcher::date() as formatter for created, modified and updated fields * diff --git a/lib/Cake/Utility/CakeNumber.php b/lib/Cake/Utility/CakeNumber.php index 94c8087ad..266326dae 100755 --- a/lib/Cake/Utility/CakeNumber.php +++ b/lib/Cake/Utility/CakeNumber.php @@ -39,27 +39,33 @@ class CakeNumber { protected static $_currencies = array( 'AUD' => array( 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after', - 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true + 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true, + 'fractionExponent' => 2 ), 'CAD' => array( 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after', - 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true + 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true, + 'fractionExponent' => 2 ), 'USD' => array( 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after', - 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true + 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true, + 'fractionExponent' => 2 ), 'EUR' => array( 'wholeSymbol' => '€', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after', - 'zero' => 0, 'places' => 2, 'thousands' => '.', 'decimals' => ',', 'negative' => '()', 'escape' => true + 'zero' => 0, 'places' => 2, 'thousands' => '.', 'decimals' => ',', 'negative' => '()', 'escape' => true, + 'fractionExponent' => 0 ), 'GBP' => array( 'wholeSymbol' => '£', 'wholePosition' => 'before', 'fractionSymbol' => 'p', 'fractionPosition' => 'after', - 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()','escape' => true + 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()','escape' => true, + 'fractionExponent' => 2 ), 'JPY' => array( - 'wholeSymbol' => '¥', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after', - 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true + 'wholeSymbol' => '¥', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after', + 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true, + 'fractionExponent' => 0 ), ); @@ -70,7 +76,8 @@ class CakeNumber { */ protected static $_currencyDefaults = array( 'wholeSymbol' => '', 'wholePosition' => 'before', 'fractionSymbol' => '', 'fractionPosition' => 'after', - 'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.','negative' => '()', 'escape' => true, + 'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true, + 'fractionExponent' => 2 ); /** @@ -230,6 +237,7 @@ public static function format($value, $options = false) { * ### Options * * - `places` - Number of decimal places to use. ie. 2 + * - `fractionExponent` - Fraction exponent of this specific currency. Defaults to 2. * - `before` - The string to place before whole numbers. ie. '[' * - `after` - The string to place after decimal numbers. ie. ']' * - `thousands` - Thousands separator ie. ',' @@ -297,6 +305,7 @@ protected static function _numberFormat($value, $places = 0, $decimals = '.', $t * - `zero` - The text to use for zero values, can be a * string or a number. ie. 0, 'Free!' * - `places` - Number of decimal places to use. ie. 2 + * - `fractionExponent` - Fraction exponent of this specific currency. Defaults to 2. * - `thousands` - Thousands separator ie. ',' * - `decimals` - Decimal separator symbol ie. '.' * - `negative` - Symbol for negative numbers. If equal to '()', @@ -339,12 +348,12 @@ public static function currency($value, $currency = null, $options = array()) { $symbolKey = 'whole'; $value = (float)$value; if (!$value) { - if ($options['zero'] !== 0 ) { + if ($options['zero'] !== 0) { return $options['zero']; } } elseif ($value < 1 && $value > -1) { if ($options['fractionSymbol'] !== false) { - $multiply = intval('1' . str_pad('', $options['places'], '0')); + $multiply = pow(10, $options['fractionExponent']); $value = $value * $multiply; $options['places'] = null; $symbolKey = 'fraction'; diff --git a/lib/Cake/Utility/Debugger.php b/lib/Cake/Utility/Debugger.php index e1b5c2aec..c004bdbd1 100755 --- a/lib/Cake/Utility/Debugger.php +++ b/lib/Cake/Utility/Debugger.php @@ -500,6 +500,8 @@ protected static function _export($var, $depth, $indent) { return strtolower(gettype($var)); case 'null': return 'null'; + case 'unknown': + return 'unknown'; default: return self::_object($var, $depth - 1, $indent + 1); } @@ -591,24 +593,20 @@ protected static function _object($var, $depth, $indent) { if (version_compare(PHP_VERSION, '5.3.0') >= 0) { $ref = new ReflectionObject($var); - $reflectionProperties = $ref->getProperties(ReflectionProperty::IS_PROTECTED); - foreach ($reflectionProperties as $reflectionProperty) { - $reflectionProperty->setAccessible(true); - $property = $reflectionProperty->getValue($var); - - $value = self::_export($property, $depth - 1, $indent); - $key = $reflectionProperty->name; - $props[] = "[protected] $key => " . $value; - } - - $reflectionProperties = $ref->getProperties(ReflectionProperty::IS_PRIVATE); - foreach ($reflectionProperties as $reflectionProperty) { - $reflectionProperty->setAccessible(true); - $property = $reflectionProperty->getValue($var); - - $value = self::_export($property, $depth - 1, $indent); - $key = $reflectionProperty->name; - $props[] = "[private] $key => " . $value; + $filters = array( + ReflectionProperty::IS_PROTECTED => 'protected', + ReflectionProperty::IS_PRIVATE => 'private', + ); + foreach ($filters as $filter => $visibility) { + $reflectionProperties = $ref->getProperties($filter); + foreach ($reflectionProperties as $reflectionProperty) { + $reflectionProperty->setAccessible(true); + $property = $reflectionProperty->getValue($var); + + $value = self::_export($property, $depth - 1, $indent); + $key = $reflectionProperty->name; + $props[] = sprintf('[%s] %s => %s', $visibility, $key, $value); + } } } diff --git a/lib/Cake/Utility/File.php b/lib/Cake/Utility/File.php index b633155aa..6415b2add 100755 --- a/lib/Cake/Utility/File.php +++ b/lib/Cake/Utility/File.php @@ -557,9 +557,14 @@ public function mime() { } if (function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME); - list($type, $charset) = explode(';', finfo_file($finfo, $this->pwd())); + $finfo = finfo_file($finfo, $this->pwd()); + if (!$finfo) { + return false; + } + list($type, $charset) = explode(';', $finfo); return $type; - } elseif (function_exists('mime_content_type')) { + } + if (function_exists('mime_content_type')) { return mime_content_type($this->pwd()); } return false; diff --git a/lib/Cake/Utility/Sanitize.php b/lib/Cake/Utility/Sanitize.php index 9494ff543..9c8e01e6d 100755 --- a/lib/Cake/Utility/Sanitize.php +++ b/lib/Cake/Utility/Sanitize.php @@ -29,6 +29,7 @@ * and all of the above on arrays. * * @package Cake.Utility + * @deprecated Deprecated since version 2.4 */ class Sanitize { diff --git a/lib/Cake/Utility/String.php b/lib/Cake/Utility/String.php index b3457cecb..e8313aead 100755 --- a/lib/Cake/Utility/String.php +++ b/lib/Cake/Utility/String.php @@ -524,7 +524,7 @@ class_exists('Multibyte'); } } - $truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength); + $truncate .= mb_substr($tag[3], 0, $left + $entitiesLength); break; } else { $truncate .= $tag[3]; diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index f40e80915..96e87f6aa 100755 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -620,7 +620,13 @@ public static function phone($check, $regex = null, $country = 'all') { case 'all': // includes all NANPA members. // see http://en.wikipedia.org/wiki/North_American_Numbering_Plan#List_of_NANPA_countries_and_territories - $regex = '/^(?:\+?1)?[-. ]?\\(?[2-9][0-8][0-9]\\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/'; + $regex = '/^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|3[02-689][0-9]|9[02-57-9][0-9]|[246-8][02-46-8][02-46-9])\s*\)'; + $regex .= '|(55[0-46-9]|5[0-46-9][5]|[0-46-9]55|[2-9]1[02-9]|[2-9][02-8]1|[2-46-9][02-46-8][02-46-9]))\s*(?:[.-]\s*)?)'; + $regex .= '(?!(555(?:\s*(?:[.|\-|\s]\s*))(01([0-9][0-9])|1212)))'; + $regex .= '(?!(555(01([0-9][0-9])|1212)))'; + $regex .= '([2-9]1[02-9]|[2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)'; + $regex .= '?([0-9]{4})'; + $regex .= '(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/'; break; } } diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index 91efb6b45..51d91d52e 100755 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -17,4 +17,4 @@ // @license http://www.opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -2.4.0-beta +2.4.0-RC1 diff --git a/lib/Cake/View/Elements/sql_dump.ctp b/lib/Cake/View/Elements/sql_dump.ctp index ed9917fb0..fe2897cfe 100755 --- a/lib/Cake/View/Elements/sql_dump.ctp +++ b/lib/Cake/View/Elements/sql_dump.ctp @@ -58,19 +58,27 @@ if ($noLogs || isset($_forced_from_dbo_)): } foreach ($i['params'] as $bindKey => $bindVal) { if ($bindType === true) { - $bindParam .= h($bindKey) ." => " . h($bindVal) . ", "; + $bindParam .= h($bindKey) . " => " . h($bindVal) . ", "; } else { $bindParam .= h($bindVal) . ", "; } } $i['query'] .= " , params[ " . rtrim($bindParam, ', ') . " ]"; } - echo "" . ($k + 1) . "" . h($i['query']) . "{$i['error']}{$i['affected']}{$i['numRows']}{$i['took']}\n"; + printf('%d%s%s%d%d%d%s', + $k + 1, + h($i['query']), + $i['error'], + $i['affected'], + $i['numRows'], + $i['took'], + "\n" + ); endforeach; ?> Encountered unexpected $sqlLogs cannot generate SQL log

      '; + printf('

      %s

      ', __d('cake_dev', 'Encountered unexpected %s. Cannot generate SQL log.', '$sqlLogs')); endif; diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index 3eaa906f8..109a29aa7 100755 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -223,7 +223,7 @@ public function __get($name) { * * @param string $name Name of the property being accessed. * @param mixed $value - * @return mixed Return the $value + * @return void */ public function __set($name, $value) { switch ($name) { @@ -231,11 +231,13 @@ public function __set($name, $value) { case 'here': case 'webroot': case 'data': - return $this->request->{$name} = $value; + $this->request->{$name} = $value; + return; case 'action': - return $this->request->params['action'] = $value; + $this->request->params['action'] = $value; + return; } - return $this->{$name} = $value; + $this->{$name} = $value; } /** @@ -322,13 +324,16 @@ public function assetUrl($path, $options = array()) { ) { $path .= $options['ext']; } + if (preg_match('|^([a-z0-9]+:)?//|', $path)) { + return $path; + } if (isset($plugin)) { $path = Inflector::underscore($plugin) . '/' . $path; } $path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path))); if (!empty($options['fullBase'])) { - $path = rtrim(Router::baseURL(), '/') . '/' . ltrim($path, '/'); + $path = rtrim(Router::fullBaseUrl(), '/') . '/' . ltrim($path, '/'); } return $path; } @@ -478,7 +483,7 @@ protected function _parseAttributes($options, $exclude = null, $insertBefore = ' */ protected function _formatAttribute($key, $value, $escape = true) { if (is_array($value)) { - $value = implode(' ' , $value); + $value = implode(' ', $value); } if (is_numeric($key)) { return sprintf($this->_minimizedAttributeFormat, $value, $value); @@ -494,6 +499,19 @@ protected function _formatAttribute($key, $value, $escape = true) { return sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value)); } +/** + * Returns a string to be used as onclick handler for confirm dialogs. + * + * @param string $message Message to be displayed + * @param string $okCode Code to be executed after user chose 'OK' + * @param string $cancelCode Code to be executed after user chose 'Cancel' + * @return string onclick JS code + */ + protected function _confirm($message, $okCode, $cancelCode = '') { + $message = json_encode($message); + return "if (confirm({$message})) { {$okCode} } {$cancelCode}"; + } + /** * Sets this helper's model and field properties to the dot-separated value-pair in $entity. * @@ -834,7 +852,7 @@ public function afterLayout($layoutFile) { * @param string $viewFile The file about to be rendered. * @return void */ - public function beforeRenderFile($viewfile) { + public function beforeRenderFile($viewFile) { } /** @@ -847,7 +865,7 @@ public function beforeRenderFile($viewfile) { * @param string $content The content that was rendered. * @return void */ - public function afterRenderFile($viewfile, $content) { + public function afterRenderFile($viewFile, $content) { } /** diff --git a/lib/Cake/View/Helper/CacheHelper.php b/lib/Cake/View/Helper/CacheHelper.php index 5538b49de..f2cd3dbce 100755 --- a/lib/Cake/View/Helper/CacheHelper.php +++ b/lib/Cake/View/Helper/CacheHelper.php @@ -67,7 +67,8 @@ protected function _enabled() { * Parses the view file and stores content for cache file building. * * @param string $viewFile - * @return void + * @param string $output The output for the file. + * @return string Updated content. */ public function afterRenderFile($viewFile, $output) { if ($this->_enabled()) { diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index d65346147..fe9e74238 100755 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -218,11 +218,9 @@ protected function _introspectModel($model, $key, $field = null) { if ($key === 'validates' && !isset($this->fieldset[$model]['validates'])) { $validates = array(); - if (!empty($object->validate)) { - foreach ($object->validator() as $validateField => $validateProperties) { - if ($this->_isRequiredField($validateProperties)) { - $validates[$validateField] = true; - } + foreach ($object->validator() as $validateField => $validateProperties) { + if ($this->_isRequiredField($validateProperties)) { + $validates[$validateField] = true; } } $this->fieldset[$model]['validates'] = $validates; @@ -1786,12 +1784,11 @@ public function postLink($title, $url = null, $options = array(), $confirmMessag $url = '#'; $onClick = 'document.' . $formName . '.submit();'; if ($confirmMessage) { - $confirmMessage = str_replace(array("'", '"'), array("\'", '\"'), $confirmMessage); - $options['onclick'] = "if (confirm('{$confirmMessage}')) { {$onClick} }"; + $options['onclick'] = $this->_confirm($confirmMessage, $onClick); } else { - $options['onclick'] = $onClick; + $options['onclick'] = $onClick . ' '; } - $options['onclick'] .= ' event.returnValue = false; return false;'; + $options['onclick'] .= 'event.returnValue = false; return false;'; $out .= $this->Html->link($title, $url, $options); return $out; @@ -1880,7 +1877,7 @@ public function submit($caption = null, $options = array()) { } elseif ($isImage) { unset($options['type']); if ($caption{0} !== '/') { - $url = $this->webroot(IMAGES_URL . $caption); + $url = $this->webroot(Configure::read('App.imageBaseUrl') . $caption); } else { $url = $this->webroot(trim($caption, '/')); } @@ -2345,6 +2342,7 @@ public function meridian($fieldName, $attributes = array()) { * - `separator` The contents of the string between select elements. Defaults to '-' * - `empty` - If true, the empty select option is shown. If a string, * that string is displayed as the empty element. + * - `round` - Set to `up` or `down` if you want to force rounding in either direction. Defaults to null. * - `value` | `default` The default value to be used by the input. A value in `$this->data` * matching the field name will override this value. If no default is provided `time()` will be used. * @@ -2379,7 +2377,7 @@ public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $a $defaults = array( 'minYear' => null, 'maxYear' => null, 'separator' => '-', - 'interval' => 1, 'monthNames' => true + 'interval' => 1, 'monthNames' => true, 'round' => null ); $attributes = array_merge($defaults, (array)$attributes); if (isset($attributes['minuteInterval'])) { @@ -2391,6 +2389,7 @@ public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $a $separator = $attributes['separator']; $interval = $attributes['interval']; $monthNames = $attributes['monthNames']; + $round = $attributes['round']; $attributes = array_diff_key($attributes, $defaults); if ($timeFormat == 12 && $hour == 12) { @@ -2405,7 +2404,18 @@ public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $a if ($hour !== null) { $current->setTime($hour, $min); } - $change = (round($min * (1 / $interval)) * $interval) - $min; + $changeValue = $min * (1 / $interval); + switch ($round) { + case 'up': + $changeValue = ceil($changeValue); + break; + case 'down': + $changeValue = floor($changeValue); + break; + default: + $changeValue = round($changeValue); + } + $change = ($changeValue * $interval) - $min; $current->modify($change > 0 ? "+$change minutes" : "$change minutes"); $format = ($timeFormat == 12) ? 'Y m d h i a' : 'Y m d H i a'; $newTime = explode(' ', $current->format($format)); diff --git a/lib/Cake/View/Helper/HtmlHelper.php b/lib/Cake/View/Helper/HtmlHelper.php index 6249f7978..8b309c981 100755 --- a/lib/Cake/View/Helper/HtmlHelper.php +++ b/lib/Cake/View/Helper/HtmlHelper.php @@ -359,15 +359,14 @@ public function link($title, $url = null, $options = array(), $confirmMessage = unset($options['confirm']); } if ($confirmMessage) { - $confirmMessage = str_replace("'", "\'", $confirmMessage); - $confirmMessage = str_replace('"', '\"', $confirmMessage); - $options['onclick'] = "return confirm('{$confirmMessage}');"; + $options['onclick'] = $this->_confirm($confirmMessage, 'return true;', 'return false;'); } elseif (isset($options['default']) && !$options['default']) { if (isset($options['onclick'])) { - $options['onclick'] .= ' event.returnValue = false; return false;'; + $options['onclick'] .= ' '; } else { - $options['onclick'] = 'event.returnValue = false; return false;'; + $options['onclick'] = ''; } + $options['onclick'] .= 'event.returnValue = false; return false;'; unset($options['default']); } return sprintf($this->_tags['link'], $url, $this->_parseAttributes($options), $title); @@ -444,13 +443,13 @@ public function css($path, $options = array()) { if (strpos($path, '//') !== false) { $url = $path; } else { - $url = $this->assetUrl($path, $options + array('pathPrefix' => CSS_URL, 'ext' => '.css')); - $options = array_diff_key($options, array('fullBase' => null)); + $url = $this->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.cssBaseUrl'), 'ext' => '.css')); + $options = array_diff_key($options, array('fullBase' => null, 'pathPrefix' => null)); if (Configure::read('Asset.filter.css')) { - $pos = strpos($url, CSS_URL); + $pos = strpos($url, Configure::read('App.cssBaseUrl')); if ($pos !== false) { - $url = substr($url, 0, $pos) . 'ccss/' . substr($url, $pos + strlen(CSS_URL)); + $url = substr($url, 0, $pos) . 'ccss/' . substr($url, $pos + strlen(Configure::read('App.cssBaseUrl'))); } } } @@ -545,11 +544,11 @@ public function script($url, $options = array()) { $this->_includedScripts[$url] = true; if (strpos($url, '//') === false) { - $url = $this->assetUrl($url, $options + array('pathPrefix' => JS_URL, 'ext' => '.js')); - $options = array_diff_key($options, array('fullBase' => null)); + $url = $this->assetUrl($url, $options + array('pathPrefix' => Configure::read('App.jsBaseUrl'), 'ext' => '.js')); + $options = array_diff_key($options, array('fullBase' => null, 'pathPrefix' => null)); if (Configure::read('Asset.filter.js')) { - $url = str_replace(JS_URL, 'cjs/', $url); + $url = str_replace(Configure::read('App.jsBaseUrl'), 'cjs/', $url); } } $attributes = $this->_parseAttributes($options, array('block', 'once'), ' '); @@ -803,7 +802,7 @@ protected function _prepareCrumbs($startText) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::image */ public function image($path, $options = array()) { - $path = $this->assetUrl($path, $options + array('pathPrefix' => IMAGES_URL)); + $path = $this->assetUrl($path, $options + array('pathPrefix' => Configure::read('App.imageBaseUrl'))); $options = array_diff_key($options, array('fullBase' => null, 'pathPrefix' => null)); if (!isset($options['alt'])) { @@ -918,13 +917,10 @@ public function tag($name, $text = null, $options = array()) { if (empty($name)) { return $text; } - if (is_array($options) && isset($options['escape']) && $options['escape']) { + if (isset($options['escape']) && $options['escape']) { $text = h($text); unset($options['escape']); } - if (!is_array($options)) { - $options = array('class' => $options); - } if ($text === null) { $tag = 'tagstart'; } else { @@ -1106,7 +1102,7 @@ public function media($path, $options = array()) { } if (isset($options['poster'])) { - $options['poster'] = $this->assetUrl($options['poster'], array('pathPrefix' => IMAGES_URL) + $options); + $options['poster'] = $this->assetUrl($options['poster'], array('pathPrefix' => Configure::read('App.imageBaseUrl')) + $options); } $text = $options['text']; diff --git a/lib/Cake/View/Helper/JsHelper.php b/lib/Cake/View/Helper/JsHelper.php index 12c453b0b..62e6a03e5 100755 --- a/lib/Cake/View/Helper/JsHelper.php +++ b/lib/Cake/View/Helper/JsHelper.php @@ -82,7 +82,7 @@ class JsHelper extends AppHelper { * Constructor - determines engine helper * * @param View $View the view object the helper is attached to. - * @param array $settings Settings array contains name of engine helper. + * @param string|array $settings Settings array contains name of engine helper. */ public function __construct(View $View, $settings = array()) { $className = 'Jquery'; diff --git a/lib/Cake/View/Helper/TimeHelper.php b/lib/Cake/View/Helper/TimeHelper.php index a5edd8ef3..211279f6d 100755 --- a/lib/Cake/View/Helper/TimeHelper.php +++ b/lib/Cake/View/Helper/TimeHelper.php @@ -361,7 +361,7 @@ public function toRSS($dateString, $timezone = null) { public function timeAgoInWords($dateTime, $options = array()) { $element = null; - if (is_array($options) && !empty($options['element'])) { + if (!empty($options['element'])) { $element = array( 'tag' => 'span', 'class' => 'time-ago-in-words', diff --git a/lib/Cake/View/HelperCollection.php b/lib/Cake/View/HelperCollection.php index 4fee4f786..b2657d736 100755 --- a/lib/Cake/View/HelperCollection.php +++ b/lib/Cake/View/HelperCollection.php @@ -113,7 +113,7 @@ public function __get($name) { * @throws MissingHelperException when the helper could not be found */ public function load($helper, $settings = array()) { - if (is_array($settings) && isset($settings['className'])) { + if (isset($settings['className'])) { $alias = $helper; $helper = $settings['className']; } diff --git a/lib/Cake/View/JsonView.php b/lib/Cake/View/JsonView.php index 3cb81ca97..7ffeb8673 100755 --- a/lib/Cake/View/JsonView.php +++ b/lib/Cake/View/JsonView.php @@ -128,6 +128,11 @@ protected function _serialize($serialize) { } else { $data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null; } + + if (version_compare(PHP_VERSION, '5.4.0', '>=') && Configure::read('debug')) { + return json_encode($data, JSON_PRETTY_PRINT); + } + return json_encode($data); } diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index d4595ed35..7e98108a7 100755 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -621,7 +621,7 @@ public function blocks() { * @see ViewBlock::start() */ public function start($name) { - return $this->Blocks->start($name); + $this->Blocks->start($name); } /** @@ -632,7 +632,7 @@ public function start($name) { * @see ViewBlock::startIfEmpty() */ public function startIfEmpty($name) { - return $this->Blocks->startIfEmpty($name); + $this->Blocks->startIfEmpty($name); } /** @@ -646,7 +646,7 @@ public function startIfEmpty($name) { * @see ViewBlock::concat() */ public function append($name, $value = null) { - return $this->Blocks->concat($name, $value); + $this->Blocks->concat($name, $value); } /** @@ -660,7 +660,7 @@ public function append($name, $value = null) { * @see ViewBlock::concat() */ public function prepend($name, $value = null) { - return $this->Blocks->concat($name, $value, ViewBlock::PREPEND); + $this->Blocks->concat($name, $value, ViewBlock::PREPEND); } /** @@ -674,7 +674,7 @@ public function prepend($name, $value = null) { * @see ViewBlock::set() */ public function assign($name, $value) { - return $this->Blocks->set($name, $value); + $this->Blocks->set($name, $value); } /** @@ -697,7 +697,7 @@ public function fetch($name, $default = '') { * @see ViewBlock::end() */ public function end() { - return $this->Blocks->end(); + $this->Blocks->end(); } /** diff --git a/lib/Cake/View/XmlView.php b/lib/Cake/View/XmlView.php index 0dbd61775..f218e389b 100755 --- a/lib/Cake/View/XmlView.php +++ b/lib/Cake/View/XmlView.php @@ -116,7 +116,13 @@ protected function _serialize($serialize) { $data = array($rootNode => array($serialize => $data)); } } - return Xml::fromArray($data)->asXML(); + + $options = array(); + if (Configure::read('debug')) { + $options['pretty'] = true; + } + + return Xml::fromArray($data, $options)->asXML(); } } diff --git a/lib/Cake/bootstrap.php b/lib/Cake/bootstrap.php index be2697abf..a2c058248 100755 --- a/lib/Cake/bootstrap.php +++ b/lib/Cake/bootstrap.php @@ -150,10 +150,6 @@ App::uses('Object', 'Core'); App::uses('Multibyte', 'I18n'); -App::$bootstrapping = true; - -Configure::bootstrap(isset($boot) ? $boot : true); - /** * Full URL prefix */ @@ -167,11 +163,19 @@ if (isset($httpHost)) { define('FULL_BASE_URL', 'http' . $s . '://' . $httpHost); - Configure::write('App.fullBaseURL', FULL_BASE_URL); + Configure::write('App.fullBaseUrl', FULL_BASE_URL); } unset($httpHost, $s); } +Configure::write('App.imageBaseUrl', IMAGES_URL); +Configure::write('App.cssBaseUrl', CSS_URL); +Configure::write('App.jsBaseUrl', JS_URL); + +App::$bootstrapping = true; + +Configure::bootstrap(isset($boot) ? $boot : true); + if (function_exists('mb_internal_encoding')) { $encoding = Configure::read('App.encoding'); if (!empty($encoding)) { From b9c46b2c8c83d58d4f001634b3e0286c64ad1ded Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 10:10:56 +0200 Subject: [PATCH 65/72] bumped version string --- app/Config/version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Config/version.php b/app/Config/version.php index dbc4e2c60..8852392f4 100644 --- a/app/Config/version.php +++ b/app/Config/version.php @@ -1,2 +1,2 @@ Date: Mon, 12 Aug 2013 13:19:19 +0200 Subject: [PATCH 66/72] moves Entry::_dispatchEvent to AppModel --- app/Model/AppModel.php | 5 +++++ app/Model/Entry.php | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index cbba5ce15..7af171b9a 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -3,6 +3,7 @@ App::uses('Model', 'Model'); App::uses('Sanitize', 'Utility'); App::uses('SaitoUser', 'Lib'); + App::uses('CakeEvent', 'Event'); // import here so that `cake schema ...` cli works App::import('Lib', 'Stopwatch.Stopwatch'); @@ -115,6 +116,10 @@ protected static function _getIp() { return $ip; } + protected function _dispatchEvent($event, $data = []) { + $this->getEventManager()->dispatch(new CakeEvent($event, $this, $data)); + } + /** * Rough and tough ip anonymizer * diff --git a/app/Model/Entry.php b/app/Model/Entry.php index e990b04ca..d11cec629 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -1,7 +1,6 @@ getEventManager()->dispatch(new CakeEvent($event, $this, $data)); - } - protected function _addAdditionalFields(&$entries) { /** * Function for checking if entry is bookmarked by current user From de1e81506be1f5c29077e033125bcfcb31f81b07 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 13:21:53 +0200 Subject: [PATCH 67/72] empties entry cache on category edit --- app/Lib/CacheSupport.php | 8 ++++-- app/Model/Category.php | 13 +++++++++ app/Test/Case/Model/CategoryTest.php | 41 ++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/app/Lib/CacheSupport.php b/app/Lib/CacheSupport.php index 63f0d77b5..d82db9a14 100644 --- a/app/Lib/CacheSupport.php +++ b/app/Lib/CacheSupport.php @@ -57,14 +57,16 @@ public function __construct() { public function implementedEvents() { return [ - 'Model.Thread.reset' => 'onThreadReset', + 'Model.Thread.reset' => 'onThreadsReset', 'Model.Thread.change' => 'onThreadChanged', 'Model.Entry.replyToEntry' => 'onEntryChanged', - 'Model.Entry.update' => 'onEntryChanged' + 'Model.Entry.update' => 'onEntryChanged', + 'Model.Category.update' => 'onThreadsReset', + 'Model.Category.delete' => 'onThreadsReset' ]; } - public function onThreadReset($event) { + public function onThreadsReset($event) { $this->clear(); } diff --git a/app/Model/Category.php b/app/Model/Category.php index 85fb3f7ce..50ba4f76b 100755 --- a/app/Model/Category.php +++ b/app/Model/Category.php @@ -104,4 +104,17 @@ public function updateThreadCounter() { return $c; } + public function afterDelete() { + $this->_dispatchEvent('Model.Category.delete'); + } + + public function afterSave($created) { + // don't empty cache if it's only a thread count update + if (!isset($this->data[$this->alias]['thread_count']) && + isset($this->data[$this->alias]['category']) + ) { + $this->_dispatchEvent('Model.Category.update'); + } + } + } \ No newline at end of file diff --git a/app/Test/Case/Model/CategoryTest.php b/app/Test/Case/Model/CategoryTest.php index d8bf5d447..eadc4d8f6 100755 --- a/app/Test/Case/Model/CategoryTest.php +++ b/app/Test/Case/Model/CategoryTest.php @@ -54,6 +54,45 @@ public function testGetCategoriesForAccession() { $this->assertEqual($result, $expected); } + public function testUpdateEvent() { + $Category = $this->getMockForModel('Category', ['_dispatchEvent']); + $Category->expects($this->once()) + ->method('_dispatchEvent') + ->with('Model.Category.update'); + + $data = [ + 'Category' => [ + 'category' => 'foo' + ] + ]; + $Category->id = 1; + $Category->save($data); + } + + public function testNoUpdateEvent() { + $Category = $this->getMockForModel('Category', ['_dispatchEvent']); + $Category->expects($this->never()) + ->method('_dispatchEvent'); + + $data = [ + 'Category' => [ + 'thread_count' => '300' + ] + ]; + $Category->id = 1; + $Category->save($data); + } + + public function testDeleteEvent() { + $Category = $this->getMockForModel('Category', ['_dispatchEvent']); + $Category->expects($this->once()) + ->method('_dispatchEvent') + ->with('Model.Category.delete'); + + $Category->id = 1; + $Category->delete(); + } + public function setUp() { parent::setup(); $this->Category = ClassRegistry::init('Category'); @@ -65,5 +104,3 @@ public function tearDown(){ } } - -?> \ No newline at end of file From 556e14aa6051481ad63604f7105c542af41a4984 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 13:28:46 +0200 Subject: [PATCH 68/72] bumped version string --- app/Config/version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Config/version.php b/app/Config/version.php index 8852392f4..65833d7e9 100644 --- a/app/Config/version.php +++ b/app/Config/version.php @@ -1,2 +1,2 @@ Date: Mon, 12 Aug 2013 13:54:56 +0200 Subject: [PATCH 69/72] code comments --- app/Model/AppModel.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 7af171b9a..46b952cc0 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -116,6 +116,16 @@ protected static function _getIp() { return $ip; } + /** + * Dispatches an event + * + * - Always passes the issuing model class as subject + * - Wrapper for CakeEvent boilerplate code + * - Easier to test + * + * @param string $event event identifier `Model..` + * @param array $data additional event data + */ protected function _dispatchEvent($event, $data = []) { $this->getEventManager()->dispatch(new CakeEvent($event, $this, $data)); } From dc504f36e376ace16b9a3bda6caf4e6a3773dabf Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 20:14:57 +0200 Subject: [PATCH 70/72] fixes waschmaschinenknopf --- app/Controller/Component/CurrentUserComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/Component/CurrentUserComponent.php b/app/Controller/Component/CurrentUserComponent.php index 6854b9508..b6e526fb0 100755 --- a/app/Controller/Component/CurrentUserComponent.php +++ b/app/Controller/Component/CurrentUserComponent.php @@ -375,7 +375,7 @@ public function __construct(SaitoUser $currentUser, User $user) { */ public function set($timestamp = null) { if ($timestamp === 'now') { - $this->_set(date("Y-m-d H:i:s")); + $timestamp = date('Y-m-d H:i:s'); } elseif ($timestamp === null) { $timestamp = $this->currentUser['last_refresh_tmp']; } From 1511b966006c22be72964d13e14b4773c060b8c6 Mon Sep 17 00:00:00 2001 From: Schlaefer Date: Mon, 12 Aug 2013 20:16:27 +0200 Subject: [PATCH 71/72] bumped version string --- app/Config/version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Config/version.php b/app/Config/version.php index 65833d7e9..fd63c50c3 100644 --- a/app/Config/version.php +++ b/app/Config/version.php @@ -1,2 +1,2 @@ Date: Wed, 14 Aug 2013 08:47:11 +0200 Subject: [PATCH 72/72] fixes shoutbox is rendered twice on page load --- app/webroot/js/main-prod.js | 2 +- app/webroot/js/views/shouts.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/webroot/js/main-prod.js b/app/webroot/js/main-prod.js index cec341ece..c117205df 100644 --- a/app/webroot/js/main-prod.js +++ b/app/webroot/js/main-prod.js @@ -97,4 +97,4 @@ * @license MIT License (see LICENSE.txt) */ -function FastClick(e){var t,n=this;this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.layer=e;if(!e||!e.nodeType)throw new TypeError("Layer must be a document node");this.onClick=function(){return FastClick.prototype.onClick.apply(n,arguments)},this.onMouse=function(){return FastClick.prototype.onMouse.apply(n,arguments)},this.onTouchStart=function(){return FastClick.prototype.onTouchStart.apply(n,arguments)},this.onTouchEnd=function(){return FastClick.prototype.onTouchEnd.apply(n,arguments)},this.onTouchCancel=function(){return FastClick.prototype.onTouchCancel.apply(n,arguments)};if(FastClick.notNeeded())return;this.deviceIsAndroid&&(e.addEventListener("mouseover",this.onMouse,!0),e.addEventListener("mousedown",this.onMouse,!0),e.addEventListener("mouseup",this.onMouse,!0)),e.addEventListener("click",this.onClick,!0),e.addEventListener("touchstart",this.onTouchStart,!1),e.addEventListener("touchend",this.onTouchEnd,!1),e.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(e.removeEventListener=function(t,n,r){var i=Node.prototype.removeEventListener;t==="click"?i.call(e,t,n.hijacked||n,r):i.call(e,t,n,r)},e.addEventListener=function(t,n,r){var i=Node.prototype.addEventListener;t==="click"?i.call(e,t,n.hijacked||(n.hijacked=function(e){e.propagationStopped||n(e)}),r):i.call(e,t,n,r)}),typeof e.onclick=="function"&&(t=e.onclick,e.addEventListener("click",function(e){t(e)},!1),e.onclick=null)}define("domReady",[],function(){function u(e){var t;for(t=0;t2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?null:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or||n===void 0)return 1;if(n>>1;n.call(r,e[u])=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&(t[t.length]=n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]==null&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var O=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=O(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&O(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return O(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i":">",'"':""","'":"'","/":"/"}};M.unescape=x.invert(M.escape);var _={escape:new RegExp("["+x.keys(M.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(M.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(_[e],function(t){return M[e][t]})}}),x.result=function(e,t){if(e==null)return null;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),j.call(this,n.apply(x,e))}})};var D=0;x.uniqueId=function(e){var t=++D+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var P=/(.)^/,H={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||P).source,(n.interpolate||P).source,(n.evaluate||P).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(B,function(e){return"\\"+H[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var j=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],j.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return j.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this),define("underscore",function(e){return function(){return e._}}(this)),function(){var e=this,t=e.Backbone,n=[],r=n.push,i=n.slice,s=n.splice,o;typeof exports!="undefined"?o=exports:o=e.Backbone={},o.VERSION="1.0.0";var u=e._;!u&&typeof require!="undefined"&&(u=require("underscore")),o.$=e.jQuery||e.Zepto||e.ender||e.$,o.noConflict=function(){return e.Backbone=t,this},o.emulateHTTP=!1,o.emulateJSON=!1;var a=o.Events={on:function(e,t,n){if(!l(this,"on",e,[t,n])||!t)return this;this._events||(this._events={});var r=this._events[e]||(this._events[e]=[]);return r.push({callback:t,context:n,ctx:n||this}),this},once:function(e,t,n){if(!l(this,"once",e,[t,n])||!t)return this;var r=this,i=u.once(function(){r.off(e,i),t.apply(this,arguments)});return i._callback=t,this.on(e,i,n)},off:function(e,t,n){var r,i,s,o,a,f,c,h;if(!this._events||!l(this,"off",e,[t,n]))return this;if(!e&&!t&&!n)return this._events={},this;o=e?[e]:u.keys(this._events);for(a=0,f=o.length;a").attr(e);this.setElement(t,!1)}else this.setElement(u.result(this,"el"),!1)}}),o.sync=function(e,t,n){var r=T[e];u.defaults(n||(n={}),{emulateHTTP:o.emulateHTTP,emulateJSON:o.emulateJSON});var i={type:r,dataType:"json"};n.url||(i.url=u.result(t,"url")||B()),n.data==null&&t&&(e==="create"||e==="update"||e==="patch")&&(i.contentType="application/json",i.data=JSON.stringify(n.attrs||t.toJSON(n))),n.emulateJSON&&(i.contentType="application/x-www-form-urlencoded",i.data=i.data?{model:i.data}:{});if(n.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){i.type="POST",n.emulateJSON&&(i.data._method=r);var s=n.beforeSend;n.beforeSend=function(e){e.setRequestHeader("X-HTTP-Method-Override",r);if(s)return s.apply(this,arguments)}}i.type!=="GET"&&!n.emulateJSON&&(i.processData=!1),i.type==="PATCH"&&window.ActiveXObject&&(!window.external||!window.external.msActiveXFilteringEnabled)&&(i.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")});var a=n.xhr=o.ajax(u.extend(i,n));return t.trigger("request",t,a,n),a};var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};o.ajax=function(){return o.$.ajax.apply(o.$,arguments)};var N=o.Router=function(e){e||(e={}),e.routes&&(this.routes=e.routes),this._bindRoutes(),this.initialize.apply(this,arguments)},C=/\((.*?)\)/g,k=/(\(\?)?:\w+/g,L=/\*\w+/g,A=/[\-{}\[\]+?.,\\\^$|#\s]/g;u.extend(N.prototype,a,{initialize:function(){},route:function(e,t,n){u.isRegExp(e)||(e=this._routeToRegExp(e)),u.isFunction(t)&&(n=t,t=""),n||(n=this[t]);var r=this;return o.history.route(e,function(i){var s=r._extractParameters(e,i);n&&n.apply(r,s),r.trigger.apply(r,["route:"+t].concat(s)),r.trigger("route",t,s),o.history.trigger("route",r,t,s)}),this},navigate:function(e,t){return o.history.navigate(e,t),this},_bindRoutes:function(){if(!this.routes)return;this.routes=u.result(this,"routes");var e,t=u.keys(this.routes);while((e=t.pop())!=null)this.route(e,this.routes[e])},_routeToRegExp:function(e){return e=e.replace(A,"\\$&").replace(C,"(?:$1)?").replace(k,function(e,t){return t?e:"([^/]+)"}).replace(L,"(.*?)"),new RegExp("^"+e+"$")},_extractParameters:function(e,t){var n=e.exec(t).slice(1);return u.map(n,function(e){return e?decodeURIComponent(e):null})}});var O=o.History=function(){this.handlers=[],u.bindAll(this,"checkUrl"),typeof window!="undefined"&&(this.location=window.location,this.history=window.history)},M=/^[#\/]|\s+$/g,_=/^\/+|\/+$/g,D=/msie [\w.]+/,P=/\/$/;O.started=!1,u.extend(O.prototype,a,{interval:50,getHash:function(e){var t=(e||this).location.href.match(/#(.*)$/);return t?t[1]:""},getFragment:function(e,t){if(e==null)if(this._hasPushState||!this._wantsHashChange||t){e=this.location.pathname;var n=this.root.replace(P,"");e.indexOf(n)||(e=e.substr(n.length))}else e=this.getHash();return e.replace(M,"")},start:function(e){if(O.started)throw new Error("Backbone.history has already been started");O.started=!0,this.options=u.extend({},{root:"/"},this.options,e),this.root=this.options.root,this._wantsHashChange=this.options.hashChange!==!1,this._wantsPushState=!!this.options.pushState,this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var t=this.getFragment(),n=document.documentMode,r=D.exec(navigator.userAgent.toLowerCase())&&(!n||n<=7);this.root=("/"+this.root+"/").replace(_,"/"),r&&this._wantsHashChange&&(this.iframe=o.$(''}};return r}),define("text!templates/mediaInsert.html",[],function(){return'
      \n \n \n
      \n
      \n
      \n \n
      \n
      \n
      \n \n
      \n'}),define("views/mediaInsert",["jquery","underscore","backbone","models/app","lib/saito/markItUp.media","text!templates/mediaInsert.html"],function(e,t,n,r,i,s){return n.View.extend({template:t.template(s),events:{"click #markitup_media_btn":"_insert"},initialize:function(){this.model!==undefined&&this.model!==null&&this.listenTo(this.model,"change:isAnsweringFormShown",this.remove)},_insert:function(t){var n,s;t.preventDefault(),s=i,n=s.multimedia(this.$("#markitup_media_txta").val(),{embedlyEnabled:r.settings.get("embedly_enabled")===!0}),n===""?this._invalidInput():(e.markItUp({replaceWith:n}),this._closeDialog())},_hideErrorMessages:function(){this.$("#markitup_media_message").hide()},_invalidInput:function(){this.$("#markitup_media_message").show(),this.$el.dialog().parent().effect("shake",{times:2},250)},_closeDialog:function(){this.$el.dialog("close"),this._hideErrorMessages(),this.$("#markitup_media_txta").val("")},_showDialog:function(){this.$el.dialog({show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},title:e.i18n.__("Multimedia"),resizable:!1,open:function(){setTimeout(function(){e("#markitup_media_txta").focus()},210)},close:t.bind(function(){this._hideErrorMessages()},this)})},render:function(){return this.$el.html(this.template),this._showDialog(),this}})}),define("models/preview",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{rendered:"",data:"",fetchingData:0},initialize:function(){this.webroot=n.settings.get("webroot"),this.listenTo(this,"change:data",this._fetchRendered)},_fetchRendered:function(){this.set("fetchingData",1),$.post(this.webroot+"entries/preview/",this.get("data"),e.bind(function(e){this.set("fetchingData",0),this.set("rendered",e.html),n.eventBus.trigger("notificationUnset","all"),n.eventBus.trigger("notification",e)},this),"json")}});return r}),define("views/preview",["jquery","underscore","backbone","text!templates/spinner.html"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.render(),this.listenTo(this.model,"change:fetchingData",this._spinner),this.listenTo(this.model,"change:rendered",this.render)},_spinner:function(e){e.get("fetchingData")?this.$el.html(r):this.$el.html("")},render:function(){var e;return e=this.model.get("rendered"),e||(e=""),this.$el.html(e),this}});return i}),function(e){var t={_scrollToTop:function(t){e("body").animate({scrollTop:t.offset().top-10,easing:"swing"},300)},_scrollToBottom:function(n){e("body").animate({scrollTop:t._elementBottom(n)-e(window).height()+20,easing:"swing"},300,function(){t._isHeigherThanView(n)&&t._scrollToTop(n)})},_elementBottom:function(e){return e.offset().top+e.height()},_isScrolledIntoView:function(n){if(e(n).length===0)return!0;var r=e(window).scrollTop(),i=r+e(window).height(),s=e(n).offset().top,o=t._elementBottom(n);return o<=i&&s>=r},_isHeigherThanView:function(t){return e(window).height()<=t.height()}},n={top:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToTop(n),this},bottom:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToBottom(n),this},isInView:function(){var n;return n=e(this),t._isScrolledIntoView(n)}};e.fn.scrollIntoView=function(t){if(n[t])return n[t].apply(this,Array.prototype.slice.call(arguments,1));if(typeof t=="object"||!t)return n.init.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.scrollIntoView")}}(jQuery),define("lib/saito/jquery.scrollIntoView",function(){}),define("views/answering",["jquery","underscore","backbone","models/app","views/uploads","views/mediaInsert","models/preview","views/preview","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({rendered:!1,answeringForm:!1,preview:!1,mediaView:!1,model:null,events:{"click .btn-previewClose":"_closePreview","click .btn-preview":"_showPreview","click .btn-markItUp-Upload":"_upload","click .btn-markItUp-Media":"_media","click .btn-submit.js-inlined":"_sendInline"},initialize:function(e){this.parentThreadline=e.parentThreadline||null,this.listenTo(r.eventBus,"isAppVisible",this._focusSubject)},_upload:function(e){var t;e.preventDefault(),t=new i({el:"#markitup_upload",textarea:this.$("textarea#EntryText")[0]})},_media:function(e){e.preventDefault(),this.mediaView===!1&&(this.mediaView=new s({el:"#markitup_media",model:this.model})),this.mediaView.render()},_showPreview:function(e){var t;e.preventDefault(),this.$(".preview").slideDown("fast"),this.preview===!1&&(t=new o,this.preview=new u({el:this.$(".preview .content"),model:t})),this.preview.model.set("data",this.$("form").serialize())},_closePreview:function(e){e.preventDefault(),this.$(".preview").slideUp("fast")},_requestAnsweringForm:function(){e.ajax({url:r.settings.get("webroot")+"entries/add/"+this.model.get("id"),success:t.bind(function(e){this.answeringForm=e,this.render()},this)})},_postProcess:function(){this.$el.scrollIntoView("bottom"),this._focusSubject()},_focusSubject:function(){this.$(".postingform input[type=text]:first").focus()},_sendInline:function(n){n.preventDefault(),e.ajax({url:r.settings.get("webroot")+"entries/add",type:"POST",dataType:"json",data:this.$("#EntryAddForm").serialize(),beforeSend:t.bind(function(){this.$(".btn.btn-submit").attr("disabled","disabled")},this),success:t.bind(function(e){this.model.set({isAnsweringFormShown:!1}),this.parentThreadline!==null&&this.parentThreadline.set("isInlineOpened",!1),r.eventBus.trigger("newEntry",{tid:e.tid,pid:this.model.get("id"),id:e.id})},this)})},render:function(){return this.answeringForm===!1?this._requestAnsweringForm():this.rendered===!1&&(this.rendered=!0,this.$el.html(this.answeringForm),t.defer(function(e){e._postProcess()},this)),this}});return a}),define("views/postings",["jquery","underscore","backbone","models/app","collections/geshis","views/geshi","views/answering","text!templates/spinner.html"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({className:"js-entry-view-core",answeringForm:!1,events:{"click .js-btn-setAnsweringForm":"setAnsweringForm","click .btn-answeringClose":"setAnsweringForm"},initialize:function(e){this.collection=e.collection,this.parentThreadline=e.parentThreadline||null,this.listenTo(this.model,"change:isAnsweringFormShown",this.toggleAnsweringForm),this.listenTo(this.model,"change:html",this.render),this.initGeshi(".c_bbc_code-wrapper")},initGeshi:function(e){var t;t=this.$(e);if(t.length>0){var n=new i;t.each(function(e,t){new s({el:t,collection:n})})}},setAnsweringForm:function(e){e.preventDefault(),this.model.toggle("isAnsweringFormShown")},toggleAnsweringForm:function(){this.model.get("isAnsweringFormShown")?(this._hideAllAnsweringForms(),this._hideSignature(),this._showAnsweringForm(),this._hideBoxActions()):(this._showBoxActions(),this._hideAnsweringForm(),this._showSignature())},_showAnsweringForm:function(){r.eventBus.trigger("breakAutoreload"),this.answeringForm===!1&&this.$(".posting_formular_slider").html(u),this.$(".posting_formular_slider").slideDown("fast"),this.answeringForm===!1&&(this.answeringForm=new o({el:this.$(".posting_formular_slider"),model:this.model,parentThreadline:this.parentThreadline})),this.answeringForm.render()},_hideAnsweringForm:function(){var t;e(this.el).find(".posting_formular_slider").slideUp("fast"),t=e(this.el).find(".posting_formular_slider").parent(),this.answeringForm!==!1&&(this.answeringForm.remove(),this.answeringForm.undelegateEvents(),this.answeringForm=!1),t.append('
      ')},_hideAllAnsweringForms:function(){this.collection.forEach(function(e){e.get("id")!==this.model.get("id")&&e.set("isAnsweringFormShown",!1)},this)},_showSignature:function(){e(this.el).find(".signature").slideDown("fast")},_hideSignature:function(){e(this.el).find(".signature").slideUp("fast")},_showBoxActions:function(){e(this.el).find(".l-box-footer").slideDown("fast")},_hideBoxActions:function(){e(this.el).find(".l-box-footer").slideUp("fast")},render:function(){return this.$el.html(this.model.get("html")),this.initGeshi(".c_bbc_code-wrapper"),this}});return a}),define("models/posting",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isAnsweringFormShown:!1,html:""},fetchHtml:function(){$.ajax({success:e.bind(function(e){this.set("html",e)},this),type:"post",async:!1,dateType:"html",url:n.settings.get("webroot")+"entries/view/"+this.get("id")})}});return r}),define("views/threadlines",["jquery","underscore","backbone","models/app","models/threadline","views/threadline-spinner","text!templates/threadline-spinner.html","views/postings","models/posting","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u,a){var f=n.View.extend({className:"js-thread_line",tagName:"li",spinnerTpl:t.template(o),postings:null,events:{"click .btn_show_thread":"toggleInlineOpen","click .link_show_thread":"toggleInlineOpenFromLink"},initialize:function(e){this.postings=e.postings,this.model=new i({id:e.id}),e.el===undefined?this.model.fetch():this.model.set({html:this.el},{silent:!0}),this.collection.add(this.model,{silent:!0}),this.attributes={"data-id":e.id},this.listenTo(this.model,"change:isInlineOpened",this._toggleInlineOpened),this.listenTo(this.model,"change:html",this.render)},toggleInlineOpenFromLink:function(e){this.model.get("isAlwaysShownInline")&&this.toggleInlineOpen(e)},toggleInlineOpen:function(e){e.preventDefault(),this.model.get("isInlineOpened")?this.model.set({isInlineOpened:!1}):this.model.set({isInlineOpened:!0})},_toggleInlineOpened:function(e,n){if(n){var r=this.model.id;this.model.get("isContentLoaded")?this._showInlineView():(this.tlsV=new s({el:this.$el.find(".thread_line-pre i")}),this.tlsV.show(),this.$el.find(".js-thread_line-content").after(this.spinnerTpl({id:r})),this.$el.find(".js-btn-strip").on("click",t.bind(this.toggleInlineOpen,this)),this._insertContent())}else this._closeInlineView()},_insertContent:function(){var e,t;e=this.model.get("id"),this.postingModel=new a({id:e}),this.postings.add(this.postingModel),t=new u({el:this.$(".t_s"),model:this.postingModel,collection:this.postings,parentThreadline:this.model}),this.postingModel.fetchHtml(),this.model.set("isContentLoaded",!0),this._showInlineView()},_showInlineView:function(){var e=t.bind(function(){var e=this.model.get("shouldScrollOnInlineOpen");this.tlsV.hide(),e?this.$el.scrollIntoView("isInView")===!1&&this.$el.scrollIntoView("bottom"):this.model.set("shouldScrollOnInlineOpen",!0)},this);this.$el.find(".js-thread_line-content").fadeOut(100,t.bind(function(){this.$(".js-thread_inline").show(0,e)},this))},_closeInlineView:function(){this.$(".js-thread_inline").hide(0,t.bind(function(){this.$el.find(".js-thread_line-content").slideDown(),this._scrollLineIntoView()},this))},_scrollLineIntoView:function(){var e=this.$(".js-thread_line-content");e.scrollIntoView("isInView")||e.scrollIntoView("top").effect("highlight",{times:1},3e3)},render:function(){var t,n,r;return n=this.model.get("html"),n.length>0&&(t=this.$el,r=e(this.model.get("html")),this.setElement(r),t.replaceWith(r)),this}});return f}),function(e,t){typeof define=="function"&&define.amd?define("backboneLocalStorage",["underscore","backbone"],function(n,r){return t(n||e._,r||e.Backbone)}):t(_,Backbone)}(this,function(e,t){function n(){return((1+Math.random())*65536|0).toString(16).substring(1)}function r(){return n()+n()+"-"+n()+"-"+n()+"-"+n()+"-"+n()+n()+n()}return t.LocalStorage=window.Store=function(e){this.name=e;var t=this.localStorage().getItem(this.name);this.records=t&&t.split(",")||[]},e.extend(t.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(e){return e.id||(e.id=r(),e.set(e.idAttribute,e.id)),this.localStorage().setItem(this.name+"-"+e.id,JSON.stringify(e)),this.records.push(e.id.toString()),this.save(),this.find(e)},update:function(t){return this.localStorage().setItem(this.name+"-"+t.id,JSON.stringify(t)),e.include(this.records,t.id.toString())||this.records.push(t.id.toString()),this.save(),this.find(t)},find:function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e.id))},findAll:function(){return e(this.records).chain().map(function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e))},this).compact().value()},destroy:function(t){return t.isNew()?!1:(this.localStorage().removeItem(this.name+"-"+t.id),this.records=e.reject(this.records,function(e){return e===t.id.toString()}),this.save(),t)},localStorage:function(){return localStorage},jsonData:function(e){return e&&JSON.parse(e)}}),t.LocalStorage.sync=window.Store.sync=t.localSync=function(e,n,r){var i=n.localStorage||n.collection.localStorage,s,o,u=$.Deferred&&$.Deferred();try{switch(e){case"read":s=n.id!=undefined?i.find(n):i.findAll();break;case"create":s=i.create(n);break;case"update":s=i.update(n);break;case"delete":s=i.destroy(n)}}catch(a){a.code===DOMException.QUOTA_EXCEEDED_ERR&&window.localStorage.length===0?o="Private browsing is unsupported":o=a.message}return s?(n.trigger("sync",n,s,r),r&&r.success&&(t.VERSION==="0.9.10"?r.success(n,s,r):r.success(s)),u&&u.resolve(s)):(o=o?o:"Record Not Found",n.trigger("error",n,o,r),r&&r.error&&(t.VERSION==="0.9.10"?r.error(n,o,r):r.error(o)),u&&u.reject(o)),r&&r.complete&&r.complete(s),u&&u.promise()},t.ajaxSync=t.sync,t.getSyncMethod=function(e){return e.localStorage||e.collection&&e.collection.localStorage?t.localSync:t.ajaxSync},t.sync=function(e,n,r){return t.getSyncMethod(n).apply(this,[e,n,r])},t.LocalStorage}),define("models/thread",["underscore","backbone","collections/threadlines"],function(e,t,n){var r=t.Model.extend({defaults:{isThreadCollapsed:!1},initialize:function(){this.threadlines=new n}});return r}),define("collections/threads",["underscore","backbone","backboneLocalStorage","models/thread"],function(e,t,n,r){var i=t.Collection.extend({model:r,localStorage:new n("Threads")});return i}),define("views/thread",["jquery","underscore","backbone","models/app","collections/threadlines","views/threadlines"],function(e,t,n,r,i,s){var o=n.View.extend({className:"thread_box",events:{"click .btn-threadCollapse":"collapseThread","click .js-btn-openAllThreadlines":"openAllThreadlines","click .js-btn-closeAllThreadlines":"closeAllThreadlines","click .js-btn-showAllNewThreadlines":"showAllNewThreadlines"},initialize:function(t){this.postings=t.postings,this.$rootUl=this.$("ul.root"),this.$subThreadRootIl=e(this.$rootUl.find("li:not(:first-child)")[0]),this.model.get("isThreadCollapsed")?this.hide():this.show(),this.listenTo(r.eventBus,"newEntry",this._showNewThreadLine),this.listenTo(this.model,"change:isThreadCollapsed",this.toggleCollapseThread)},_showNewThreadLine:function(e){var t;if(e.tid!==this.model.get("id"))return;t=new s({id:e.id,collection:this.model.threadlines,postings:this.postings}),this._appendThreadlineToThread(e.pid,t.render().$el)},_appendThreadlineToThread:function(e,t){var n,r;n=this.$('.js-thread_line[data-id="'+e+'"]'),r=n.next().not(".js_threadline").find("ul:first"),r.length===0?t.wrap("
        ").parent().wrap("
      • ").parent().insertAfter(n):r.append(t)},openAllThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},closeAllThreadlines:function(e){e&&e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!0}),function(e){e.set({isInlineOpened:!1})},this)},showAllNewThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1,isNewToUser:!0}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},collapseThread:function(e){e.preventDefault(),this.closeAllThreadlines(),this.model.toggle("isThreadCollapsed"),this.model.save()},toggleCollapseThread:function(e,t){t?this.slideUp():this.slideDown()},slideUp:function(){this.$subThreadRootIl.slideUp(300),this.markHidden()},slideDown:function(){this.$subThreadRootIl.slideDown(300),this.markShown()},hide:function(){this.$subThreadRootIl.hide(),this.markHidden()},show:function(){this.$subThreadRootIl.show(),this.markShown()},markShown:function(){e(this.el).find(".icon-thread-closed").removeClass("icon-thread-closed").addClass("icon-thread-open")},markHidden:function(){e(this.el).find(".icon-thread-open").removeClass("icon-thread-open").addClass("icon-thread-closed")}});return o}),define("collections/postings",["underscore","backbone","models/posting"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/bookmark",["underscore","backbone","models/app","cakeRest"],function(e,t,n,r){var i=t.Model.extend({initialize:function(){this.webroot=n.settings.get("webroot")+"bookmarks/"}});return e.extend(i.prototype,r),i}),define("collections/bookmarks",["underscore","backbone","models/bookmark"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("views/bookmark",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({events:{"click .btn-bookmark-delete":"deleteBookmark"},initialize:function(){t.bindAll(this,"render"),this.model.on("destroy",this.removeBookmark,this)},deleteBookmark:function(e){e.preventDefault(),this.model.destroy()},removeBookmark:function(){this.$el.hide("slide",null,500,function(){e(this).remove()})}});return r}),define("views/bookmarks",["jquery","underscore","backbone","views/bookmark"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.initCollectionFromDom(".js-bookmark",this.collection,r)}});return i}),define("views/helps",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({isHelpShown:!1,events:function(){var e={};return e["click "+this.indicatorName]="toggle",e},initialize:function(e){this.indicatorName=e.indicatorName,this.elementName=e.elementName,this.activateHelpButton(),this.placeHelp()},activateHelpButton:function(){this.isHelpOnPage()&&e(this.indicatorName).removeClass("no-color")},placeHelp:function(){var t={trigger:"manual",html:!0},n=["bottom","right","left"];for(var r in n)e(this.elementName+"-"+n[r]).popover(e.extend(t,{placement:n[r]}));e(this.indicatorName).popover({placement:"left",trigger:"manual"})},isHelpOnPage:function(){return this.$el.find(this.elementName).length>0},toggle:function(){event.preventDefault(),this.isHelpShown?this.hide():this.show()},show:function(){this.isHelpShown=!0,this.isHelpOnPage()?e(this.elementName).popover("show"):e(this.indicatorName).popover("show")},hide:function(){this.isHelpShown=!1,e(this.elementName).popover("hide"),e(this.indicatorName).popover("hide")}});return r}),define("views/categoryChooser",["jquery","underscore","backbone"],function(e,t,n){return n.View.extend({initialize:function(){this.$el.dialog({autoOpen:!1,show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},width:400,position:[e("#btn-category-chooser").offset().left+e("#btn-category-chooser").width()-e(window).scrollLeft()-410,e("#btn-category-chooser").offset().top-e(window).scrollTop()+e("#btn-category-chooser").height()],title:e.i18n.__("Categories"),resizable:!1})},toggle:function(){this.$el.dialog("isOpen")?this.$el.dialog("close"):this.$el.dialog("open")}})}),define("models/slidetab",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isOpen:!1},initialize:function(){this.webroot=n.settings.get("webroot")},sync:function(){$.ajax({url:this.webroot+"users/ajax_toggle/show_"+this.get("id")})}});return r}),define("collections/slidetabs",["underscore","backbone","models/slidetab"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/shout",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{html:"",newShout:""},initialize:function(){this.webroot=n.settings.get("webroot")+"shouts/",this.listenTo(this,"change:newShout",this.send)},fetch:function(){$.ajax({url:this.webroot+"index",dataType:"html",success:e.bind(function(e){e.length>0&&this.set({html:e})},this)})},send:function(){$.ajax({url:this.webroot+"add",type:"post",data:{text:this.get("newShout")},success:e.bind(function(){this.fetch()},this)})}});return r}),function(e){var t={className:"autosizejs",append:"",callback:!1},n="hidden",r="border-box",i="lineHeight",s='\n
        \n
        \n
        \n \n
        \n
        \n
        \n \n\n'}),define("views/mediaInsert",["jquery","underscore","backbone","models/app","lib/saito/markItUp.media","text!templates/mediaInsert.html"],function(e,t,n,r,i,s){return n.View.extend({template:t.template(s),events:{"click #markitup_media_btn":"_insert"},initialize:function(){this.model!==undefined&&this.model!==null&&this.listenTo(this.model,"change:isAnsweringFormShown",this.remove)},_insert:function(t){var n,s;t.preventDefault(),s=i,n=s.multimedia(this.$("#markitup_media_txta").val(),{embedlyEnabled:r.settings.get("embedly_enabled")===!0}),n===""?this._invalidInput():(e.markItUp({replaceWith:n}),this._closeDialog())},_hideErrorMessages:function(){this.$("#markitup_media_message").hide()},_invalidInput:function(){this.$("#markitup_media_message").show(),this.$el.dialog().parent().effect("shake",{times:2},250)},_closeDialog:function(){this.$el.dialog("close"),this._hideErrorMessages(),this.$("#markitup_media_txta").val("")},_showDialog:function(){this.$el.dialog({show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},title:e.i18n.__("Multimedia"),resizable:!1,open:function(){setTimeout(function(){e("#markitup_media_txta").focus()},210)},close:t.bind(function(){this._hideErrorMessages()},this)})},render:function(){return this.$el.html(this.template),this._showDialog(),this}})}),define("models/preview",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{rendered:"",data:"",fetchingData:0},initialize:function(){this.webroot=n.settings.get("webroot"),this.listenTo(this,"change:data",this._fetchRendered)},_fetchRendered:function(){this.set("fetchingData",1),$.post(this.webroot+"entries/preview/",this.get("data"),e.bind(function(e){this.set("fetchingData",0),this.set("rendered",e.html),n.eventBus.trigger("notificationUnset","all"),n.eventBus.trigger("notification",e)},this),"json")}});return r}),define("views/preview",["jquery","underscore","backbone","text!templates/spinner.html"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.render(),this.listenTo(this.model,"change:fetchingData",this._spinner),this.listenTo(this.model,"change:rendered",this.render)},_spinner:function(e){e.get("fetchingData")?this.$el.html(r):this.$el.html("")},render:function(){var e;return e=this.model.get("rendered"),e||(e=""),this.$el.html(e),this}});return i}),function(e){var t={_scrollToTop:function(t){e("body").animate({scrollTop:t.offset().top-10,easing:"swing"},300)},_scrollToBottom:function(n){e("body").animate({scrollTop:t._elementBottom(n)-e(window).height()+20,easing:"swing"},300,function(){t._isHeigherThanView(n)&&t._scrollToTop(n)})},_elementBottom:function(e){return e.offset().top+e.height()},_isScrolledIntoView:function(n){if(e(n).length===0)return!0;var r=e(window).scrollTop(),i=r+e(window).height(),s=e(n).offset().top,o=t._elementBottom(n);return o<=i&&s>=r},_isHeigherThanView:function(t){return e(window).height()<=t.height()}},n={top:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToTop(n),this},bottom:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToBottom(n),this},isInView:function(){var n;return n=e(this),t._isScrolledIntoView(n)}};e.fn.scrollIntoView=function(t){if(n[t])return n[t].apply(this,Array.prototype.slice.call(arguments,1));if(typeof t=="object"||!t)return n.init.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.scrollIntoView")}}(jQuery),define("lib/saito/jquery.scrollIntoView",function(){}),define("views/answering",["jquery","underscore","backbone","models/app","views/uploads","views/mediaInsert","models/preview","views/preview","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({rendered:!1,answeringForm:!1,preview:!1,mediaView:!1,model:null,events:{"click .btn-previewClose":"_closePreview","click .btn-preview":"_showPreview","click .btn-markItUp-Upload":"_upload","click .btn-markItUp-Media":"_media","click .btn-submit.js-inlined":"_sendInline"},initialize:function(e){this.parentThreadline=e.parentThreadline||null,this.listenTo(r.eventBus,"isAppVisible",this._focusSubject)},_upload:function(e){var t;e.preventDefault(),t=new i({el:"#markitup_upload",textarea:this.$("textarea#EntryText")[0]})},_media:function(e){e.preventDefault(),this.mediaView===!1&&(this.mediaView=new s({el:"#markitup_media",model:this.model})),this.mediaView.render()},_showPreview:function(e){var t;e.preventDefault(),this.$(".preview").slideDown("fast"),this.preview===!1&&(t=new o,this.preview=new u({el:this.$(".preview .content"),model:t})),this.preview.model.set("data",this.$("form").serialize())},_closePreview:function(e){e.preventDefault(),this.$(".preview").slideUp("fast")},_requestAnsweringForm:function(){e.ajax({url:r.settings.get("webroot")+"entries/add/"+this.model.get("id"),success:t.bind(function(e){this.answeringForm=e,this.render()},this)})},_postProcess:function(){this.$el.scrollIntoView("bottom"),this._focusSubject()},_focusSubject:function(){this.$(".postingform input[type=text]:first").focus()},_sendInline:function(n){n.preventDefault(),e.ajax({url:r.settings.get("webroot")+"entries/add",type:"POST",dataType:"json",data:this.$("#EntryAddForm").serialize(),beforeSend:t.bind(function(){this.$(".btn.btn-submit").attr("disabled","disabled")},this),success:t.bind(function(e){this.model.set({isAnsweringFormShown:!1}),this.parentThreadline!==null&&this.parentThreadline.set("isInlineOpened",!1),r.eventBus.trigger("newEntry",{tid:e.tid,pid:this.model.get("id"),id:e.id})},this)})},render:function(){return this.answeringForm===!1?this._requestAnsweringForm():this.rendered===!1&&(this.rendered=!0,this.$el.html(this.answeringForm),t.defer(function(e){e._postProcess()},this)),this}});return a}),define("views/postings",["jquery","underscore","backbone","models/app","collections/geshis","views/geshi","views/answering","text!templates/spinner.html"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({className:"js-entry-view-core",answeringForm:!1,events:{"click .js-btn-setAnsweringForm":"setAnsweringForm","click .btn-answeringClose":"setAnsweringForm"},initialize:function(e){this.collection=e.collection,this.parentThreadline=e.parentThreadline||null,this.listenTo(this.model,"change:isAnsweringFormShown",this.toggleAnsweringForm),this.listenTo(this.model,"change:html",this.render),this.initGeshi(".c_bbc_code-wrapper")},initGeshi:function(e){var t;t=this.$(e);if(t.length>0){var n=new i;t.each(function(e,t){new s({el:t,collection:n})})}},setAnsweringForm:function(e){e.preventDefault(),this.model.toggle("isAnsweringFormShown")},toggleAnsweringForm:function(){this.model.get("isAnsweringFormShown")?(this._hideAllAnsweringForms(),this._hideSignature(),this._showAnsweringForm(),this._hideBoxActions()):(this._showBoxActions(),this._hideAnsweringForm(),this._showSignature())},_showAnsweringForm:function(){r.eventBus.trigger("breakAutoreload"),this.answeringForm===!1&&this.$(".posting_formular_slider").html(u),this.$(".posting_formular_slider").slideDown("fast"),this.answeringForm===!1&&(this.answeringForm=new o({el:this.$(".posting_formular_slider"),model:this.model,parentThreadline:this.parentThreadline})),this.answeringForm.render()},_hideAnsweringForm:function(){var t;e(this.el).find(".posting_formular_slider").slideUp("fast"),t=e(this.el).find(".posting_formular_slider").parent(),this.answeringForm!==!1&&(this.answeringForm.remove(),this.answeringForm.undelegateEvents(),this.answeringForm=!1),t.append('
        ')},_hideAllAnsweringForms:function(){this.collection.forEach(function(e){e.get("id")!==this.model.get("id")&&e.set("isAnsweringFormShown",!1)},this)},_showSignature:function(){e(this.el).find(".signature").slideDown("fast")},_hideSignature:function(){e(this.el).find(".signature").slideUp("fast")},_showBoxActions:function(){e(this.el).find(".l-box-footer").slideDown("fast")},_hideBoxActions:function(){e(this.el).find(".l-box-footer").slideUp("fast")},render:function(){return this.$el.html(this.model.get("html")),this.initGeshi(".c_bbc_code-wrapper"),this}});return a}),define("models/posting",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isAnsweringFormShown:!1,html:""},fetchHtml:function(){$.ajax({success:e.bind(function(e){this.set("html",e)},this),type:"post",async:!1,dateType:"html",url:n.settings.get("webroot")+"entries/view/"+this.get("id")})}});return r}),define("views/threadlines",["jquery","underscore","backbone","models/app","models/threadline","views/threadline-spinner","text!templates/threadline-spinner.html","views/postings","models/posting","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u,a){var f=n.View.extend({className:"js-thread_line",tagName:"li",spinnerTpl:t.template(o),postings:null,events:{"click .btn_show_thread":"toggleInlineOpen","click .link_show_thread":"toggleInlineOpenFromLink"},initialize:function(e){this.postings=e.postings,this.model=new i({id:e.id}),e.el===undefined?this.model.fetch():this.model.set({html:this.el},{silent:!0}),this.collection.add(this.model,{silent:!0}),this.attributes={"data-id":e.id},this.listenTo(this.model,"change:isInlineOpened",this._toggleInlineOpened),this.listenTo(this.model,"change:html",this.render)},toggleInlineOpenFromLink:function(e){this.model.get("isAlwaysShownInline")&&this.toggleInlineOpen(e)},toggleInlineOpen:function(e){e.preventDefault(),this.model.get("isInlineOpened")?this.model.set({isInlineOpened:!1}):this.model.set({isInlineOpened:!0})},_toggleInlineOpened:function(e,n){if(n){var r=this.model.id;this.model.get("isContentLoaded")?this._showInlineView():(this.tlsV=new s({el:this.$el.find(".thread_line-pre i")}),this.tlsV.show(),this.$el.find(".js-thread_line-content").after(this.spinnerTpl({id:r})),this.$el.find(".js-btn-strip").on("click",t.bind(this.toggleInlineOpen,this)),this._insertContent())}else this._closeInlineView()},_insertContent:function(){var e,t;e=this.model.get("id"),this.postingModel=new a({id:e}),this.postings.add(this.postingModel),t=new u({el:this.$(".t_s"),model:this.postingModel,collection:this.postings,parentThreadline:this.model}),this.postingModel.fetchHtml(),this.model.set("isContentLoaded",!0),this._showInlineView()},_showInlineView:function(){var e=t.bind(function(){var e=this.model.get("shouldScrollOnInlineOpen");this.tlsV.hide(),e?this.$el.scrollIntoView("isInView")===!1&&this.$el.scrollIntoView("bottom"):this.model.set("shouldScrollOnInlineOpen",!0)},this);this.$el.find(".js-thread_line-content").fadeOut(100,t.bind(function(){this.$(".js-thread_inline").show(0,e)},this))},_closeInlineView:function(){this.$(".js-thread_inline").hide(0,t.bind(function(){this.$el.find(".js-thread_line-content").slideDown(),this._scrollLineIntoView()},this))},_scrollLineIntoView:function(){var e=this.$(".js-thread_line-content");e.scrollIntoView("isInView")||e.scrollIntoView("top").effect("highlight",{times:1},3e3)},render:function(){var t,n,r;return n=this.model.get("html"),n.length>0&&(t=this.$el,r=e(this.model.get("html")),this.setElement(r),t.replaceWith(r)),this}});return f}),function(e,t){typeof define=="function"&&define.amd?define("backboneLocalStorage",["underscore","backbone"],function(n,r){return t(n||e._,r||e.Backbone)}):t(_,Backbone)}(this,function(e,t){function n(){return((1+Math.random())*65536|0).toString(16).substring(1)}function r(){return n()+n()+"-"+n()+"-"+n()+"-"+n()+"-"+n()+n()+n()}return t.LocalStorage=window.Store=function(e){this.name=e;var t=this.localStorage().getItem(this.name);this.records=t&&t.split(",")||[]},e.extend(t.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(e){return e.id||(e.id=r(),e.set(e.idAttribute,e.id)),this.localStorage().setItem(this.name+"-"+e.id,JSON.stringify(e)),this.records.push(e.id.toString()),this.save(),this.find(e)},update:function(t){return this.localStorage().setItem(this.name+"-"+t.id,JSON.stringify(t)),e.include(this.records,t.id.toString())||this.records.push(t.id.toString()),this.save(),this.find(t)},find:function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e.id))},findAll:function(){return e(this.records).chain().map(function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e))},this).compact().value()},destroy:function(t){return t.isNew()?!1:(this.localStorage().removeItem(this.name+"-"+t.id),this.records=e.reject(this.records,function(e){return e===t.id.toString()}),this.save(),t)},localStorage:function(){return localStorage},jsonData:function(e){return e&&JSON.parse(e)}}),t.LocalStorage.sync=window.Store.sync=t.localSync=function(e,n,r){var i=n.localStorage||n.collection.localStorage,s,o,u=$.Deferred&&$.Deferred();try{switch(e){case"read":s=n.id!=undefined?i.find(n):i.findAll();break;case"create":s=i.create(n);break;case"update":s=i.update(n);break;case"delete":s=i.destroy(n)}}catch(a){a.code===DOMException.QUOTA_EXCEEDED_ERR&&window.localStorage.length===0?o="Private browsing is unsupported":o=a.message}return s?(n.trigger("sync",n,s,r),r&&r.success&&(t.VERSION==="0.9.10"?r.success(n,s,r):r.success(s)),u&&u.resolve(s)):(o=o?o:"Record Not Found",n.trigger("error",n,o,r),r&&r.error&&(t.VERSION==="0.9.10"?r.error(n,o,r):r.error(o)),u&&u.reject(o)),r&&r.complete&&r.complete(s),u&&u.promise()},t.ajaxSync=t.sync,t.getSyncMethod=function(e){return e.localStorage||e.collection&&e.collection.localStorage?t.localSync:t.ajaxSync},t.sync=function(e,n,r){return t.getSyncMethod(n).apply(this,[e,n,r])},t.LocalStorage}),define("models/thread",["underscore","backbone","collections/threadlines"],function(e,t,n){var r=t.Model.extend({defaults:{isThreadCollapsed:!1},initialize:function(){this.threadlines=new n}});return r}),define("collections/threads",["underscore","backbone","backboneLocalStorage","models/thread"],function(e,t,n,r){var i=t.Collection.extend({model:r,localStorage:new n("Threads")});return i}),define("views/thread",["jquery","underscore","backbone","models/app","collections/threadlines","views/threadlines"],function(e,t,n,r,i,s){var o=n.View.extend({className:"thread_box",events:{"click .btn-threadCollapse":"collapseThread","click .js-btn-openAllThreadlines":"openAllThreadlines","click .js-btn-closeAllThreadlines":"closeAllThreadlines","click .js-btn-showAllNewThreadlines":"showAllNewThreadlines"},initialize:function(t){this.postings=t.postings,this.$rootUl=this.$("ul.root"),this.$subThreadRootIl=e(this.$rootUl.find("li:not(:first-child)")[0]),this.model.get("isThreadCollapsed")?this.hide():this.show(),this.listenTo(r.eventBus,"newEntry",this._showNewThreadLine),this.listenTo(this.model,"change:isThreadCollapsed",this.toggleCollapseThread)},_showNewThreadLine:function(e){var t;if(e.tid!==this.model.get("id"))return;t=new s({id:e.id,collection:this.model.threadlines,postings:this.postings}),this._appendThreadlineToThread(e.pid,t.render().$el)},_appendThreadlineToThread:function(e,t){var n,r;n=this.$('.js-thread_line[data-id="'+e+'"]'),r=n.next().not(".js_threadline").find("ul:first"),r.length===0?t.wrap("
          ").parent().wrap("
        • ").parent().insertAfter(n):r.append(t)},openAllThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},closeAllThreadlines:function(e){e&&e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!0}),function(e){e.set({isInlineOpened:!1})},this)},showAllNewThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1,isNewToUser:!0}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},collapseThread:function(e){e.preventDefault(),this.closeAllThreadlines(),this.model.toggle("isThreadCollapsed"),this.model.save()},toggleCollapseThread:function(e,t){t?this.slideUp():this.slideDown()},slideUp:function(){this.$subThreadRootIl.slideUp(300),this.markHidden()},slideDown:function(){this.$subThreadRootIl.slideDown(300),this.markShown()},hide:function(){this.$subThreadRootIl.hide(),this.markHidden()},show:function(){this.$subThreadRootIl.show(),this.markShown()},markShown:function(){e(this.el).find(".icon-thread-closed").removeClass("icon-thread-closed").addClass("icon-thread-open")},markHidden:function(){e(this.el).find(".icon-thread-open").removeClass("icon-thread-open").addClass("icon-thread-closed")}});return o}),define("collections/postings",["underscore","backbone","models/posting"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/bookmark",["underscore","backbone","models/app","cakeRest"],function(e,t,n,r){var i=t.Model.extend({initialize:function(){this.webroot=n.settings.get("webroot")+"bookmarks/"}});return e.extend(i.prototype,r),i}),define("collections/bookmarks",["underscore","backbone","models/bookmark"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("views/bookmark",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({events:{"click .btn-bookmark-delete":"deleteBookmark"},initialize:function(){t.bindAll(this,"render"),this.model.on("destroy",this.removeBookmark,this)},deleteBookmark:function(e){e.preventDefault(),this.model.destroy()},removeBookmark:function(){this.$el.hide("slide",null,500,function(){e(this).remove()})}});return r}),define("views/bookmarks",["jquery","underscore","backbone","views/bookmark"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.initCollectionFromDom(".js-bookmark",this.collection,r)}});return i}),define("views/helps",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({isHelpShown:!1,events:function(){var e={};return e["click "+this.indicatorName]="toggle",e},initialize:function(e){this.indicatorName=e.indicatorName,this.elementName=e.elementName,this.activateHelpButton(),this.placeHelp()},activateHelpButton:function(){this.isHelpOnPage()&&e(this.indicatorName).removeClass("no-color")},placeHelp:function(){var t={trigger:"manual",html:!0},n=["bottom","right","left"];for(var r in n)e(this.elementName+"-"+n[r]).popover(e.extend(t,{placement:n[r]}));e(this.indicatorName).popover({placement:"left",trigger:"manual"})},isHelpOnPage:function(){return this.$el.find(this.elementName).length>0},toggle:function(){event.preventDefault(),this.isHelpShown?this.hide():this.show()},show:function(){this.isHelpShown=!0,this.isHelpOnPage()?e(this.elementName).popover("show"):e(this.indicatorName).popover("show")},hide:function(){this.isHelpShown=!1,e(this.elementName).popover("hide"),e(this.indicatorName).popover("hide")}});return r}),define("views/categoryChooser",["jquery","underscore","backbone"],function(e,t,n){return n.View.extend({initialize:function(){this.$el.dialog({autoOpen:!1,show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},width:400,position:[e("#btn-category-chooser").offset().left+e("#btn-category-chooser").width()-e(window).scrollLeft()-410,e("#btn-category-chooser").offset().top-e(window).scrollTop()+e("#btn-category-chooser").height()],title:e.i18n.__("Categories"),resizable:!1})},toggle:function(){this.$el.dialog("isOpen")?this.$el.dialog("close"):this.$el.dialog("open")}})}),define("models/slidetab",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isOpen:!1},initialize:function(){this.webroot=n.settings.get("webroot")},sync:function(){$.ajax({url:this.webroot+"users/ajax_toggle/show_"+this.get("id")})}});return r}),define("collections/slidetabs",["underscore","backbone","models/slidetab"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/shout",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{html:"",newShout:""},initialize:function(){this.webroot=n.settings.get("webroot")+"shouts/",this.listenTo(this,"change:newShout",this.send)},fetch:function(){$.ajax({url:this.webroot+"index",dataType:"html",success:e.bind(function(e){e.length>0&&this.set({html:e})},this)})},send:function(){$.ajax({url:this.webroot+"add",type:"post",data:{text:this.get("newShout")},success:e.bind(function(){this.fetch()},this)})}});return r}),function(e){var t={className:"autosizejs",append:"",callback:!1},n="hidden",r="border-box",i="lineHeight",s='