diff --git a/.gitignore b/.gitignore index 3f0f36f22..b3abe2e04 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,11 @@ .DS_Store tags +### CakePHP 3 ### +/logs +/tmp +/vendor + # Saito /app/Config/email.php /app/Config/database.php diff --git a/app/Config/Schema/schema.php b/app/Config/Schema/schema.php index 1375d4814..363d549fb 100644 --- a/app/Config/Schema/schema.php +++ b/app/Config/Schema/schema.php @@ -1,21 +1,7 @@ cakeMysqlMediumBlobFix(); - } - } - public $bookmarks = array( 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'unsigned' => false, 'key' => 'primary'), 'user_id' => array('type' => 'integer', 'null' => false, 'default' => null, 'unsigned' => false, 'key' => 'index'), @@ -44,17 +30,6 @@ public function after($event = array()) { 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_unicode_ci', 'engine' => 'MyISAM') ); - public $ecaches = array( - 'created' => array('type' => 'datetime', 'null' => false, 'default' => null), - 'modified' => array('type' => 'datetime', 'null' => false, 'default' => null), - 'key' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 128, 'key' => 'primary', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'), - 'value' => array('type' => 'binary', 'null' => false, 'default' => null), - 'indexes' => array( - 'PRIMARY' => array('column' => 'key', 'unique' => 1) - ), - 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM') - ); - public $entries = array( 'created' => array('type' => 'datetime', 'null' => true, 'default' => null), 'modified' => array('type' => 'datetime', 'null' => true, 'default' => null), diff --git a/app/Config/bootstrap.php b/app/Config/bootstrap.php index 4ceb4e8b7..e195abc8b 100755 --- a/app/Config/bootstrap.php +++ b/app/Config/bootstrap.php @@ -190,7 +190,6 @@ 'file' => 'saito-auth' )); -include APP . 'Lib' . DS . 'SaitoExceptions.php'; include 'version.php'; /** diff --git a/app/Config/routes.php b/app/Config/routes.php index c6292aec9..53a6d1f29 100755 --- a/app/Config/routes.php +++ b/app/Config/routes.php @@ -56,6 +56,10 @@ */ Router::connect('/admin', ['controller' => 'admins', 'action' => 'index', 'admin' => true]); + Router::connect( + '/admin/plugins', + ['controller' => 'admins', 'action' => 'plugins', 'admin' => true] + ); /** * Default search action diff --git a/app/Config/version.php b/app/Config/version.php index 714e31477..44b4d7a2f 100644 --- a/app/Config/version.php +++ b/app/Config/version.php @@ -1,4 +1,4 @@ Categories = new CategoryAuth($this); + $this->Categories = new Auth\CategoryAuthorization($this); } public function getMaxAccession() { diff --git a/app/Controller/AdminsController.php b/app/Controller/AdminsController.php index c76143972..ff011c1ff 100755 --- a/app/Controller/AdminsController.php +++ b/app/Controller/AdminsController.php @@ -15,20 +15,26 @@ public function admin_logs() { // order here is output order in frontend $_logsToRead = ['error', 'debug']; + $_logsToRead = glob(LOGS . '*.log'); + if (!$_logsToRead) { + return; + } + // will contain ['error' => '', 'debug' => ''] $_logs = []; - foreach ($_logsToRead as $_log) { - $_path = LOGS . $_log . '.log'; + foreach ($_logsToRead as $_path) { $_content = ''; - if (file_exists($_path)) { - $_size = filesize($_path); - $_content = file_get_contents($_path, false, null, $_size - 65536); - } - $_logs[$_log] = $_content; + $_size = filesize($_path); + $_content = file_get_contents($_path, false, null, $_size - 65536); + $name = basename($_path); + $_logs[$name] = $_content; } $this->set('logs', $_logs); } + public function admin_plugins() { + } + public function admin_stats() { $postingsPA = $this->_getYearStats('Entry', 'time'); $registrationsPA = $this->_getYearStats('User', 'registered'); diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index c6db51a20..4a4dfe4d7 100644 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -1,13 +1,11 @@ 0) { App::uses('FireCake', 'DebugKit.Lib'); } @@ -105,9 +103,20 @@ public function __construct($request = null, $response = null) { Stopwatch::start( '---------------------- Controller ----------------------' ); + + ClassRegistry::addObject('dic', \Saito\DicSetup::getNewDic()); parent::__construct($request, $response); } + public function __get($name) { + switch ($name) { + case 'dic': + return ClassRegistry::getObject('dic'); + default: + return parent::__get($name); + } + } + public function beforeFilter() { Stopwatch::start('App->beforeFilter()'); @@ -151,7 +160,7 @@ public function beforeFilter() { $this->_setConfigurationFromGetParams(); // must be set after all language chooser - Properize::setLanguage(Configure::read('Config.language')); + \Saito\String\Properize::setLanguage(Configure::read('Config.language')); // allow sql explain for DebugKit toolbar if ($this->request->plugin === 'debug_kit') { @@ -298,10 +307,10 @@ protected function _setXFrameOptionsHeader() { * * * @param $type - * @throws Saito\BlackHoledException + * @throws Saito\Exception\SaitoBlackholeException */ public function blackhole($type) { - throw new Saito\BlackHoledException($type, + throw new SaitoBlackholeException($type, ['CurrentUser' => $this->CurrentUser]); } diff --git a/app/Controller/BookmarksController.php b/app/Controller/BookmarksController.php index cc8385dd6..84ad53702 100644 --- a/app/Controller/BookmarksController.php +++ b/app/Controller/BookmarksController.php @@ -55,12 +55,18 @@ public function add() { * @param null $id * @throws NotFoundException * @throws MethodNotAllowedException - * @throws Saito\ForbiddenException */ public function edit($id = null) { $bookmark = $this->_getBookmark($id); if (!$this->request->is('post') && !$this->request->is('put')) { + $posting = array( + 'Entry' => $bookmark['Entry'], + 'Category' => $bookmark['Entry']['Category'], + 'User' => $bookmark['Entry']['User'], + ); + $this->set('entry', $this->dic->newInstance('\Saito\Posting\Posting', + ['rawData' => $posting])); $this->request->data = $bookmark; return; } @@ -103,10 +109,10 @@ public function beforeFilter() { /** * @param $id - * @return mixed * @throws NotFoundException * @throws MethodNotAllowedException - * @throws Saito\ForbiddenException + * @throws Saito\Exception\SaitoForbiddenException + * @return mixed */ protected function _getBookmark($id) { if (!$this->CurrentUser->isLoggedIn()) { @@ -121,7 +127,7 @@ protected function _getBookmark($id) { $bookmark = $this->Bookmark->findById($id); if ($bookmark['Bookmark']['user_id'] != $this->CurrentUser->getId()) { - throw new Saito\ForbiddenException("Attempt to edit bookmark $id."); + throw new Saito\Exception\SaitoForbiddenException("Attempt to edit bookmark $id."); } return $bookmark; } diff --git a/app/Controller/Component/CacheSupportComponent.php b/app/Controller/Component/CacheSupportComponent.php index 1f8611206..86d5e1821 100644 --- a/app/Controller/Component/CacheSupportComponent.php +++ b/app/Controller/Component/CacheSupportComponent.php @@ -1,20 +1,17 @@ {$Controller->modelClass}->SharedObjects['CacheSupport'] = $this->_CacheSupport; } $this->_addConfigureCachelets(); - $this->_initCacheTree($Controller); $this->_initLineCache($Controller); } @@ -32,29 +28,11 @@ protected function _initLineCache() { 'Saito.LineCache', new SaitoCacheEngineAppCache, // duration: update relative time values in HTML at least every hour - ['duration' => 3600, 'maxItems' => 500] + ['duration' => 3600, 'maxItems' => 600] ); $this->_CacheSupport->add(new LineCacheSupportCachelet($this->LineCache)); } - protected function _initCacheTree($Controller) { - $cacheConfig = Cache::settings(); - if ($cacheConfig['engine'] === 'Apc') { - $CacheEngine = new SaitoCacheEngineAppCache; - } else { - $CacheEngine = new SaitoCacheEngineDbCache; - } - - $this->CacheTree = new CacheTree( - 'EntrySub', - $CacheEngine, - ['maxItems' => 240] - ); - - $this->CacheTree->initialize($Controller->CurrentUser); - $this->_CacheSupport->add(new CacheTreeCacheSupportCachelet($this->CacheTree)); - } - /** * Adds additional cachelets from Configure `Saito.Cachelets` * diff --git a/app/Controller/Component/CurrentUserComponent.php b/app/Controller/Component/CurrentUserComponent.php index 923648611..a340967a0 100755 --- a/app/Controller/Component/CurrentUserComponent.php +++ b/app/Controller/Component/CurrentUserComponent.php @@ -1,26 +1,22 @@ name === 'CakeError') { return; } + $this->_Controller = $Controller; if ($this->_Controller->modelClass) { $this->_Controller->{$this->_Controller->modelClass}->SharedObjects['CurrentUser'] = $this; } - $this->Categories = new CategoryAuth($this); + $this->Categories = new CategoryAuthorization($this); + + $this->_Controller->dic->set('CU', $this); /* * We create a new User Model instance. Otherwise we would overwrite $this->request->data @@ -98,7 +93,7 @@ public function initialize(Controller $Controller) { ['class' => 'User', 'alias' => 'currentUser'] ); - $this->PersistentCookie = new SaitoCurrentUserCookie($this->Cookie, 'AU'); + $this->PersistentCookie = new \Saito\User\Cookie\CurrentUserCookie($this->Cookie, 'AU'); $this->_configureAuth(); @@ -117,14 +112,14 @@ public function initialize(Controller $Controller) { } if ($this->isLoggedIn()) { - $this->ReadEntries = new ReadPostingsDatabase($this); + $this->ReadEntries = new ReadPostings\ReadPostingsDatabase($this); } elseif ($this->isBot()) { - $this->ReadEntries = new ReadPostingsDummy($this); + $this->ReadEntries = new ReadPostings\ReadPostingsDummy($this); } else { - $this->ReadEntries = new ReadPostingsCookie($this); + $this->ReadEntries = new ReadPostings\ReadPostingsCookie($this); } - $this->_Bookmarks = new UserBookmarks($this); + $this->_Bookmarks = new Bookmarks($this); $this->_markOnline(); } @@ -231,11 +226,11 @@ public function refresh() { if ($this->isLoggedIn()) { $this->_User->id = $this->getId(); $this->setSettings($this->_User->getProfile($this->getId())); - $this->LastRefresh = new LastRefreshDatabase($this); + $this->LastRefresh = new LastRefresh\LastRefreshDatabase($this); } elseif ($this->isBot()) { - $this->LastRefresh = new LastRefreshDummy($this); + $this->LastRefresh = new LastRefresh\LastRefreshDummy($this); } else { - $this->LastRefresh = new LastRefreshCookie($this); + $this->LastRefresh = new LastRefresh\LastRefreshCookie($this); } } diff --git a/app/Controller/Component/JsDataComponent.php b/app/Controller/Component/JsDataComponent.php index d9ca3a0ee..24bdc9a1d 100644 --- a/app/Controller/Component/JsDataComponent.php +++ b/app/Controller/Component/JsDataComponent.php @@ -1,7 +1,8 @@ set($controller->User); $smilies = new \Saito\Smiley\Cache($controller); $controller->set('smiliesData', $smilies); diff --git a/app/Controller/DynamicAssetsController.php b/app/Controller/DynamicAssetsController.php index 29a090815..733500875 100644 --- a/app/Controller/DynamicAssetsController.php +++ b/app/Controller/DynamicAssetsController.php @@ -20,7 +20,7 @@ class DynamicAssetsController extends Controller { * Output current language strings as json */ public function langJs() { - // dummy translation to load po files + //= dummy translation to load po files __d('nondynamic', 'foo'); __d('default', 'foo'); $domains = I18n::domains(); diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 1d50426ab..6930c1103 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -1,5 +1,7 @@ _prepareSlidetabData(); - //# determine user sort order + //= determine user sort order $sortKey = 'Entry.'; if ($this->CurrentUser['user_sort_last_answer'] == false) { $sortKey .= 'time'; @@ -30,39 +32,13 @@ public function index() { $sortKey .= 'last_answer'; } $order = ['Entry.fixed' => 'DESC', $sortKey => 'DESC']; - $initials = $this->_getInitialThreads($this->CurrentUser, $order); - - //# match initial threads against cache - $threads = []; - $uncached = []; - foreach ($initials as $thread) { - // ensure string so that integer index won't reorder - $id = (string)$thread['id']; - if ($this->CacheSupport->CacheTree->isCacheValid($thread)) { - $threads[$id] = $this->CacheSupport->CacheTree->get($id); - } else { - $threads[$id] = $id; - $uncached[$id] = $thread; - } - } - - //# get threads not available in cache from DB - $dbThreads = $this->Entry->treesForThreads($uncached, $order); - - $page = 0; - if (isset($this->request->named['page'])) { - $page = $this->request->named['page']; - } - - foreach ($dbThreads as $thread) { - $id = (string)$thread['Entry']['tid']; - $threads[$id] = $thread; - if ($page < 3 && $this->CacheSupport->CacheTree->isCacheUpdatable($thread['Entry'])) { - $this->CacheSupport->CacheTree->set($id, $thread); - } + //= get threads + $initials = $this->_getInitialThreads($this->CurrentUser, $order); + $threads = $this->Entry->treesForThreads($initials, $order); + foreach ($threads as $tid => $thread) { + $threads[$tid] = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $thread]); } - $this->set('entries', $threads); $currentPage = 1; @@ -76,6 +52,7 @@ public function index() { // @bogus $this->Session->write('paginator.lastPage', $currentPage); $this->showDisclaimer = true; + $this->set('allowThreadCollapse', true); Stopwatch::stop('Entries->index()'); } @@ -128,16 +105,19 @@ public function mix($tid) { } // check if anonymous tries to access internal categories - $accession = $entries[0]['Category']['accession']; + $root = reset($entries); + $accession = $root['Category']['accession']; if (!$this->CurrentUser->Categories->isAccessionAuthorized($accession)) { $this->_requireAuth(); return; } - $root = $entries[0]; $this->_setRootEntry($root); $this->_setTitleFromEntry($root, __('view.type.mix')); + + $entries = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $root]); $this->set('entries', $entries); + $this->_showAnsweringPanel(); $this->_incrementViews($root, 'thread'); @@ -309,8 +289,10 @@ public function add($id = null) { } $this->request->data = $this->Entry->get($id); + $posting = $this->dic->newInstance('\Saito\Posting\Posting', + ['rawData' => $this->request->data]); - if ($this->Entry->isAnsweringForbidden($this->request->data)) { + if ($posting->isAnsweringForbidden()) { throw new ForbiddenException; } @@ -380,7 +362,10 @@ public function edit($id = null) { throw new NotFoundException(); } - switch ($oldEntry['rights']['isEditingForbidden']) { + /** * @var \Saito\Posting\Posting */ + $posting = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $oldEntry]); + + switch ($posting->isEditingAsCurrentUserForbidden()) { case 'time': $this->Session->setFlash( 'Stand by your word bro\', it\'s too late. @lo', @@ -415,7 +400,7 @@ public function edit($id = null) { } // show editing form - if ($oldEntry['rights']['isEditingAsUserForbidden']) { + if ($posting->isEditingWithRoleUserForbidden()) { $this->Session->setFlash(__('notice_you_are_editing_as_mod'), 'flash/warning'); } @@ -560,6 +545,7 @@ public function preview() { 'subject' => $data['subject'], 'text' => $data['text'], 'category' => $data['category'], + 'edited_by' => null, 'fixed' => false, 'solves' => 0, 'views' => 0, @@ -588,7 +574,8 @@ public function preview() { ) ) ); - $this->set('entry', $newEntry); + $entry = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $newEntry]); + $this->set('entry', $entry); else : // validation errors foreach ($errors as $field => $error) { @@ -852,14 +839,14 @@ protected function _getInitialThreads(CurrentUserComponent $User, $order) { $initialThreadsNew = []; foreach ($initialThreads as $k => $v) { - $initialThreadsNew[$k] = $v['Entry']; + $initialThreadsNew[$k] = $v['Entry']['id']; } Stopwatch::stop('Entries->_getInitialThreads() Paginate'); return $initialThreadsNew; } - protected function _setupCategoryChooser(ForumsUserInterface $User) { + protected function _setupCategoryChooser(\Saito\User\ForumsUserInterface $User) { $categories = $User->Categories->getAllowed(); $isUsed = $User->isLoggedIn() && @@ -874,8 +861,7 @@ protected function _setupCategoryChooser(ForumsUserInterface $User) { if ($isUsed) { // @todo find right place for this; also: User::getCategories(); - App::uses('UserCategories', 'Lib'); - $UserCategories = new UserCategories($User->getSettings(), $categories); + $UserCategories = new Categories($User->getSettings(), $categories); list($categories, $type, $custom) = $UserCategories->get(); $this->set('categoryChooserChecked', $custom); diff --git a/app/Controller/SearchesController.php b/app/Controller/SearchesController.php index 1313609f3..d6db49cf7 100644 --- a/app/Controller/SearchesController.php +++ b/app/Controller/SearchesController.php @@ -1,7 +1,8 @@ request->data['User']['password']); - $Logger = new \Saito\Logger\ForbiddenLogger(); + $Logger = new ForbiddenLogger; $Logger->write("Unsuccessful login for user: $username", ['msgs' => [$message]]); @@ -129,7 +134,7 @@ public function register() { // only used in test cases $this->set('email', $email); } catch (Exception $e) { - $Logger = new Saito\Logger\ExceptionLogger(); + $Logger = new ExceptionLogger(); $Logger->write('Registering email confirmation failed', ['e' => $e]); $this->set('status', 'fail: email'); return; @@ -337,17 +342,17 @@ public function view($id = null) { ); } - /** - * @param null $id - * @throws Saito\ForbiddenException - * @throws BadRequestException - */ + /** + * @param null $id + * @throws Saito\Exception\SaitoForbiddenException + * @throws BadRequestException + */ public function edit($id = null) { if (!$id) { throw new BadRequestException; } if (!$this->_isEditingAllowed($this->CurrentUser, $id)) { - throw new \Saito\ForbiddenException("Attempt to edit user $id.", [ + throw new \Saito\Exception\SaitoForbiddenException("Attempt to edit user $id.", [ 'CurrentUser' => $this->CurrentUser ]); } @@ -399,7 +404,7 @@ public function edit($id = null) { $this->set( 'title_for_layout', __('Edit %s Profil', - Properize::prop($this->request->data['User']['username'])) + Saito\String\Properize::prop($this->request->data['User']['username'])) ); } @@ -440,12 +445,15 @@ public function lock() { ); } else { try { - App::uses('UserBlockerManual', 'Lib/SaitoUser/Blocking'); $duration = (int)$this->request->data('User.lockPeriod'); - $status = $this->User->UserBlock->block(new UserBlockerManual, $id, [ - 'adminId' => $this->CurrentUser->getId(), - 'duration' => $duration - ]); + $status = $this->User->UserBlock->block( + new \Saito\User\Blocker\ManualBlocker, + $id, + [ + 'adminId' => $this->CurrentUser->getId(), + 'duration' => $duration + ] + ); $username = $readUser['User']['username']; if ($status === true) { $message = __('User %s is locked.', $username); @@ -501,7 +509,7 @@ public function admin_delete($id = null) { * changes user password * * @param null $id - * @throws Saito\ForbiddenException + * @throws \Saito\Exception\SaitoForbiddenException * @throws BadRequestException */ public function changepassword($id = null) { @@ -512,7 +520,7 @@ public function changepassword($id = null) { $user = $this->User->getProfile($id); $allowed = $this->_isEditingAllowed($this->CurrentUser, $id); if (empty($user) || !$allowed) { - throw new \Saito\ForbiddenException("Attempt to change password for user $id.", + throw new SaitoForbiddenException("Attempt to change password for user $id.", ['CurrentUser' => $this->CurrentUser]); } $this->set('userId', $id); @@ -655,7 +663,7 @@ public function beforeFilter() { * @param int $userId * @return type */ - protected function _isEditingAllowed(ForumsUserInterface $CurrentUser, $userId) { + protected function _isEditingAllowed(\Saito\User\ForumsUserInterface $CurrentUser, $userId) { if ($CurrentUser->isAdmin()) { return true; } diff --git a/app/Lib/Cache/CacheTree.php b/app/Lib/Cache/CacheTree.php deleted file mode 100644 index a0832a090..000000000 --- a/app/Lib/Cache/CacheTree.php +++ /dev/null @@ -1,115 +0,0 @@ -_CurrentUser = $CurrentUser; - - if (Configure::read('debug') > 1) { - Configure::write('Saito.Cache.Thread', false); - } - - if (!Configure::read('Saito.Cache.Thread')) { - return; - } - - $this->_allowUpdate = true; - $this->_allowRead = true; - } - - public function isCacheUpdatable(array $entry) { - if (!$this->_allowUpdate) { - return false; - } - return $this->_isRead($entry); - } - - public function isCacheValid(array $entry) { - if (!$this->_allowRead) { - return false; - } - - $id = (int)$entry['id']; - if (isset($this->_validEntries[$id])) { - return $this->_validEntries[$id]; - } - - if (!$this->_inCache($entry)) { - $valid = false; - } elseif ($this->_isRead($entry)) { - $valid = true; - } else { - $valid = false; - } - - $this->_validEntries[$id] = $valid; - return $valid; - } - - /** - * @param array $entry - * @return bool - */ - protected function _isRead(array $entry) { - return $this->_CurrentUser->LastRefresh->isNewerThan($entry['last_answer']) === true; - } - - protected function _inCache($entry) { - $id = $entry['id']; - - if (!$this->get($id)) { - return false; - } - - $lastAnswer = strtotime($entry['last_answer']); - $hasNewerAnswers = $this->compareUpdated($id, $lastAnswer, - function ($updated, $lastAnswer) { - return $lastAnswer > $updated; - }); - if ($hasNewerAnswers) { - $this->delete($id); - return false; - } - - return true; - } - - public function get($id = null) { - if (!$this->_allowRead) { - return false; - } - return parent::get($id); - } - - public function set($id, $content, $timestamp = null) { - if (!$this->_allowUpdate) { - return false; - } - parent::set($id, $content, $timestamp); - } - - } \ No newline at end of file diff --git a/app/Lib/Cache/CacheTreeCacheSupportCachelet.php b/app/Lib/Cache/CacheTreeCacheSupportCachelet.php deleted file mode 100644 index 6583a0f26..000000000 --- a/app/Lib/Cache/CacheTreeCacheSupportCachelet.php +++ /dev/null @@ -1,53 +0,0 @@ -_CacheTree = $CacheTree; - CakeEventManager::instance()->attach($this); - } - - public function implementedEvents() { - return [ - 'Model.Thread.change' => 'onThreadChanged', - 'Model.Entry.replyToEntry' => 'onEntryChanged', - 'Model.Entry.update' => 'onEntryChanged' - ]; - } - - public function onThreadChanged($event) { - $this->clear($event->data['subject']); - } - - /** - * @param $event - * @throws InvalidArgumentException - */ - public function onEntryChanged($event) { - $_modelAlias = $event->subject()->alias; - if (!isset($event->data['data'][$_modelAlias]['tid'])) { - throw new InvalidArgumentException('No thread-id in event data.'); - } - $_threadId = $event->data['data'][$_modelAlias]['tid']; - $this->clear($_threadId); - } - - public function clear($id = null) { - Cache::clearGroup('entries', 'entries'); - if ($id === null) { - $this->_CacheTree->reset(); - } else { - $this->_CacheTree->delete($id); - } - } - - } - diff --git a/app/Lib/Cache/SaitoCacheEngineDbCache.php b/app/Lib/Cache/SaitoCacheEngineDbCache.php deleted file mode 100644 index 7bb841df5..000000000 --- a/app/Lib/Cache/SaitoCacheEngineDbCache.php +++ /dev/null @@ -1,52 +0,0 @@ -_Database === null) { - $this->_Database = ClassRegistry::init('Ecach'); - $this->_Database->primaryKey = 'key'; - } - return $this->_Database; - } - - public function read($name) { - $result = $this->_db()->findByKey($name); - if (!$result) { - return []; - } - - $result = @unserialize($result['Ecach']['value']); - // catches storage overflow - if ($result === false) { - $this->_reset($name); - return []; - } - - return $result; - } - - protected function _reset($name) { - $this->write($name, []); - } - - public function write($name, $content) { - // @todo @bogus - // Calling this write in ItemCache::__deconstruct fails in PHPUnit - // because the table is already cleaned up and does no longer exist - // to write the content. - // Shouldn't never be an issue on a real server. - try { - return $this->_db()->save([ - 'Ecach' => ['key' => $name, 'value' => serialize($content)] - ]); - } catch (Exception $e) { - return false; - } - } - - } \ No newline at end of file diff --git a/app/Lib/Cache/CacheSupport.php b/app/Lib/Saito/Cache/CacheSupport.php similarity index 64% rename from app/Lib/Cache/CacheSupport.php rename to app/Lib/Saito/Cache/CacheSupport.php index 879326d4d..9a0d3a1ff 100644 --- a/app/Lib/Cache/CacheSupport.php +++ b/app/Lib/Saito/Cache/CacheSupport.php @@ -1,9 +1,11 @@ _buildInCaches as $_name) { - $this->add(new $_name); + $name = 'Saito\Cache\\' . $_name; + $this->add(new $name); } - CakeEventManager::instance()->attach($this); + \CakeEventManager::instance()->attach($this); } public function implementedEvents() { @@ -82,22 +86,11 @@ public function getId(); } - abstract class CacheSupportCachelet implements CacheSupportCacheletInterface { - - public function getId() { - if (!empty($this->_title)) { - return $this->_title; - } - return str_replace('CacheSupportCachelet', '', get_class($this)); - } - - } - class SaitoCacheSupportCachelet extends CacheSupportCachelet { public function clear($id = null) { - Cache::clear(false, 'default'); - Cache::clear(false, 'short'); + \Cache::clear(false, 'default'); + \Cache::clear(false, 'short'); } } @@ -129,9 +122,45 @@ class CakeCacheSupportCachelet extends CacheSupportCachelet { protected $_title = 'Cake'; public function clear($id = null) { - Cache::clearGroup('persistent'); - Cache::clearGroup('models'); - Cache::clearGroup('views'); + \Cache::clearGroup('persistent'); + \Cache::clearGroup('models'); + \Cache::clearGroup('views'); + } + + } + + class EntriesCacheSupportCachelet extends CacheSupportCachelet implements \CakeEventListener { + + protected $_title = 'EntriesCache'; + + protected $_CacheTree; + + public function __construct() { + \CakeEventManager::instance()->attach($this); + } + + public function implementedEvents() { + return [ + 'Model.Thread.change' => 'onThreadChanged', + 'Model.Entry.replyToEntry' => 'onEntryChanged', + 'Model.Entry.update' => 'onEntryChanged' + ]; + } + + public function onThreadChanged($event) { + $this->clear(); + } + + /** + * @param $event + * @throws InvalidArgumentException + */ + public function onEntryChanged($event) { + $this->clear(); + } + + public function clear($id = null) { + \Cache::clearGroup('entries', 'entries'); } } diff --git a/app/Lib/Saito/Cache/CacheSupportCachelet.php b/app/Lib/Saito/Cache/CacheSupportCachelet.php new file mode 100644 index 000000000..ae14dca6b --- /dev/null +++ b/app/Lib/Saito/Cache/CacheSupportCachelet.php @@ -0,0 +1,15 @@ +_title)) { + return $this->_title; + } + return str_replace('CacheSupportCachelet', '', get_class($this)); + } + + } + diff --git a/app/Lib/Cache/ItemCache.php b/app/Lib/Saito/Cache/ItemCache.php similarity index 62% rename from app/Lib/Cache/ItemCache.php rename to app/Lib/Saito/Cache/ItemCache.php index ede65cbcb..b018a136e 100644 --- a/app/Lib/Cache/ItemCache.php +++ b/app/Lib/Saito/Cache/ItemCache.php @@ -1,7 +1,15 @@ null, 'maxItems' => null, // +/- percentage maxItems can deviate before gc is triggered - 'maxItemsFuzzy' => 0.05 + 'maxItemsFuzzy' => 0.06 ]; + protected $_gcFuzzy; + + protected $_gcMax; + + protected $_gcMin; + protected $_name; protected $_now; + protected $_oldestPersistent = 0; + protected $_updated = false; public function __construct($name, SaitoCacheEngineInterface $CacheEngine = null, $options = []) { @@ -29,6 +45,12 @@ public function __construct($name, SaitoCacheEngineInterface $CacheEngine = null $this->_now = time(); $this->_name = $name; $this->_CacheEngine = $CacheEngine; + + if ($this->_settings['maxItems']) { + $this->_gcFuzzy = $this->_settings['maxItemsFuzzy']; + $this->_gcMax = (int)($this->_settings['maxItems'] * (1 + $this->_gcFuzzy)); + $this->_gcMin = (int)($this->_gcMax * (1 - $this->_gcFuzzy)); + } } public function __destruct() { @@ -70,7 +92,7 @@ public function get($key = null) { */ public function compareUpdated($key, $timestamp, callable $comp) { if (!isset($this->_cache[$key])) { - throw new InvalidArgumentException; + throw new \InvalidArgumentException; } return $comp($this->_cache[$key]['metadata']['content_last_updated'], $timestamp); @@ -81,11 +103,26 @@ public function set($key, $content, $timestamp = null) { $this->_read(); } - $this->_updated = true; - if (!$timestamp) { $timestamp = $this->_now; } + + /* + * Don't fill the cache with entries which are removed by the maxItemsGC. + * Old entries may trigger a store to disk on each request without + * adding new cache data. + */ + // there's no upper limit + if (!$this->_gcMax) { + $this->_updated = true; + // the new entry is not older than the oldest existing + } elseif ($timestamp > $this->_oldestPersistent) { + $this->_updated = true; + // there's still room in lower maxItemsGc limit + } elseif (count($this->_cache) < $this->_gcMin) { + $this->_updated = true; + } + $metadata = [ 'created' => $this->_now, 'content_last_updated' => $timestamp, @@ -95,12 +132,19 @@ public function set($key, $content, $timestamp = null) { $this->_cache[$key] = $data; } + protected function _isCacheFull() { + if (!$this->_gcMax) { + return false; + } + return count($this->_cache) >= $this->_settings['maxItems']; + } + protected function _read() { if ($this->_CacheEngine === null) { $this->_cache = []; return; } - Stopwatch::start("ItemCache read: {$this->_name}"); + \Stopwatch::start("ItemCache read: {$this->_name}"); $this->_cache = $this->_CacheEngine->read($this->_name); if (empty($this->_cache)) { $this->_cache = []; @@ -108,8 +152,11 @@ protected function _read() { if ($this->_settings['duration']) { $this->_gcOutdated(); } - Stopwatch::stop("ItemCache read: {$this->_name}"); - return; + if (count($this->_cache) > 0) { + $oldest = reset($this->_cache); + $this->_oldestPersistent = $oldest['metadata']['content_last_updated']; + } + \Stopwatch::stop("ItemCache read: {$this->_name}"); } public function reset() { @@ -118,7 +165,7 @@ public function reset() { } protected function _gcOutdated() { - Stopwatch::start("ItemCache _gcOutdated: {$this->_name}"); + \Stopwatch::start("ItemCache _gcOutdated: {$this->_name}"); $expired = time() - $this->_settings['duration']; foreach ($this->_cache as $key => $item) { if ($item['metadata']['created'] < $expired) { @@ -126,7 +173,7 @@ protected function _gcOutdated() { $this->_updated = true; } } - Stopwatch::stop("ItemCache _gcOutdated: {$this->_name}"); + \Stopwatch::stop("ItemCache _gcOutdated: {$this->_name}"); } /** @@ -135,16 +182,16 @@ protected function _gcOutdated() { * costly function for larger arrays, relieved by maxItemsFuzzy */ protected function _gcMaxItems() { - // Stopwatch::start("ItemCache _gxMaxItems: {$this->_name}"); - - $fuzzy = $this->_settings['maxItemsFuzzy']; - $max = (int)($this->_settings['maxItems'] * (1 + $fuzzy)); - - if (count($this->_cache) <= $max) { - Stopwatch::stop("ItemCache _gxMaxItems: {$this->_name}"); + if (count($this->_cache) <= $this->_gcMax) { return; } + $this->_cache = array_slice($this->_cache, 0, $this->_gcMin, true); + } + /** + * sorts for 'content_last_updated', oldest on top + */ + protected function _sort() { // keep items which were last used/updated uasort($this->_cache, function ($a, $b) { if ($a['metadata']['content_last_updated'] === $b['metadata']['content_last_updated']) { @@ -152,17 +199,14 @@ protected function _gcMaxItems() { } return ($a['metadata']['content_last_updated'] < $b['metadata']['content_last_updated']) ? 1 : -1; }); - - $min = (int)($this->_settings['maxItems'] * (1 - $fuzzy)); - $this->_cache = array_slice($this->_cache, 0, $min, true); - // Stopwatch::stop("ItemCache _gxMaxItems: {$this->_name}"); } protected function _write() { if ($this->_CacheEngine === null || !$this->_updated) { return; } - if ($this->_settings['maxItems']) { + $this->_sort(); + if ($this->_gcMax) { $this->_gcMaxItems(); } $this->_CacheEngine->write($this->_name, $this->_cache); diff --git a/app/Lib/Cache/LineCacheSupportCachelet.php b/app/Lib/Saito/Cache/LineCacheSupportCachelet.php similarity index 75% rename from app/Lib/Cache/LineCacheSupportCachelet.php rename to app/Lib/Saito/Cache/LineCacheSupportCachelet.php index ffe5c527c..bc194b60e 100644 --- a/app/Lib/Cache/LineCacheSupportCachelet.php +++ b/app/Lib/Saito/Cache/LineCacheSupportCachelet.php @@ -1,18 +1,19 @@ attach($this); + \CakeEventManager::instance()->attach($this); $this->_LineCache = $LineCache; } @@ -29,7 +30,7 @@ public function implementedEvents() { public function onEntryChanged($event) { $_modelAlias = $event->subject()->alias; if (!isset($event->data['data'][$_modelAlias]['tid'])) { - throw new InvalidArgumentException('No thread-id in event data.'); + throw new \InvalidArgumentException('No thread-id in event data.'); } $id = $event->data['data'][$_modelAlias]['id']; $this->clear($id); diff --git a/app/Lib/Cache/SaitoCacheEngineAppCache.php b/app/Lib/Saito/Cache/SaitoCacheEngineAppCache.php similarity index 57% rename from app/Lib/Cache/SaitoCacheEngineAppCache.php rename to app/Lib/Saito/Cache/SaitoCacheEngineAppCache.php index d402e8d56..08fd074ca 100644 --- a/app/Lib/Cache/SaitoCacheEngineAppCache.php +++ b/app/Lib/Saito/Cache/SaitoCacheEngineAppCache.php @@ -1,15 +1,15 @@ params['\Saito\Posting\Posting']['CurrentUser'] = $dic->lazyGet('CU'); + return $dic; + } + + } \ No newline at end of file diff --git a/app/Lib/DomainParser.php b/app/Lib/Saito/DomainParser.php similarity index 97% rename from app/Lib/DomainParser.php rename to app/Lib/Saito/DomainParser.php index 93a52cad2..9c67833f8 100644 --- a/app/Lib/DomainParser.php +++ b/app/Lib/Saito/DomainParser.php @@ -1,5 +1,7 @@ __Logger = new ExceptionLogger; + $this->__Logger->write($message, $data); + parent::__construct($message, 400); + } + + } + diff --git a/app/Lib/Saito/Exception/SaitoForbiddenException.php b/app/Lib/Saito/Exception/SaitoForbiddenException.php new file mode 100644 index 000000000..a628028c6 --- /dev/null +++ b/app/Lib/Saito/Exception/SaitoForbiddenException.php @@ -0,0 +1,21 @@ +__Logger = new ForbiddenLogger; + $this->__Logger->write($message, $data); + parent::__construct($message, 403); + } + + } + diff --git a/app/Lib/JsData.php b/app/Lib/Saito/JsData.php similarity index 93% rename from app/Lib/JsData.php rename to app/Lib/Saito/JsData.php index 083e17a25..4a8123451 100644 --- a/app/Lib/JsData.php +++ b/app/Lib/Saito/JsData.php @@ -1,5 +1,7 @@ _appJs[$key] = $value; } - public function addAppJsMessage($message, $options = null) { + public function addAppJsMessage($message, $options = []) { $defaults = array( 'type' => 'notice', 'channel' => 'notification' diff --git a/app/Lib/Saito/SaitoPlugin.php b/app/Lib/Saito/Plugin.php similarity index 73% rename from app/Lib/Saito/SaitoPlugin.php rename to app/Lib/Saito/Plugin.php index 6f4682257..7b2379ed0 100644 --- a/app/Lib/Saito/SaitoPlugin.php +++ b/app/Lib/Saito/Plugin.php @@ -1,6 +1,8 @@ newInstanceArgs($args); } diff --git a/app/Lib/Saito/Posting/Decorator/DecoratorInterface.php b/app/Lib/Saito/Posting/Decorator/DecoratorInterface.php new file mode 100644 index 000000000..4c2faf14e --- /dev/null +++ b/app/Lib/Saito/Posting/Decorator/DecoratorInterface.php @@ -0,0 +1,56 @@ +_Posting = $Posting; + return $this; + } + + public function get($var) { + return $this->_Posting->get($var); + } + + public function getChildren() { + return $this->_Posting->getChildren(); + } + + public function getLevel() { + return $this->_Posting->getLevel(); + } + + public function getRaw() { + return $this->_Posting->getRaw(); + } + + public function getThread() { + return $this->_Posting->getThread(); + } + + public function hasAnswers() { + return $this->_Posting->hasAnswers(); + } + + public function isNt() { + return $this->_Posting->isNt(); + } + + public function isPinned() { + return $this->_Posting->isPinned(); + } + + public function isRoot() { + return $this->_Posting->isRoot(); + } + + public function addDecorator($fct) { + return $this->_Posting->addDecorator($fct); + } + + } \ No newline at end of file diff --git a/app/Lib/Saito/Posting/Decorator/UserPostingTrait.php b/app/Lib/Saito/Posting/Decorator/UserPostingTrait.php new file mode 100644 index 000000000..3404f8d25 --- /dev/null +++ b/app/Lib/Saito/Posting/Decorator/UserPostingTrait.php @@ -0,0 +1,134 @@ +_CU; + } + + public function setCurrentUser($CU) { + $this->_CU = $CU; + } + + /** + * Checks if answering an entry is allowed + * + * @param array $entry + * @return boolean + */ + public function isAnsweringForbidden() { + if ($this->isLocked()) { + $isAnsweringForbidden = 'locked'; + } else { + $isAnsweringForbidden = false; + } + return $isAnsweringForbidden; + } + + /** + * checks if entry is bookmarked by current user + */ + public function isBookmarked() { + return $this->_CU->hasBookmarked($this->get('id')); + } + + public function isEditingAsCurrentUserForbidden() { + return $this->_isEditingForbidden($this, $this->_CU); + } + + public function isEditingWithRoleUserForbidden() { + return $this->_isEditingForbidden($this, $this->_CU->mockUserType('user')); + } + + protected function _isEditingForbidden(\Saito\Posting\Posting $posting, $User) { + // Anon + if ($User->isLoggedIn() !== true) { + return true; + } + + // Admins + if ($User->isAdmin()) { + return false; + } + + $verboten = true; + + $timeLimit = (\Configure::read('Saito.Settings.edit_period') * 60) + strtotime($posting->get('time')); + $isOverTimeLimit = time() > $timeLimit; + + $isUsersPosting = (int)$User->getId() === (int)$posting->get('user_id'); + + if ($User->isMod()) { + // Mods + // @todo mods don't edit admin posts + if ($isUsersPosting && $isOverTimeLimit && + /* 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, + * as long as no one checks pinning for Configure::read('Saito.Settings.edit_period') * 60 + * for mods pinning root-posts. + */ + (!$posting->isPinned()) + ) { + // mods don't mod themselves + $verboten = 'time'; + } else { + $verboten = false; + }; + + } else { + // Users + if ($isUsersPosting === false) { + $verboten = 'user'; + } elseif ($isOverTimeLimit) { + $verboten = 'time'; + } elseif ($this->isLocked()) { + $verboten = 'locked'; + } else { + $verboten = false; + } + } + + return $verboten; + } + + public function isIgnored() { + return $this->_CU->ignores($this->get('user_id')); + } + + public function isNew() { + if (isset($this->_cache['isNew'])) { + $this->_cache['isNew'] = $this->_cache['isNew']; + } else { + $id = $this->get('id'); + $time = $this->get('time'); + $this->_cache['isNew'] = !$this->_CU->ReadEntries->isRead($id, $time); + } + return $this->_cache['isNew']; + } + + /** + * Checks if posting has newer answers + * + * currently only supported for root postings + * + * @return bool + * @throws \RuntimeException + */ + public function hasNewAnswers() { + if (!$this->isRoot()) { + throw new \RuntimeException('Posting with id ' . $this->get('id') . ' is no root posting.'); + } + if (!isset($this->_CU['last_refresh'])) { + return false; + } + return $this->_CU['last_refresh_unix'] < strtotime($this->get('last_answer')); + } + + } diff --git a/app/Lib/Saito/Posting/Posting.php b/app/Lib/Saito/Posting/Posting.php new file mode 100644 index 000000000..a74a6da4e --- /dev/null +++ b/app/Lib/Saito/Posting/Posting.php @@ -0,0 +1,126 @@ +_rawData = $rawData; + $this->_rawData[self::ALIAS]['id'] = (int)$this->_rawData[self::ALIAS]['id']; + if (isset($this->_rawData[self::ALIAS]['pid'])) { + $this->_rawData[self::ALIAS]['pid'] = (int)$this->_rawData[self::ALIAS]['pid']; + } + if (isset($this->_rawData[self::ALIAS]['tid'])) { + $this->_rawData[self::ALIAS]['tid'] = (int)$this->_rawData[self::ALIAS]['tid']; + } + + $this->setCurrentUser($CurrentUser); + + $options += ['level' => 0]; + $this->_level = $options['level']; + + if (!$tree) { + $tree = new \Saito\Thread\Thread; + } + $this->_Thread = $tree; + $this->_Thread->add($this); + + $this->_attachChildren(); + return $this; + } + + /** + * @param $var + * @return mixed + * @throws \InvalidArgumentException + */ + public function get($var) { + switch ($var) { + case (isset($this->_rawData[self::ALIAS][$var])): + return $this->_rawData[self::ALIAS][$var]; + case (isset($this->_rawData[$var])): + return $this->_rawData[$var]; + // if key is set but null + case (array_key_exists($var, $this->_rawData[self::ALIAS])): + return $this->_rawData[self::ALIAS][$var]; + default: + throw new \InvalidArgumentException("Attribute '$var' not found in class Posting."); + } + } + + public function getLevel() { + return $this->_level; + } + + public function getChildren() { + return $this->_children; + } + + public function getRaw() { + return $this->_rawData; + } + + public function getThread() { + return $this->_Thread; + } + + public function hasAnswers() { + return count($this->_children) > 0; + } + + public function isLocked() { + return $this->get('locked') != false; + } + + /** + * checks if entry is n/t + * + * @return bool + */ + public function isNt() { + return empty($this->_rawData[self::ALIAS]['text']); + } + + public function isPinned() { + return $this->_rawData[self::ALIAS]['fixed'] == true; + } + + public function isRoot() { + return $this->_rawData[self::ALIAS]['pid'] === 0; + } + + public function addDecorator($fct) { + foreach ($this->_children as $key => $child) { + $newChild = $fct($child); + $newChild->addDecorator($fct); + $this->_children[$key] = $newChild; + } + $new = $fct($this); + // replace decorated object in Thread collection + $this->_Thread->add($new); + return $new; + } + + protected function _attachChildren() { + if (isset($this->_rawData['_children'])) { + foreach ($this->_rawData['_children'] as $child) { + $this->_children[] = new Posting($this->getCurrentUser(), $child, ['level' => $this->_level + 1], $this->_Thread); + } + } + unset($this->_rawData['_children']); + } + + } + diff --git a/app/Lib/Thread/PostingInterface.php b/app/Lib/Saito/Posting/PostingInterface.php similarity index 53% rename from app/Lib/Thread/PostingInterface.php rename to app/Lib/Saito/Posting/PostingInterface.php index 91ddf0dfe..92acaac03 100644 --- a/app/Lib/Thread/PostingInterface.php +++ b/app/Lib/Saito/Posting/PostingInterface.php @@ -1,13 +1,23 @@ isPinned()) { $out .= ' '; } // anchor for inserting solve-icon via FE-JS - $out .= ''; - if ($entry['Entry']['solves']) { + $out .= ''; + if ($entry->get('solves')) { $out .= $this->solvedBadge(); } $out .= ''; if (!isset($this->_SEM)) { - $this->_SEM = SaitoEventManager::getInstance(); + $this->_SEM = \SaitoEventManager::getInstance(); } $additionalBadges = $this->_SEM->dispatch( 'Request.Saito.View.Posting.badges', - ['posting' => $entry] + ['posting' => $entry->getRaw()] ); if ($additionalBadges) { $out .= implode('', $additionalBadges); @@ -46,11 +45,11 @@ public function solvedBadge() { * This function may be called serveral hundred times on the front page. * Don't make ist slow, benchmark! * - * @param $entry + * @param $posting * @return string */ - public function getSubject($entry) { - return h($entry['Entry']['subject']) . (empty($entry['Entry']['text']) ? ' n/t' : ''); + public function getSubject(\Saito\Posting\Posting $posting) { + return \h($posting->get('subject')) . ($posting->isNt() ? ' n/t' : ''); } } \ No newline at end of file diff --git a/app/Lib/SimpleSearchString.php b/app/Lib/Saito/SimpleSearchString.php similarity index 98% rename from app/Lib/SimpleSearchString.php rename to app/Lib/Saito/SimpleSearchString.php index 0425a73c4..96f02c38e 100644 --- a/app/Lib/SimpleSearchString.php +++ b/app/Lib/Saito/SimpleSearchString.php @@ -1,5 +1,7 @@ loadHTML('' . $html); - $xpath = new DOMXPath($document); + $xpath = new \DOMXPath($document); libxml_clear_errors(); return $xpath; } diff --git a/app/Lib/Test/SaitoControllerTestCase.php b/app/Lib/Saito/Test/ControllerTestCase.php similarity index 66% rename from app/Lib/Test/SaitoControllerTestCase.php rename to app/Lib/Saito/Test/ControllerTestCase.php index c2b04fb83..01cf4692c 100644 --- a/app/Lib/Test/SaitoControllerTestCase.php +++ b/app/Lib/Saito/Test/ControllerTestCase.php @@ -1,5 +1,7 @@ _logoutUser(); - $userFixture = new UserFixture(); + $userFixture = new \UserFixture(); $users = $userFixture->records; $this->controller->Session->write('Auth.User', $users[$id - 1]); } protected function _debugEmail() { - Configure::write('Saito.Debug.email', true); + \Configure::write('Saito.Debug.email', true); } protected function _resetEmail() { - Configure::write('Saito.Debug.email', false); + \Configure::write('Saito.Debug.email', false); } public function assertRedirectedTo($url = '') { $this->assertEquals( $this->headers['Location'], - Router::fullBaseUrl() . $this->controller->request->webroot . $url + \Router::fullBaseUrl() . $this->controller->request->webroot . $url ); } @@ -121,12 +121,12 @@ public function setUp() { $this->_unsetAjax(); $this->_unsetJson(); $this->_debugEmail(); - Configure::write('Cache.disable', true); - Configure::write('Config.language', 'eng'); + \Configure::write('Cache.disable', true); + \Configure::write('Config.language', 'eng'); } public function tearDown() { - Configure::write('Cache.disable', false); + \Configure::write('Cache.disable', false); $this->_unsetUserAgent(); $this->_resetEmail(); $this->_logoutUser(); @@ -135,34 +135,4 @@ public function tearDown() { } - trait SaitoSecurityMockTrait { - - public function generate($controller, $mocks = []) { - $byPassSecurity = false; - if (!isset($mocks['components']['Security'])) { - $byPassSecurity = true; - $mocks['components']['Security'] = ['_validateCsrf', '_validatePost']; - } - $Mock = parent::generate($controller, $mocks); - if ($byPassSecurity) { - $this->assertSecurityByPass($Mock); - } - return $Mock; - } - - /** - * Assume that SecurityComponent was called - * - * @param $Controller - */ - public function assertSecurityBypass($Controller) { - $Controller->Security->expects($this->any()) - ->method('_validatePost') - ->will($this->returnValue(true)); - $Controller->Security->expects($this->any()) - ->method('_validateCsrf') - ->will($this->returnValue(true)); - } - - } diff --git a/app/Lib/Saito/Test/DicSetup.php b/app/Lib/Saito/Test/DicSetup.php new file mode 100644 index 000000000..5a2044a3d --- /dev/null +++ b/app/Lib/Saito/Test/DicSetup.php @@ -0,0 +1,19 @@ +set('CU', $User); + return $dic; + } + + } \ No newline at end of file diff --git a/app/Lib/Saito/Test/SecurityMockTrait.php b/app/Lib/Saito/Test/SecurityMockTrait.php new file mode 100644 index 000000000..404297e7b --- /dev/null +++ b/app/Lib/Saito/Test/SecurityMockTrait.php @@ -0,0 +1,34 @@ +assertSecurityByPass($Mock); + } + return $Mock; + } + + /** + * Assume that SecurityComponent was called + * + * @param $Controller + */ + public function assertSecurityBypass($Controller) { + $Controller->Security->expects($this->any()) + ->method('_validatePost') + ->will($this->returnValue(true)); + $Controller->Security->expects($this->any()) + ->method('_validateCsrf') + ->will($this->returnValue(true)); + } + + } diff --git a/app/Lib/Thread/Renderer/HtmlRendererAbstract.php b/app/Lib/Saito/Thread/Renderer/HtmlRendererAbstract.php similarity index 70% rename from app/Lib/Thread/Renderer/HtmlRendererAbstract.php rename to app/Lib/Saito/Thread/Renderer/HtmlRendererAbstract.php index b19d13af9..8360a60db 100644 --- a/app/Lib/Thread/Renderer/HtmlRendererAbstract.php +++ b/app/Lib/Saito/Thread/Renderer/HtmlRendererAbstract.php @@ -1,6 +1,6 @@ _EntryHelper = $EntryHelper; + $this->_SEM = \SaitoEventManager::getInstance(); $this->setOptions($options); } - public function render(PostingInterface $node) { - $this->_lastAnswer = $node->Thread->getLastAnswer(); + public function render(\Saito\Posting\PostingInterface $node) { + $this->_lastAnswer = $node->getThread()->getLastAnswer(); $html = $this->_renderNode($node); - if ($node->getLevel() === 0) { - $html = $this->_wrapUl($html, 0, $node->id); + if ($node->isRoot()) { + $html = $this->_wrapUl($html, 0, $node->get('id')); } return $html; } @@ -37,7 +41,7 @@ public function setOptions($options) { $this->_settings = $options + $this->_defaults; } - protected function _renderNode(PostingInterface $node) { + protected function _renderNode(\Saito\Posting\PostingInterface $node) { $html = $this->_renderCore($node); $children = $node->getChildren(); @@ -54,7 +58,7 @@ protected function _renderNode(PostingInterface $node) { return $html; } - protected abstract function _renderCore(PostingInterface $node); + protected abstract function _renderCore(\Saito\Posting\PostingInterface $node); /** * Wraps li tags with ul tag @@ -85,9 +89,9 @@ protected function _wrapUl($string, $level = null, $id = null) { * @return string */ protected function _css($node) { - $entryType = ($node->getLevel() === 0) ? 'et-root' : 'et-reply'; + $entryType = ($node->isRoot()) ? 'et-root' : 'et-reply'; $entryType .= ($node->isNew()) ? ' et-new' : ' et-old'; - if ($node->id === (int)$this->_settings['currentEntry']) { + if ($node->get('id') === (int)$this->_settings['currentEntry']) { $entryType .= ' et-current'; } $css = $entryType; diff --git a/app/Lib/Thread/Renderer/MixHtmlRenderer.php b/app/Lib/Saito/Thread/Renderer/MixHtmlRenderer.php similarity index 60% rename from app/Lib/Thread/Renderer/MixHtmlRenderer.php rename to app/Lib/Saito/Thread/Renderer/MixHtmlRenderer.php index 655eacabe..f715beb92 100644 --- a/app/Lib/Thread/Renderer/MixHtmlRenderer.php +++ b/app/Lib/Saito/Thread/Renderer/MixHtmlRenderer.php @@ -1,19 +1,19 @@ _css($node); $html = $this->_EntryHelper->_View->element('/entry/view_posting', - ['entry' => $node->getRaw(), 'level' => $node->getLevel()]); + ['entry' => $node, 'level' => $node->getLevel()]); $html = << +
  • {$html}
  • EOF; diff --git a/app/Lib/Thread/Renderer/ThreadHtmlRenderer.php b/app/Lib/Saito/Thread/Renderer/ThreadHtmlRenderer.php similarity index 84% rename from app/Lib/Thread/Renderer/ThreadHtmlRenderer.php rename to app/Lib/Saito/Thread/Renderer/ThreadHtmlRenderer.php index 3da11197b..23a4a328b 100644 --- a/app/Lib/Thread/Renderer/ThreadHtmlRenderer.php +++ b/app/Lib/Saito/Thread/Renderer/ThreadHtmlRenderer.php @@ -1,6 +1,6 @@ getRaw(); $level = $node->getLevel(); - $id = (int)$posting['Entry']['id']; + $id = $posting['Entry']['id']; - $threadLine = $this->_renderThreadLine($posting, $level); + $threadLine = $this->_renderThreadLine($node, $posting, $level); $css = $this->_css($node); - $badges = $this->getBadges($posting); + $badges = $this->getBadges($node); $requestedParams = $this->_SEM->dispatch( 'Request.Saito.View.ThreadLine.beforeRender', @@ -52,7 +52,7 @@ protected function _renderCore(PostingInterface $node) { } //= manual json_encode() for performance - $tid = (int)$posting['Entry']['tid']; + $tid = $posting['Entry']['tid']; $isNew = $node->isNew() ? 'true' : 'false'; $jsData = << comes from _renderThreadLine and allows appending to threadLine-post $out = << -
    +
    @@ -76,15 +76,15 @@ class="link_show_thread et threadLine-content"> return $out; } - protected function _renderThreadLine(array $posting, $level) { - $id = (int)$posting['Entry']['id']; + protected function _renderThreadLine($node, array $posting, $level) { + $id = $posting['Entry']['id']; $useLineCache = $level > 0 && $this->_LineCache; if ($useLineCache && $threadLine = $this->_LineCache->get($id)) { return $threadLine; } - $subject = $this->getSubject($posting); + $subject = $this->getSubject($node); $username = h($posting['User']['username']); $time = $this->_EntryHelper->TimeH->formatTime($posting['Entry']['time']); diff --git a/app/Lib/Thread/Thread.php b/app/Lib/Saito/Thread/Thread.php similarity index 71% rename from app/Lib/Thread/Thread.php rename to app/Lib/Saito/Thread/Thread.php index 1dd52cf33..3c3e59514 100644 --- a/app/Lib/Thread/Thread.php +++ b/app/Lib/Saito/Thread/Thread.php @@ -1,5 +1,7 @@ id; + public function add(\Saito\Posting\PostingInterface $posting) { + $id = $posting->get('id'); $this->_Postings[$id] = $posting; if ($this->_rootId === null) { @@ -28,7 +30,7 @@ public function get($id) { } public function getLastAnswer() { - return strtotime($this->get('root')->last_answer); + return strtotime($this->get('root')->get('last_answer')); } } diff --git a/app/Lib/SaitoUser/CategoryAuth.php b/app/Lib/Saito/User/Auth/CategoryAuthorization.php similarity index 81% rename from app/Lib/SaitoUser/CategoryAuth.php rename to app/Lib/Saito/User/Auth/CategoryAuthorization.php index 2d951ebec..0fa9fb306 100644 --- a/app/Lib/SaitoUser/CategoryAuth.php +++ b/app/Lib/Saito/User/Auth/CategoryAuthorization.php @@ -1,12 +1,16 @@ _User = $User; } @@ -23,7 +27,7 @@ public function getAllowed($format = 'short') { return $this->_cache[$format]; } - $Category = ClassRegistry::init('Category'); + $Category = \ClassRegistry::init('Category'); $acs = $this->_User->getMaxAccession(); $categories = $Category->getCategoriesForAccession($acs); diff --git a/app/Lib/SaitoUser/Blocking/UserBlockerAbstract.php b/app/Lib/Saito/User/Blocker/BlockerAbstract.php similarity index 83% rename from app/Lib/SaitoUser/Blocking/UserBlockerAbstract.php rename to app/Lib/Saito/User/Blocker/BlockerAbstract.php index 9a2b9da18..9280c0ba2 100644 --- a/app/Lib/SaitoUser/Blocking/UserBlockerAbstract.php +++ b/app/Lib/Saito/User/Blocker/BlockerAbstract.php @@ -1,6 +1,8 @@ _defaults; $user = $this->_Model->User->getProfile($userId); if (empty($user)) { - throw new InvalidArgumentException; + throw new \InvalidArgumentException; } $conditions = [ 'ended' => null, @@ -51,7 +51,7 @@ public function block($userId, array $options = []) { } $success = $this->_Model->save($conditions); if (empty($success)) { - throw new Exception; + throw new \Exception; } } else { $this->_Model->unblock($existing[$this->_Model->alias]['id']); diff --git a/app/Lib/UserBookmarks.php b/app/Lib/Saito/User/Bookmarks.php similarity index 85% rename from app/Lib/UserBookmarks.php rename to app/Lib/Saito/User/Bookmarks.php index dc93553b3..6b8248240 100644 --- a/app/Lib/UserBookmarks.php +++ b/app/Lib/Saito/User/Bookmarks.php @@ -1,9 +1,11 @@ id, …] @@ -12,7 +14,7 @@ class UserBookmarks { protected $_CurrentUser; - public function __construct(CurrentUserComponent $CurrentUser) { + public function __construct(\CurrentUserComponent $CurrentUser) { $this->_CurrentUser = $CurrentUser; } diff --git a/app/Lib/UserCategories.php b/app/Lib/Saito/User/Categories.php similarity index 97% rename from app/Lib/UserCategories.php rename to app/Lib/Saito/User/Categories.php index 15fdb7264..5f4db1b8b 100644 --- a/app/Lib/UserCategories.php +++ b/app/Lib/Saito/User/Categories.php @@ -1,6 +1,8 @@ _Cookie = $Cookie; $this->_key = $key; $this->_setup(); diff --git a/app/Lib/SaitoUser/ForumsUserInterface.php b/app/Lib/Saito/User/ForumsUserInterface.php similarity index 95% rename from app/Lib/SaitoUser/ForumsUserInterface.php rename to app/Lib/Saito/User/ForumsUserInterface.php index f1a4f8158..e12da7e3f 100644 --- a/app/Lib/SaitoUser/ForumsUserInterface.php +++ b/app/Lib/Saito/User/ForumsUserInterface.php @@ -1,5 +1,7 @@ _CurrentUser = $CurrentUser; } diff --git a/app/Lib/SaitoUser/LastRefresh/LastRefreshCookie.php b/app/Lib/Saito/User/LastRefresh/LastRefreshCookie.php similarity index 73% rename from app/Lib/SaitoUser/LastRefresh/LastRefreshCookie.php rename to app/Lib/Saito/User/LastRefresh/LastRefreshCookie.php index 32bf5bb17..5aa701e0a 100644 --- a/app/Lib/SaitoUser/LastRefresh/LastRefreshCookie.php +++ b/app/Lib/Saito/User/LastRefresh/LastRefreshCookie.php @@ -1,7 +1,8 @@ _CurrentUser = $CurrentUser; - $this->_Cookie = new SaitoUserCookieStorage( + $this->_Cookie = new Cookie\Storage( $this->_CurrentUser->Cookie, 'lastRefresh' ); diff --git a/app/Lib/SaitoUser/LastRefresh/LastRefreshDatabase.php b/app/Lib/Saito/User/LastRefresh/LastRefreshDatabase.php similarity index 90% rename from app/Lib/SaitoUser/LastRefresh/LastRefreshDatabase.php rename to app/Lib/Saito/User/LastRefresh/LastRefreshDatabase.php index 37044f195..ba343bfb1 100644 --- a/app/Lib/SaitoUser/LastRefresh/LastRefreshDatabase.php +++ b/app/Lib/Saito/User/LastRefresh/LastRefreshDatabase.php @@ -1,6 +1,6 @@ _timestamp === null) { @@ -19,7 +19,7 @@ protected function _get() { // on ArrayIterator … Yeah for PHP!1!! $settings = $this->_CurrentUser->getSettings(); if (!array_key_exists('last_refresh', $settings)) { - throw new Exception('last_refresh not set'); + throw new \Exception('last_refresh not set'); } elseif ($settings['last_refresh'] === null) { // mar is not initialized $this->_timestamp = false; diff --git a/app/Lib/SaitoUser/LastRefresh/LastRefreshDummy.php b/app/Lib/Saito/User/LastRefresh/LastRefreshDummy.php similarity index 84% rename from app/Lib/SaitoUser/LastRefresh/LastRefreshDummy.php rename to app/Lib/Saito/User/LastRefresh/LastRefreshDummy.php index 2f0c2737d..29377ec76 100644 --- a/app/Lib/SaitoUser/LastRefresh/LastRefreshDummy.php +++ b/app/Lib/Saito/User/LastRefresh/LastRefreshDummy.php @@ -1,6 +1,6 @@ _CurrentUser = $CurrentUser; $this->_LastRefresh = $this->_CurrentUser->LastRefresh; } @@ -65,7 +67,7 @@ abstract public function delete(); * * @param $postings * @return array - * @throws InvalidArgumentException + * @throws \InvalidArgumentException */ protected function _preparePostings($postings) { // wrap single posting @@ -74,7 +76,7 @@ protected function _preparePostings($postings) { } if (empty($postings)) { - throw new InvalidArgumentException; + throw new \InvalidArgumentException; } // performance: don't store entries covered by timestamp @@ -85,7 +87,7 @@ protected function _preparePostings($postings) { } if (!empty($postings)) { - $postings = Hash::extract($postings, '{n}.' . $this->_modelAlias . '.id'); + $postings = \Hash::extract($postings, '{n}.' . $this->_modelAlias . '.id'); } return $postings; diff --git a/app/Lib/SaitoUser/ReadPostings/ReadPostingsCookie.php b/app/Lib/Saito/User/ReadPostings/ReadPostingsCookie.php similarity index 88% rename from app/Lib/SaitoUser/ReadPostings/ReadPostingsCookie.php rename to app/Lib/Saito/User/ReadPostings/ReadPostingsCookie.php index 0f5ebb152..821ca8396 100644 --- a/app/Lib/SaitoUser/ReadPostings/ReadPostingsCookie.php +++ b/app/Lib/Saito/User/ReadPostings/ReadPostingsCookie.php @@ -1,7 +1,6 @@ _Cookie = new SaitoUserCookieStorage( + $this->_Cookie = new \Saito\User\Cookie\Storage( $this->_CurrentUser->Cookie, 'Read' ); diff --git a/app/Lib/SaitoUser/ReadPostings/ReadPostingsDatabase.php b/app/Lib/Saito/User/ReadPostings/ReadPostingsDatabase.php similarity index 85% rename from app/Lib/SaitoUser/ReadPostings/ReadPostingsDatabase.php rename to app/Lib/Saito/User/ReadPostings/ReadPostingsDatabase.php index e47312612..59b84cffd 100644 --- a/app/Lib/SaitoUser/ReadPostings/ReadPostingsDatabase.php +++ b/app/Lib/Saito/User/ReadPostings/ReadPostingsDatabase.php @@ -1,6 +1,6 @@ _UserRead = $this->_CurrentUser->getModel()->UserRead; $this->_registerGc($this->_CurrentUser->Cron); @@ -21,7 +21,7 @@ public function __construct(CurrentUserComponent $CurrentUser) { * @throws InvalidArgumentException */ public function set($entries) { - Stopwatch::start('ReadPostingsDatabase::set()'); + \Stopwatch::start('ReadPostingsDatabase::set()'); if (!$this->_CurrentUser->isLoggedIn()) { return; } @@ -32,7 +32,7 @@ public function set($entries) { } $this->_UserRead->setEntriesForUser($entries, $this->_id()); - Stopwatch::stop('ReadPostingsDatabase::set()'); + \Stopwatch::stop('ReadPostingsDatabase::set()'); } public function delete() { @@ -43,23 +43,23 @@ public function delete() { * calculates user quota of allowed entries in DB * * @return int - * @throws UnexpectedValueException + * @throws \UnexpectedValueException */ protected function _minNPostingsToKeep() { if ($this->_minPostingsToKeep) { return $this->_minPostingsToKeep; } - $threadsOnPage = Configure::read('Saito.Settings.topics_per_page'); - $postingsPerThread = Configure::read('Saito.Globals.postingsPerThread'); + $threadsOnPage = \Configure::read('Saito.Settings.topics_per_page'); + $postingsPerThread = \Configure::read('Saito.Globals.postingsPerThread'); $pagesToCache = 1.5; $this->_minPostingsToKeep = intval($postingsPerThread * $threadsOnPage * $pagesToCache); if (empty($this->_minPostingsToKeep)) { - throw new UnexpectedValueException(); + throw new \UnexpectedValueException(); } return $this->_minPostingsToKeep; } - protected function _registerGc(CronComponent $Cron) { + protected function _registerGc(\CronComponent $Cron) { $Cron->addCronJob('ReadUser.' . $this->_id(), 'hourly', [$this, 'gcUser']); $Cron->addCronJob('ReadUser.global', 'hourly', [$this, 'gcGlobal']); } @@ -79,7 +79,8 @@ public function gcGlobal() { if (!$lastEntry) { return; } - $Category = ClassRegistry::init('Category'); + // @bogus why not getModel()->Entry->Category + $Category = \ClassRegistry::init('Category'); $nCategories = $Category->find('count'); $entriesToKeep = $nCategories * $this->_minNPostingsToKeep(); $lastEntryId = $lastEntry['Entry']['id'] - $entriesToKeep; diff --git a/app/Lib/SaitoUser/ReadPostings/ReadPostingsDummy.php b/app/Lib/Saito/User/ReadPostings/ReadPostingsDummy.php similarity index 86% rename from app/Lib/SaitoUser/ReadPostings/ReadPostingsDummy.php rename to app/Lib/Saito/User/ReadPostings/ReadPostingsDummy.php index f8ff62df5..11523f372 100644 --- a/app/Lib/SaitoUser/ReadPostings/ReadPostingsDummy.php +++ b/app/Lib/Saito/User/ReadPostings/ReadPostingsDummy.php @@ -1,6 +1,6 @@ setSettings($settings); + } + } + + } diff --git a/app/Lib/SaitoUser/SaitoUserTrait.php b/app/Lib/Saito/User/SaitoUserTrait.php similarity index 99% rename from app/Lib/SaitoUser/SaitoUserTrait.php rename to app/Lib/Saito/User/SaitoUserTrait.php index e109b21e9..f1c1194eb 100644 --- a/app/Lib/SaitoUser/SaitoUserTrait.php +++ b/app/Lib/Saito/User/SaitoUserTrait.php @@ -1,5 +1,7 @@ _User = $User; } diff --git a/app/Lib/SaitoExceptions.php b/app/Lib/SaitoExceptions.php deleted file mode 100644 index 1322d8a51..000000000 --- a/app/Lib/SaitoExceptions.php +++ /dev/null @@ -1,32 +0,0 @@ -__Logger = new \Saito\Logger\ForbiddenLogger(); - $this->__Logger->write($message, $data); - parent::__construct($message, 403); - } - - } - - class BlackHoledException extends \BadRequestException { - - public function __construct($type = null, $data = []) { - $message = 'Request was blackholed. Type: ' . $type; - $this->__Logger = new \Saito\Logger\ExceptionLogger(); - $this->__Logger->write($message, $data); - parent::__construct($message, 400); - } - - } - diff --git a/app/Lib/SaitoUser/SaitoUser.php b/app/Lib/SaitoUser/SaitoUser.php deleted file mode 100755 index 587fa5235..000000000 --- a/app/Lib/SaitoUser/SaitoUser.php +++ /dev/null @@ -1,16 +0,0 @@ -setSettings($settings); - } - } - - } diff --git a/app/Lib/SchemaCakeMysqlFixTrait.php b/app/Lib/SchemaCakeMysqlFixTrait.php deleted file mode 100644 index fa62c1ce8..000000000 --- a/app/Lib/SchemaCakeMysqlFixTrait.php +++ /dev/null @@ -1,22 +0,0 @@ -getDataSource(); - if ($DS instanceof Mysql) { - $table = $Ecach->tablePrefix . $Ecach->table; - $DS->fetchAll('ALTER TABLE `' . $table . '` CHANGE `value` `value` MEDIUMBLOB NOT NULL;'); - } - } - - } \ No newline at end of file diff --git a/app/Lib/Thread/Posting.php b/app/Lib/Thread/Posting.php deleted file mode 100644 index 0a97bb3a6..000000000 --- a/app/Lib/Thread/Posting.php +++ /dev/null @@ -1,88 +0,0 @@ - 0]; - $this->_rawData = $rawData; - $this->_level = $options['level']; - - if (!$tree) { - $tree = new Thread; - } - $this->Thread = $tree; - $this->Thread->add($this); - - $this->_attachChildren(); - return $this; - } - - /** - * magic get accessor - * - * @param $var - * @return int - * @throws InvalidArgumentException - */ - public function __get($var) { - switch ($var) { - case 'id': - return (int)$this->_rawData['Entry']['id']; - case 'pid': - return (int)$this->_rawData['Entry']['pid']; - case (isset($this->_rawData['Entry'][$var])): - return $this->_rawData['Entry'][$var]; - default: - throw new InvalidArgumentException("Attribute '$var' not found in class Posting."); - } - } - - public function getChildren() { - return $this->_children; - } - - public function getLevel() { - return $this->_level; - } - - public function getRaw() { - return $this->_rawData; - } - - public function isRoot() { - return $this->pid === 0; - } - - public function addDecorator($fct) { - foreach ($this->_children as $key => $child) { - $newChild = $fct($child); - $newChild->addDecorator($fct); - $this->_children[$key] = $newChild; - } - $new = $fct($this); - $this->Thread->add($new); - return $new; - } - - protected function _attachChildren() { - if (isset($this->_rawData['_children'])) { - foreach ($this->_rawData['_children'] as $child) { - $this->_children[] = new Posting($child, ['level' => $this->_level + 1], $this->Thread); - } - } - unset($this->_rawData['_children']); - } - - } - diff --git a/app/Lib/Thread/PostingCurrentUserDecorator.php b/app/Lib/Thread/PostingCurrentUserDecorator.php deleted file mode 100644 index 04c7ea877..000000000 --- a/app/Lib/Thread/PostingCurrentUserDecorator.php +++ /dev/null @@ -1,22 +0,0 @@ -_CU = $CU; - } - - public function isIgnored() { - return $this->_CU->ignores($this->getRaw()['Entry']['user_id']); - } - - public function isNew() { - $data = $this->getRaw(); - return !$this->_CU->ReadEntries->isRead($data['Entry']['id'], $data['Entry']['time']); - } - - } diff --git a/app/Lib/Thread/PostingDecorator.php b/app/Lib/Thread/PostingDecorator.php deleted file mode 100644 index 610cc77d7..000000000 --- a/app/Lib/Thread/PostingDecorator.php +++ /dev/null @@ -1,36 +0,0 @@ -_Posting = $Posting; - return $this; - } - - public function __get($var) { - return $this->_Posting->{$var}; - } - - public function getChildren() { - return $this->_Posting->getChildren(); - } - - public function getLevel() { - return $this->_Posting->getLevel(); - } - - public function getRaw() { - return $this->_Posting->getRaw(); - } - - public function isRoot() { - return $this->_Posting->isRoot(); - } - - public function addDecorator($fct) { - return $this->_Posting->addDecorator($fct); - } - - } \ No newline at end of file diff --git a/app/Locale/deu/LC_MESSAGES/default.po b/app/Locale/deu/LC_MESSAGES/default.po index 611fd4891..2a56409c9 100644 --- a/app/Locale/deu/LC_MESSAGES/default.po +++ b/app/Locale/deu/LC_MESSAGES/default.po @@ -1724,6 +1724,9 @@ msgstr "Nichts erkannt." msgid "ncy.bkm" msgstr "Noch keine Lesezeichen angelegt." +msgid "ncy.aub" +msgstr "Noch keine Benutzer gesperrt." + #: webroot/js/templates/upload.html:6 msgid "upload_btn_insert_into_posting" msgstr "Einfügen" diff --git a/app/Locale/eng/LC_MESSAGES/default.po b/app/Locale/eng/LC_MESSAGES/default.po index 6214227d1..66f8884ac 100644 --- a/app/Locale/eng/LC_MESSAGES/default.po +++ b/app/Locale/eng/LC_MESSAGES/default.po @@ -1680,6 +1680,9 @@ msgstr "" msgid "ncy.bkm" msgstr "No bookmarks created yet." +msgid "ncy.aub" +msgstr "No users blocked yet." + #: webroot/js/templates/upload.html:6 msgid "upload_btn_insert_into_posting" msgstr "Insert" diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 43791c313..4d0d16431 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -2,7 +2,6 @@ App::uses('Model', 'Model'); App::uses('Sanitize', 'Utility'); - App::uses('SaitoUser', 'Lib/SaitoUser'); App::uses('CakeEvent', 'Event'); App::uses('SaitoEventManager', 'Lib/Saito/Event'); @@ -35,8 +34,13 @@ public function __construct($id = false, $table = null, $ds = null) { } public function __get($name) { - if (isset($this->SharedObjects[$name])) { - return $this->SharedObjects[$name]; + switch ($name) { + case 'dic': + return ClassRegistry::getObject('dic'); + default: + if (isset($this->SharedObjects[$name])) { + return $this->SharedObjects[$name]; + } } return parent::__get($name); } @@ -69,21 +73,23 @@ public function filterFields(&$data, $fields) { } public function requireFields(&$data, array $required) { - return $this->_mapFields($data, $required, function(&$data, $model, $field) { - if (!isset($data[$model][$field])) { - return false; - } - return true; - }); + return $this->_mapFields($data, $required, + function (&$data, $model, $field) { + if (!isset($data[$model][$field])) { + return false; + } + return true; + }); } public function unsetFields(&$data, array $unset = ['id']) { - return $this->_mapFields($data, $unset, function(&$data, $model, $field) { - if (isset($data[$model][$field])) { - unset($data[$model][$field]); - } - return true; - }); + return $this->_mapFields($data, $unset, + function (&$data, $model, $field) { + if (isset($data[$model][$field])) { + unset($data[$model][$field]); + } + return true; + }); } protected function _mapFields(&$data, $fields, callable $func) { @@ -134,7 +140,7 @@ public function increment($id, $field, $amount = 1) { } $field = $this->alias . '.' . $field; $this->updateAll([$field => "$field $operator $amount"], - [$this->alias . '.id' => $id]); + [$this->alias . '.id' => $id]); } public function pipeMerger(array $data) { @@ -145,12 +151,12 @@ public function pipeMerger(array $data) { return implode(' | ', $out); } -/** - * Splits String 'a=b|c=d|e=f' into an array('a'=>'b', 'c'=>'d', 'e'=>'f') - * - * @param string $pipeString - * @return array - */ + /** + * Splits String 'a=b|c=d|e=f' into an array('a'=>'b', 'c'=>'d', 'e'=>'f') + * + * @param string $pipeString + * @return array + */ protected function _pipeSplitter($pipeString) { $unpipedArray = array(); $ranks = explode('|', $pipeString); @@ -166,25 +172,25 @@ protected function _pipeSplitter($pipeString) { protected static function _getIp() { $ip = null; - if ( Configure::read('Saito.Settings.store_ip') ): + if (Configure::read('Saito.Settings.store_ip')): $ip = env('REMOTE_ADDR'); - if ( Configure::read('Saito.Settings.store_ip_anonymized' ) ): + if (Configure::read('Saito.Settings.store_ip_anonymized')): $ip = self::_anonymizeIp($ip); endif; endif; 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 - */ + /** + * 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)); // propagate event on Saito's event bus @@ -198,12 +204,12 @@ public function dispatchSaitoEvent($event, $data) { $this->_SEM->dispatch($event, $data + ['Model' => $this]); } -/** - * Rough and tough ip anonymizer - * - * @param string $ip - * @return string - */ + /** + * Rough and tough ip anonymizer + * + * @param string $ip + * @return string + */ protected static function _anonymizeIp($ip) { $strlen = strlen($ip); if ($strlen > 6) : @@ -274,7 +280,8 @@ public function inRange($check, $lower = null, $upper = null) { */ public function logSql() { if (Configure::read('debug') < 2) { - trigger_error('You must set debug level to at least 2 to enable SQL-logging', E_USER_NOTICE); + trigger_error('You must set debug level to at least 2 to enable SQL-logging', + E_USER_NOTICE); } $dbo = $this->getDatasource(); $logs = $dbo->getLog(); diff --git a/app/Model/Behavior/MarkupBehavior.php b/app/Model/Behavior/MarkupBehavior.php index f5877748c..cfee8632c 100644 --- a/app/Model/Behavior/MarkupBehavior.php +++ b/app/Model/Behavior/MarkupBehavior.php @@ -1,7 +1,6 @@ _Preprocessor === null) { $settings = Configure::read('Saito.Settings.Parser'); - $this->_Preprocessor = SaitoPlugin::getParserClassInstance( + $this->_Preprocessor = \Saito\Plugin::getParserClassInstance( 'Preprocessor', $settings ); diff --git a/app/Model/Ecach.php b/app/Model/Ecach.php deleted file mode 100644 index 3ad9febe4..000000000 --- a/app/Model/Ecach.php +++ /dev/null @@ -1,18 +0,0 @@ - true // performance improvements if it's a known thread-root - * 'complete' => true // include all fields necessary to render the complete entries * ); * * @param int $id @@ -505,24 +506,25 @@ public function treeForNode($id, $options = array()) { $fields = array_merge($this->threadLineFieldList, $this->showEntryFieldListAdditional); } - $tree = $this->treesForThreads([['id' => $tid]], null, $fields); + $tree = $this->treesForThreads([$tid], null, $fields); if ((int)$tid !== (int)$id) { $tree = $this->treeGetSubtree($tree, $id); } - if ($options['complete'] && $tree) { - $this->_addAdditionalFields($tree); - } - return $tree; } -/** - * trees for multiple tids - */ - public function treesForThreads($searchArray, $order = null, $fieldlist = null) { - if (empty($searchArray)) { + /** + * trees for multiple tids + * + * @param $ids + * @param null $order + * @param null $fieldlist + * @return array|bool false if no threads or array with Posting + */ + public function treesForThreads($ids, $order = null, $fieldlist = null) { + if (empty($ids)) { return []; } @@ -532,32 +534,31 @@ public function treesForThreads($searchArray, $order = null, $fieldlist = null) $order = 'last_answer ASC'; } - $where = []; - foreach ($searchArray as $_searchItem) { - $where[] = $_searchItem['id']; - } - if ($fieldlist === null) { $fieldlist = $this->threadLineFieldList; } - $threads = $this->_getThreadEntries( - $where, - [ - 'order' => $order, - 'fields' => $fieldlist - ] + $entries = $this->_getThreadEntries( + $ids, + ['order' => $order, 'fields' => $fieldlist] ); - Stopwatch::stop('Model->Entries->treeForThreads() DB'); - $out = false; - if ($threads) { - Stopwatch::start('Model->Entries->treeForThreads() CPU'); - $out = $this->treeBuild($threads); - Stopwatch::stop('Model->Entries->treeForThreads() CPU'); + Stopwatch::stop('Model->Entries->treeForThreads() DB'); + Stopwatch::start('Model->Entries->treeForThreads() CPU'); + + $threads = false; + if ($entries) { + $threads = []; + $entries = $this->treeBuild($entries); + foreach ($entries as $thread) { + $id = (int)$thread['Entry']['tid']; + $threads[$id] = $thread; + } } - return $out; + Stopwatch::stop('Model->Entries->treeForThreads() CPU'); + + return $threads; } /** @@ -816,6 +817,7 @@ public function threadMerge($targetId) { } // set target entry as new parent entry + $this->data = []; $this->set('pid', $targetEntry[$this->alias]['id']); if ($this->save()) { // associate all entries in source thread to target thread @@ -838,6 +840,13 @@ public function threadMerge($targetId) { $this->save(); } + // propagate pinned property from target to source + $isTargetPinned = (bool)$targetEntry[$this->alias]['locked']; + if ($sourceEntry[$this->alias]['locked'] !== $isTargetPinned) { + $this->id = $targetEntry[$this->alias]['tid']; + $this->_threadLock($isTargetPinned); + } + $this->Esevent->transferSubjectForEventType( $threadIdSource, $targetEntry[$this->alias]['tid'], @@ -852,110 +861,6 @@ public function threadMerge($targetId) { return false; } - protected function _addAdditionalFields(&$entries) { - /* - * Function for checking if entry is bookmarked by current user - * - * @param $entries - */ - $ldGetBookmarkForEntryAndUser = function (&$tree, &$element, $_this) { - $element['isBookmarked'] = $this->CurrentUser - ->hasBookmarked($element['Entry']['id']); - }; - Entry::mapTreeElements($entries, $ldGetBookmarkForEntryAndUser, $this); - - /* - * Function for checking user rights on an entry - * - * @param $tree - * @param $element - * @param $_this - */ - $ldGetRightsForEntryAndUser = function (&$tree, &$element, $_this) { - $rights = [ - 'isEditingForbidden' => $_this->isEditingForbidden($element, $_this->CurrentUser), - 'isEditingAsUserForbidden' => $_this->isEditingForbidden($element, $_this->CurrentUser->mockUserType('user')), - 'isAnsweringForbidden' => $_this->isAnsweringForbidden($element) - ]; - $element['rights'] = $rights; - }; - Entry::mapTreeElements($entries, $ldGetRightsForEntryAndUser, $this); - } - - /** - * Checks if someone is allowed to edit an entry - * - * @param $entry - * @param ForumsUserInterface $User - * - * @return bool|string - * @throws Exception - */ - public function isEditingForbidden($entry, ForumsUserInterface $User = null) { - if ($User === null) { - $User = $this->CurrentUser; - } - - // Anon - if ($User->isLoggedIn() !== true) { - return true; - } - - // Admins - if ($User->isAdmin()) { - return false; - } - - $verboten = true; - - if (!isset($entry['Entry'])) { - $entry = $this->get($entry); - } - - if (empty($entry)) { - throw new Exception(sprintf('Entry %s not found.', $entry)); - } - - $expired = ($this->_setting('edit_period') * 60) + - strtotime($entry['Entry']['time']); - $isOverEditLimit = time() > $expired; - - $isUsersPosting = (int)$User->getId() === (int)$entry['Entry']['user_id']; - - if ($User->isMod()) { - // Mods - // @todo mods don't edit admin posts - 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, - * as long as no one checks pinning for Configure::read('Saito.Settings.edit_period') * 60 - * for mods pinning root-posts. - */ - ($entry['Entry']['fixed'] == false) - ) { - // mods don't mod themselves - $verboten = 'time'; - } else { - $verboten = false; - }; - - } else { - // Users - if ($isUsersPosting === false) { - $verboten = 'user'; - } elseif ($isOverEditLimit) { - $verboten = 'time'; - } elseif ($this->_isLocked($entry)) { - $verboten = 'locked'; - } else { - $verboten = false; - } - } - - return $verboten; - } - /** * Test if entry is thread-root * @@ -997,13 +902,6 @@ public function isRoot($id = null) { return $this->_isRoot[$md5]; } - protected function _isLocked($entry) { - if (!isset($entry[$this->alias]['locked'])) { - throw new InvalidArgumentException; - } - return $entry[$this->alias]['locked'] != false; - } - /** * Preprocesses entry data before saving it * @@ -1045,7 +943,9 @@ public function prepare(&$data, array $options = []) { throw new InvalidArgumentException; } - if ($this->isAnsweringForbidden($parent)) { + // @todo highly @bogus should be a validator? + $parentPosting = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $parent]); + if ($parentPosting->isAnsweringForbidden()) { throw new ForbiddenException; } @@ -1071,7 +971,6 @@ protected function _findEntry($state, $query, $results = []) { return $query; } if ($results) { - $this->_addAdditionalFields($results); return $results[0]; } return $results; @@ -1102,26 +1001,11 @@ protected function _findFeed($state, $query, $results = array()) { protected function _threadLock($value) { $tid = $this->field('tid'); $this->contain(); + // @bogus throws error on $value = false + $value = $value ? 1 : 0; $this->updateAll(['locked' => $value], ['tid' => $tid]); } -/** - * Checks if answering an entry is allowed - * - * @param array $entry - * @return boolean - */ - public function isAnsweringForbidden($entry) { - $isAnsweringForbidden = true; - if ($this->_isLocked($entry)) { - $isAnsweringForbidden = 'locked'; - } else { - $isAnsweringForbidden = false; - } - - return $isAnsweringForbidden; - } - public function paginateCount($conditions, $recursive, $extra) { if (isset($extra['getInitialThreads'])) { $this->Category->contain(); @@ -1180,8 +1064,21 @@ public function validateCategoryIsAllowed($check) { return true; } + /** + * @param $check + * @return bool + * @throws Exception + */ public function validateEditingAllowed($check) { - $forbidden = $this->isEditingForbidden($this->data['Entry']['id']); + $id = $this->data[$this->alias]['id']; + $entry = $this->get($id); + if (empty($entry)) { + throw new Exception(sprintf('Entry %s not found.', $entry)); + } + + $posting = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $entry]); + $forbidden = $posting->isEditingAsCurrentUserForbidden(); + if (is_bool($forbidden)) { return !$forbidden; } else { diff --git a/app/Model/User.php b/app/Model/User.php index af2908156..b88848208 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -75,7 +75,10 @@ class User extends AppModel { 'isUnique' => ['rule' => 'isUniqueCiString'], 'notEmpty' => ['rule' => 'notEmpty'], 'hasAllowedChars' => ['rule' => ['validateHasAllowedChars']], - 'isUsernameEqual' => ['rule' => 'validateUsernameEqual'] + 'isUsernameEqual' => [ + 'on' => 'create', + 'rule' => 'validateUsernameEqual' + ] ], 'user_type' => [ 'allowedChoice' => ['rule' => ['inList', ['user', 'admin', 'mod']]] diff --git a/app/Model/UserBlock.php b/app/Model/UserBlock.php index b86afdeab..ec6711d46 100644 --- a/app/Model/UserBlock.php +++ b/app/Model/UserBlock.php @@ -99,9 +99,7 @@ public function gc() { public function getAll() { $blocklist = $this->find('all', [ 'contain' => ['By', 'User'], - 'order' => [ - 'UserBlock.id' => 'DESC' - ] + 'order' => ['UserBlock.id' => 'DESC'] ]); $o = []; foreach ($blocklist as $k => $b) { @@ -112,6 +110,15 @@ public function getAll() { return $o; } + public function getAllActive() { + $blocklist = $this->find('all', [ + 'contain' => false, + 'conditions' => ['ended' => null], + 'order' => ['UserBlock.id' => 'DESC'] + ]); + return $blocklist; + } + protected function _updateIsBlocked($userId) { $blocks = $this->find('first', [ 'contain' => false, diff --git a/app/Plugin/Api/Controller/ApiEntriesController.php b/app/Plugin/Api/Controller/ApiEntriesController.php index c6957a87b..d0382e925 100644 --- a/app/Plugin/Api/Controller/ApiEntriesController.php +++ b/app/Plugin/Api/Controller/ApiEntriesController.php @@ -134,7 +134,9 @@ public function entriesItemPut($id = null) { throw new NotFoundException(sprintf('Entry with id `%s` not found.', $id)); } - $isEditingForbidden = $oldEntry['rights']['isEditingForbidden']; + $posting = $this->dic->newInstance('\Saito\Posting\Posting', ['rawData' => $oldEntry]); + + $isEditingForbidden = $posting->isEditingAsCurrentUserForbidden(); if ($isEditingForbidden === 'time') { throw new ForbiddenException('The editing time ran out.'); } elseif ($isEditingForbidden === 'user') { diff --git a/app/Plugin/Api/Lib/ApiControllerTestCase.php b/app/Plugin/Api/Lib/ApiControllerTestCase.php index 9c025425e..a652b9b13 100644 --- a/app/Plugin/Api/Lib/ApiControllerTestCase.php +++ b/app/Plugin/Api/Lib/ApiControllerTestCase.php @@ -1,8 +1,6 @@ generate( 'Api.ApiEntries', - [ - 'models' => ['Entry' => ['get']] - ] + ['models' => ['Entry' => ['get']]] ); + $entry = [ + 'Entry' => [ + 'id' => 1, + 'locked' => true, + 'time' => bDate(time() + 9999), + 'user_id' => 3 + ] + ]; + $ApiEntries->Entry->expects($this->once()) ->method('get') - ->will($this->returnValue(['rights' => ['isEditingForbidden' => true]])); + ->will($this->returnValue($entry)); $this->_loginUser(3); $this->setExpectedException('ForbiddenException', 'Editing is forbidden for unknown reason.'); $this->testAction( diff --git a/app/Plugin/Api/Test/Case/Controller/ApiShoutsControllerTest.php b/app/Plugin/Api/Test/Case/Controller/ApiShoutsControllerTest.php index 68f7d7ba9..90c889de8 100644 --- a/app/Plugin/Api/Test/Case/Controller/ApiShoutsControllerTest.php +++ b/app/Plugin/Api/Test/Case/Controller/ApiShoutsControllerTest.php @@ -49,11 +49,12 @@ class ApiShoutsControllerTest extends ApiControllerTestCase { 'plugin.api.entry', 'plugin.api.category', 'plugin.api.user', + 'plugin.api.user_block', + 'plugin.api.user_ignore', 'plugin.api.user_online', 'plugin.api.bookmark', 'plugin.api.esnotification', 'plugin.api.esevent', - 'plugin.api.ecach', 'plugin.api.setting', 'plugin.api.smiley', 'plugin.api.smiley_code', diff --git a/app/Plugin/Api/Test/Case/Controller/ApiUsersControllerTest.php b/app/Plugin/Api/Test/Case/Controller/ApiUsersControllerTest.php index e545af548..ce852933d 100644 --- a/app/Plugin/Api/Test/Case/Controller/ApiUsersControllerTest.php +++ b/app/Plugin/Api/Test/Case/Controller/ApiUsersControllerTest.php @@ -19,14 +19,15 @@ class ApiUsersControllerTest extends ApiControllerTestCase { 'plugin.api.entry', 'plugin.api.category', 'plugin.api.user', + 'plugin.api.user_block', + 'plugin.api.user_ignore', 'plugin.api.user_online', 'plugin.api.user_read', 'plugin.api.bookmark', 'plugin.api.esnotification', 'plugin.api.esevent', 'plugin.api.upload', - 'plugin.api.setting', - 'plugin.api.ecach' + 'plugin.api.setting' ); public function testLoginNoUsername() { diff --git a/app/Plugin/BbcodeParser/Lib/Helper/UrlParserTrait.php b/app/Plugin/BbcodeParser/Lib/Helper/UrlParserTrait.php index 3f9c20c6d..52d042bc9 100644 --- a/app/Plugin/BbcodeParser/Lib/Helper/UrlParserTrait.php +++ b/app/Plugin/BbcodeParser/Lib/Helper/UrlParserTrait.php @@ -2,7 +2,7 @@ namespace Plugin\BbcodeParser\Lib\Helper; - \App::uses('DomainParser', 'Lib'); + use Saito\DomainParser; trait UrlParserTrait { @@ -34,7 +34,7 @@ protected function _url($url, $text, $label = false, $truncate = false) { // add domain info: `[url=domain.info]my link[/url]` -> `my link [domain.info]` if ($label !== false && $label !== 'none' && $label !== 'false') { if (!empty($url) && preg_match('/\[' . $host . ']'; } diff --git a/app/Plugin/BbcodeParser/Lib/Parser.php b/app/Plugin/BbcodeParser/Lib/Parser.php index a03960333..6046a014e 100644 --- a/app/Plugin/BbcodeParser/Lib/Parser.php +++ b/app/Plugin/BbcodeParser/Lib/Parser.php @@ -187,10 +187,10 @@ public function parse($string, array $options = []) { $this->_Parser->parse($string); + $this->_Parser->accept(new Visitors\JbbCodeNl2BrVisitor($this->_Helper, $options)); if ($this->_cSettings['autolink']) { $this->_Parser->accept(new Visitors\JbbCodeAutolinkVisitor($this->_Helper, $options)); } - $this->_Parser->accept(new Visitors\JbbCodeNl2BrVisitor($this->_Helper, $options)); if ($this->_cSettings['smilies']) { $this->_Parser->accept(new Visitors\JbbCodeSmileyVisitor($this->_Helper, $options)); } diff --git a/app/Plugin/BbcodeParser/Lib/jBBCode/Definitions/JbbCodeDefinitions.php b/app/Plugin/BbcodeParser/Lib/jBBCode/Definitions/JbbCodeDefinitions.php index 2d1865271..1b8ecbf5f 100644 --- a/app/Plugin/BbcodeParser/Lib/jBBCode/Definitions/JbbCodeDefinitions.php +++ b/app/Plugin/BbcodeParser/Lib/jBBCode/Definitions/JbbCodeDefinitions.php @@ -4,6 +4,7 @@ use Plugin\BbcodeParser\Lib\Helper\Message; use Plugin\BbcodeParser\Lib\Helper\UrlParserTrait; + use Saito\DomainParser; include 'CodeDefinition.php'; include 'JbbHtml5MediaCodeDefinition.php'; @@ -66,8 +67,6 @@ protected function _parse($content, $attributes) { } - \App::uses('DomainParser', 'Lib'); - class Iframe extends CodeDefinition { protected $_sTagName = 'iframe'; @@ -142,7 +141,7 @@ protected function _checkHostAllowed($url) { return true; } - $host = \DomainParser::domain($url); + $host = DomainParser::domain($url); if ($host && isset($allowedDomains[$host])) { return true; } @@ -188,7 +187,7 @@ protected function _parse($content, $attributes) { } if (env('HTTPS')) { - $host = \DomainParser::domain($url); + $host = DomainParser::domain($url); if (isset(self::$_flashVideoDomainsWithHttps[$host])) { $url = str_ireplace('http://', 'https://', $url); } diff --git a/app/Plugin/BbcodeParser/Lib/jBBCode/Visitors/JbbCodeAutolinkVisitor.php b/app/Plugin/BbcodeParser/Lib/jBBCode/Visitors/JbbCodeAutolinkVisitor.php index 5424c32ec..2ef6f63cd 100644 --- a/app/Plugin/BbcodeParser/Lib/jBBCode/Visitors/JbbCodeAutolinkVisitor.php +++ b/app/Plugin/BbcodeParser/Lib/jBBCode/Visitors/JbbCodeAutolinkVisitor.php @@ -24,7 +24,12 @@ protected function _processTextNode($string, $node) { protected function _atUserLink($string) { $tags = array(); - if (!preg_match_all('/(\s|^)@([^\s\pP]+)/m', $string, $tags)) { + /* + * - '\pP' all unicode punctuation marks + * - '<' if nl2br has taken place whatchout for
    linebreaks + */ + $hasTags = preg_match_all('/(\s|^)@([^\s\pP<]+)/m', $string, $tags); + if (!$hasTags) { return $string; } diff --git a/app/Plugin/BbcodeParser/Test/Case/Lib/BbcodeParserTest.php b/app/Plugin/BbcodeParser/Test/Case/Lib/BbcodeParserTest.php index ba0b7e8b7..d57e02372 100755 --- a/app/Plugin/BbcodeParser/Test/Case/Lib/BbcodeParserTest.php +++ b/app/Plugin/BbcodeParser/Test/Case/Lib/BbcodeParserTest.php @@ -1,6 +1,7 @@ set(['Alice', 'Bobby Junior', 'Dr. No']); //= ParserHelper diff --git a/app/Plugin/BbcodeParser/docs/help/eng/1-bbcodes.md b/app/Plugin/BbcodeParser/docs/help/eng/1-bbcodes.md index a0769be4f..bee2d159e 100755 --- a/app/Plugin/BbcodeParser/docs/help/eng/1-bbcodes.md +++ b/app/Plugin/BbcodeParser/docs/help/eng/1-bbcodes.md @@ -58,11 +58,11 @@ Usually the top-level domain is appended to a `[url]` link. This can be controll ### Email Link ### - [email]mailto:mail@tosomeone.com[/email] + [email]mail@tosomeone.com[/email] or - [email=mailto:mail@tosomeone.com]Mail[/email] + [email=mail@tosomeone.com]Mail[/email] ### Internal Shortlinks ### diff --git a/app/Plugin/Install/Controller/InstallController.php b/app/Plugin/Install/Controller/InstallController.php index b55489d8a..488507ea6 100755 --- a/app/Plugin/Install/Controller/InstallController.php +++ b/app/Plugin/Install/Controller/InstallController.php @@ -202,8 +202,6 @@ public function data() { $this->redirect(array('action' => 'database')); } } - // @hack - $schema->after(['create' => 'ecaches']); $path = CakePlugin::path('Install') .DS. 'Config' .DS. 'Data' .DS; $dataObjects = App::objects('class', $path); diff --git a/app/Plugin/M/Lib/MCacheSupportCachelet.php b/app/Plugin/M/Lib/MCacheSupportCachelet.php index b1004524b..4116a1da7 100644 --- a/app/Plugin/M/Lib/MCacheSupportCachelet.php +++ b/app/Plugin/M/Lib/MCacheSupportCachelet.php @@ -1,6 +1,6 @@ ' . $title . ''; + $mailto = '' . $title . ''; $mailto .= ''; $mailto .= $this->Html->scriptBlock( "$(function(){ diff --git a/app/Plugin/Sitemap/Test/Case/Controller/SitemapsControllerTest.php b/app/Plugin/Sitemap/Test/Case/Controller/SitemapsControllerTest.php index 08063b43a..07a472152 100644 --- a/app/Plugin/Sitemap/Test/Case/Controller/SitemapsControllerTest.php +++ b/app/Plugin/Sitemap/Test/Case/Controller/SitemapsControllerTest.php @@ -16,7 +16,6 @@ class SitemapsControllerTest extends ControllerTestCase { 'setting', 'user', 'user_online', - 'ecach', 'bookmark', 'entry', 'category', diff --git a/app/Test/Case/Controller/AppControllerTest.php b/app/Test/Case/Controller/AppControllerTest.php index e9d8b6d1d..2001ead0a 100755 --- a/app/Test/Case/Controller/AppControllerTest.php +++ b/app/Test/Case/Controller/AppControllerTest.php @@ -2,14 +2,12 @@ App::uses('Controller', 'Controller'); App::uses('AppController', 'Controller'); - App::uses('SaitoControllerTestCase', 'Lib/Test'); - class AppControllerTest extends SaitoControllerTestCase { + class AppControllerTest extends \Saito\Test\ControllerTestCase { public $fixtures = [ 'app.bookmark', 'app.category', - 'app.ecach', 'app.entry', 'app.esevent', 'app.esnotification', diff --git a/app/Test/Case/Controller/BookmarksControllerTest.php b/app/Test/Case/Controller/BookmarksControllerTest.php index b1c1b4990..670229aac 100644 --- a/app/Test/Case/Controller/BookmarksControllerTest.php +++ b/app/Test/Case/Controller/BookmarksControllerTest.php @@ -1,11 +1,10 @@ generate('Bookmarks'); $this->_loginUser(1); - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $this->testAction('/bookmarks/edit/1'); } @@ -121,7 +119,7 @@ public function testDeleteNotUsersBookmark() { $this->generate('Bookmarks'); $this->_loginUser(1); - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $this->testAction('/bookmarks/delete/1', ['method' => 'POST']); } diff --git a/app/Test/Case/Controller/ContactsControllerTest.php b/app/Test/Case/Controller/ContactsControllerTest.php index 7a3ad0ff5..57cf14e63 100644 --- a/app/Test/Case/Controller/ContactsControllerTest.php +++ b/app/Test/Case/Controller/ContactsControllerTest.php @@ -4,9 +4,9 @@ App::uses('UsersController', 'Controller'); App::uses('SaitoControllerTestCase', 'Lib/Test'); - class ContactsControllerTestCase extends SaitoControllerTestCase { + class ContactsControllerTestCase extends \Saito\Test\ControllerTestCase { - use SaitoSecurityMockTrait; + use \Saito\Test\SecurityMockTrait; public $fixtures = [ 'app.category', diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 52fe34698..bc4d4ffe6 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -16,14 +16,13 @@ public function getInitialThreads($User, $order = ['Entry.last_answer' => 'DESC' } - class EntriesControllerTestCase extends SaitoControllerTestCase { + class EntriesControllerTestCase extends \Saito\Test\ControllerTestCase { - use SaitoSecurityMockTrait; + use \Saito\Test\SecurityMockTrait; public $fixtures = [ 'app.bookmark', 'app.category', - 'app.ecach', 'app.entry', 'app.esevent', 'app.esnotification', @@ -191,7 +190,7 @@ public function testCategoryChooserNotLoggedIn() { App::uses('ComponentCollection', 'Controller'); $User = new CurrentUserComponent(new ComponentCollection()); - $User->Categories = $this->getMock('CategoryAuth', ['getAllowed'], + $User->Categories = $this->getMock('Saito\User\Auth\CategoryAuthorization', ['getAllowed'], [$User]); $User->Categories->expects($this->any()) ->method('getAllowed') @@ -224,8 +223,11 @@ public function testCategoryChooserDeactivated() { App::uses('ComponentCollection', 'Controller'); $User = new CurrentUserComponent(new ComponentCollection()); - $User->Categories = $this->getMock('CategoryAuth', ['getAllowed'], - [$User]); + $User->Categories = $this->getMock( + 'Saito\User\Auth\CategoryAuthorization', + ['getAllowed'], + [$User] + ); $User->Categories->expects($this->any()) ->method('getAllowed') ->will($this->returnValue([ @@ -261,8 +263,11 @@ public function testCategoryChooserEmptyCustomSet() { App::uses('ComponentCollection', 'Controller'); $User = new CurrentUserComponent(new ComponentCollection()); - $User->Categories = $this->getMock('CategoryAuth', ['getAllowed'], - [$User]); + $User->Categories = $this->getMock( + 'Saito\User\Auth\CategoryAuthorization', + ['getAllowed'], + [$User] + ); $User->Categories->expects($this->any()) ->method('getAllowed') ->will($this->returnValue([ @@ -303,8 +308,11 @@ public function testCategoryChooserCustomSet() { App::uses('ComponentCollection', 'Controller'); $User = new CurrentUserComponent(new ComponentCollection()); - $User->Categories = $this->getMock('CategoryAuth', ['getAllowed'], - [$User]); + $User->Categories = $this->getMock( + 'Saito\User\Auth\CategoryAuthorization', + ['getAllowed'], + [$User] + ); $User->Categories->expects($this->any()) ->method('getAllowed') ->will($this->returnValue([ @@ -351,8 +359,11 @@ public function testCategoryChooserSingleCategory() { App::uses('ComponentCollection', 'Controller'); $User = new CurrentUserComponent(new ComponentCollection()); - $User->Categories = $this->getMock('CategoryAuth', ['getAllowed'], - [$User]); + $User->Categories = $this->getMock( + 'Saito\User\Auth\CategoryAuthorization', + ['getAllowed'], + [$User] + ); $User->Categories->expects($this->any()) ->method('getAllowed') ->will($this->returnValue([ @@ -569,7 +580,7 @@ public function testEditShowForm() { ) )); - $this->_loginUser(2); + $this->_loginUser(1); $Entries->Entry->expects($this->any()) ->method('isEditingForbidden') @@ -585,8 +596,8 @@ public function testEditShowForm() { // test that text is quoted $this->assertContains('Second_Text', $result); // notification are un/checked - $this->assertNoPattern('/data\[Event\]\[1\]\[event_type_id\]"\s+?checked="checked"/', $result); - $this->assertPattern('/data\[Event\]\[2\]\[event_type_id\]"\s+?checked="checked"/', $result); + $this->assertNotRegExp('/data\[Event\]\[1\]\[event_type_id\]"\s+?checked="checked"/', $result); + $this->assertRegExp('/data\[Event\]\[2\]\[event_type_id\]"\s+?checked="checked"/', $result); } /** @@ -608,7 +619,8 @@ public function testEditNoInternalErrorOnValidationError() { 'tid' => 1, 'pid' => 1, 'time' => time() - 1, - 'user_id' => 2 + 'user_id' => 2, + 'fixed' => false ], 'User' => [ 'username' => 'Mitch' @@ -622,7 +634,7 @@ public function testEditNoInternalErrorOnValidationError() { ->method('update') ->will($this->returnValue(false)); - $this->_loginUser(2); + $this->_loginUser(1); $this->testAction('entries/edit/2', [ 'data' => [ 'Entry' => [ @@ -820,7 +832,7 @@ public function testAppStats() { $headerCounter = $result['HeaderCounter']; $this->assertEquals($headerCounter['user_online'], 1); - $this->assertEquals($headerCounter['user'], 9); + $this->assertEquals($headerCounter['user'], 10); $this->assertEquals($headerCounter['entries'], 11); $this->assertEquals($headerCounter['threads'], 5); $this->assertEquals($headerCounter['user_registered'], 0); diff --git a/app/Test/Case/Controller/EsnotificationsControllerTest.php b/app/Test/Case/Controller/EsnotificationsControllerTest.php index 7ca0dfe0f..62d97e75e 100644 --- a/app/Test/Case/Controller/EsnotificationsControllerTest.php +++ b/app/Test/Case/Controller/EsnotificationsControllerTest.php @@ -1,13 +1,12 @@ setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('\Saito\Exception\SaitoForbiddenException'); $this->testAction('/users/edit/3'); } public function testEditNotUsersEntryGet() { $this->generate('Users'); $this->_loginUser(2); // mod - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $this->testAction('/users/edit/3', ['method' => 'GET']); } public function testEditNotUsersEntryPost() { $this->generate('Users'); $this->_loginUser(2); // mod - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $this->testAction('/users/edit/3', ['method' => 'POST']); } @@ -814,7 +812,7 @@ public function testDelete() { } public function testChangePasswordNotLoggedIn() { - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $this->testAction('/users/changepassword/5'); $this->assertRedirectedTo(); } @@ -823,7 +821,7 @@ public function testChangePasswordWrongUser() { $this->generate('Users'); $this->_loginUser(4); - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $data = [ 'User' => [ @@ -838,7 +836,7 @@ public function testChangePasswordWrongUser() { public function testChangePasswordViewFormWrongUser() { $this->generate('Users'); - $this->setExpectedException('Saito\ForbiddenException'); + $this->setExpectedException('Saito\Exception\SaitoForbiddenException'); $this->_loginUser(4); $this->testAction('/users/changepassword/5'); } diff --git a/app/Test/Case/Lib/Cache/CacheTreeTest.php b/app/Test/Case/Lib/Cache/CacheTreeTest.php deleted file mode 100644 index 0b4bf73ff..000000000 --- a/app/Test/Case/Lib/Cache/CacheTreeTest.php +++ /dev/null @@ -1,227 +0,0 @@ -_CurrentUser = new SaitoUser(); - $this->_Cache = new ItemCache('EntrySub', null, ['maxItems' => 240]); - } - - public function setCache($data) { - $this->_cachedEntries = $data; - } - - public function setItemCache($IC) { - $this->_Cache = $IC; - } - - public function setAllowRead($state) { - $this->_allowRead = $state; - } - - public function setAllowUpdate($state) { - $this->_allowUpdate = $state; - } - - public function __get($name) { - if ($name === 'CurrentUser') { - return $this->_CurrentUser; - } - } - - } - - /** - * CacheTreeComponent Test Case - * - */ - class CacheTreeTest extends CakeTestCase { - - protected $_fixture; - - protected $_time; - - /** - * setUp method - * - * @return void - */ - public function setUp() { - parent::setUp(); - $this->CacheTree = new CacheTreeMock(); - - $this->_time = time(); - $fixtureTime = $this->_time - 3600; - $fixtureContent = 'foo'; - $fixtureKey = 1; - - $this->CacheTree->setAllowUpdate(true); - $this->CacheTree->set($fixtureKey, $fixtureContent, $fixtureTime); - $this->CacheTree->setAllowUpdate(false); - } - - /** - * tearDown method - * - * @return void - */ - public function tearDown() { - unset($this->CacheTree); - - parent::tearDown(); - } - - /** - * testIsCacheUpdatable method - * - * @return void - */ - public function testIsCacheUpdatableDisabled() { - $this->CacheTree->setAllowUpdate(false); - - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->never()) - ->method('isNewerThan'); - - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 7200), - ); - $result = $this->CacheTree->isCacheUpdatable($in); - $this->assertFalse($result); - } - - public function testIsCacheUpdatable() { - $this->CacheTree->setAllowUpdate(true); - - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->once()) - ->method('isNewerThan') - ->will($this->returnValue(true)); - - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 7200), - ); - $result = $this->CacheTree->isCacheUpdatable($in); - $this->assertTrue($result); - } - - public function testIsCacheUpdatableNewToUser() { - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->once()) - ->method('isNewerThan') - ->will($this->returnValue(false)); - - $this->CacheTree->setAllowUpdate(true); - - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 1800), - ); - $result = $this->CacheTree->isCacheUpdatable($in); - $this->assertFalse($result); - } - - public function testIsCacheValidReadDisabled() { - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->never()) - ->method('isNewerThan'); - - $this->CacheTree->setAllowRead(false); - - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 7200), - ); - $result = $this->CacheTree->isCacheValid($in); - $this->assertFalse($result); - } - - public function testIsCacheValid() { - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->once()) - ->method('isNewerThan') - ->will($this->returnValue(true)); - - $this->CacheTree->setAllowRead(true); - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 7200), - ); - $result = $this->CacheTree->isCacheValid($in); - $this->assertTrue($result); - } - - public function testIsCacheValidNewAnswerForUser() { - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->once()) - ->method('isNewerThan') - ->will($this->returnValue(false)); - - $this->CacheTree->setAllowRead(true); - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 7200), - ); - $result = $this->CacheTree->isCacheValid($in); - $this->assertFalse($result); - } - - /** - * last refresh for user is undetermined - */ - public function testIsCacheValidNewUser() { - $this->CacheTree->CurrentUser->LastRefresh = $this->getMock('Object', [ - 'isNewerThan' - ]); - $this->CacheTree->CurrentUser->LastRefresh - ->expects($this->once()) - ->method('isNewerThan') - ->will($this->returnValue(null)); - - $this->CacheTree->setAllowRead(true); - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 7200), - ); - $result = $this->CacheTree->isCacheValid($in); - $this->assertFalse($result); - } - - /** - * tests that cache is invalid if thread has new answers - */ - public function testIsCacheValidNewAnswerInThread() { - $this->CacheTree->setAllowRead(true); - - $in = array( - 'id' => 1, - 'last_answer' => date('Y-m-d H:i:s', time() - 1800), - ); - $result = $this->CacheTree->isCacheValid($in); - $this->assertFalse($result); - } - - } diff --git a/app/Test/Case/Lib/Cache/ItemCacheTest.php b/app/Test/Case/Lib/Saito/Cache/ItemCacheTest.php similarity index 98% rename from app/Test/Case/Lib/Cache/ItemCacheTest.php rename to app/Test/Case/Lib/Saito/Cache/ItemCacheTest.php index e55911250..40f69d015 100644 --- a/app/Test/Case/Lib/Cache/ItemCacheTest.php +++ b/app/Test/Case/Lib/Saito/Cache/ItemCacheTest.php @@ -1,6 +1,6 @@ {$key} = $val; + } elseif ($key === lcfirst($key)) { + $this->_rawData['Entry'][$key] = $val; + } else { + $this->_rawData[$key] = $val; + } + } + + } + + class UserPostingTraitTest extends CakeTestCase { + + public $editPeriod = 20; + + public function testIsAnsweringForbidden() { + $this->Mock->set('locked', 0); + $result = $this->Mock->isAnsweringForbidden(); + $expected = false; + $this->assertSame($result, $expected); + + $this->Mock->set('locked', '0'); + $result = $this->Mock->isAnsweringForbidden(); + $expected = false; + $this->assertSame($result, $expected); + + $this->Mock->set('locked', false); + $result = $this->Mock->isAnsweringForbidden(); + $expected = false; + $this->assertSame($result, $expected); + } + + public function testIsEditingForbiddenSuccess() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", time() - ($this->editPeriod * 60) + 1), + 'locked' => 0 + ]; + $this->Mock->set('Entry', $entry); + + $user = ['id' => 1, 'user_type' => 'user']; + $this->Mock->set('_CU', new SaitoUser($user)); + + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertFalse($result); + } + + public function testIsEditingForbiddenEmptyUser() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", + time() - ($this->editPeriod * 60) + 1), + 'locked' => 0, + ]; + $this->Mock->set('Entry', $entry); + $this->Mock->set('_CU', new SaitoUser); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertTrue($result); + } + + public function testIsEditingForbiddenAnon() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", time()), + ]; + $this->Mock->set('Entry', $entry); + $user = [ + 'id' => null, + 'user_type' => 'anon', + ]; + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertTrue($result); + } + + public function testIsEditingForbiddenWrongUser() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", time()), + ]; + $this->Mock->set('Entry', $entry); + $user = [ + 'id' => 2, + 'user_type' => 'user', + ]; + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertEquals($result, 'user'); + } + + public function testIsEditingForbiddenToLate() { + $editPeriod = 20; + Configure::write('Saito.Settings.edit_period', $editPeriod); + $entry = [ + 'user_id' => 1, + 'locked' => false, + 'time' => strftime( "%c", time() - ($this->editPeriod * 60) - 1) + ]; + $this->Mock->set('Entry', $entry); + $user = [ + 'id' => 1, + 'user_type' => 'user', + ]; + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertEquals($result, 'time'); + } + + public function testIsEditingForbiddenLocked() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", time()), + 'locked' => 1, + ]; + $this->Mock->set('Entry', $entry); + $user = [ + 'id' => 1, + 'user_type' => 'user', + ]; + $this->Mock->set('Entry', $entry); + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertEquals($result, 'locked'); + } + + public function testIsEditingForbiddenModToLateNotFixed() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", + time() - ($this->editPeriod * 60) - 1), + 'fixed' => false, + ]; + $user = [ + 'id' => 1, + 'user_type' => 'mod', + ]; + $this->Mock->set('Entry', $entry); + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertEquals($result, 'time'); + } + + public function testIsEditingForbiddenModToLateFixed() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", + time() - (Configure::read('Saito.Settings.edit_period') * 60) - 1), + 'fixed' => true, + ]; + $user = [ + 'id' => 1, + 'user_type' => 'mod', + ]; + $this->Mock->set('Entry', $entry); + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertFalse($result); + } + + public function testIsEditingForbiddenAdminToLateNotFixed() { + $entry = [ + 'user_id' => 1, + 'time' => strftime("%c", + time() - ($this->editPeriod * 60) - 1), + 'fixed' => false, + ]; + $user = [ + 'id' => 1, + 'user_type' => 'admin', + ]; + $this->Mock->set('Entry', $entry); + $this->Mock->set('_CU', new SaitoUser($user)); + $result = $this->Mock->isEditingAsCurrentUserForbidden(); + $this->assertFalse($result); + } + + public function setUp() { + $this->editPeriodGlob = Configure::read('Saito.Settings.edit_period'); + Configure::write('Saito.Settings.edit_period', $this->editPeriod); + $this->Mock = new UserPostingTraitClassMock(); + } + + public function tearDown() { + unset($this->Mock); + Configure::write('Saito.Settings.edit_period', $this->editPeriodGlob); + } + + } diff --git a/app/Test/Case/Lib/ProperizeTest.php b/app/Test/Case/Lib/Saito/ProperizeTest.php similarity index 97% rename from app/Test/Case/Lib/ProperizeTest.php rename to app/Test/Case/Lib/Saito/ProperizeTest.php index 043d46bce..73eda27fe 100644 --- a/app/Test/Case/Lib/ProperizeTest.php +++ b/app/Test/Case/Lib/Saito/ProperizeTest.php @@ -1,6 +1,6 @@ [ 'id' => 1, 'tid' => 0, 'subject' => 'a', 'text' => 'b', 'time' => 0, 'last_answer' => 0, 'fixed' => false, 'solves' => '', 'user_id' => 1 ], + 'Entry' => [ 'id' => 1, 'tid' => 0, 'pid' => '0', 'subject' => 'a', 'text' => 'b', 'time' => 0, 'last_answer' => 0, 'fixed' => false, 'solves' => '', 'user_id' => 1 ], 'Category' => ['id' => 1, 'accession' => 0, 'description' => 'd', 'category' => 'c' ], 'User' => ['id' => 1, 'username' => 'u'] ]; - $entries = $this->EntryHelper->createTreeObject($entry); - $entries = $entries->addDecorator(function ($node) { - $node = $this->getMock('PostingCurrentUserDecorator', ['isIgnored'], [$node]); - $node->expects($this->once())->method('isIgnored')->will($this->returnValue(true)); - $node->setCurrentUser($this->SaitoUser); - return $node; - }); + $entries = $this->getMock('\Saito\Posting\Posting', ['isIgnored'], [$this->SaitoUser, $entry]); + $entries->expects($this->once())->method('isIgnored')->will($this->returnValue(true)); $xPathQuery = '//ul[@data-id=1]/li[contains(@class,"ignored")]'; //= posting should be ignored $options = ['maxThreadDepthIndent' => 25]; - $renderer = new ThreadHtmlRenderer($this->EntryHelper, $options); + $renderer = new \Saito\Thread\Renderer\ThreadHtmlRenderer($this->EntryHelper, $options); $result = $renderer->render($entries); $this->assertXPath($result, $xPathQuery); @@ -47,7 +37,7 @@ public function testIgnore() { public function testNesting() { $entry = $entry1 = $entry2 = $entry3 = [ - 'Entry' => [ 'id' => 1, 'tid' => 0, 'subject' => 'a', 'text' => 'b', 'time' => 0, 'last_answer' => 0, 'fixed' => false, 'solves' => '', 'user_id' => 1 ], + 'Entry' => [ 'id' => 1, 'tid' => 0, 'pid' => 0, 'subject' => 'a', 'text' => 'b', 'time' => 0, 'last_answer' => 0, 'fixed' => false, 'solves' => '', 'user_id' => 1 ], 'Category' => ['id' => 1, 'accession' => 0, 'description' => 'd', 'category' => 'c' ], 'User' => ['username' => 'u'] ]; @@ -61,12 +51,7 @@ public function testNesting() { $entries['_children'] = [$entry1 + ['_children' => [$entry2]], $entry3]; $entries = $this->EntryHelper->createTreeObject($entries); - $entries = $entries->addDecorator(function ($node) { - $node = new PostingCurrentUserDecorator($node); - $node->setCurrentUser($this->SaitoUser); - return $node; - }); - $renderer = new ThreadHtmlRenderer($this->EntryHelper, ['maxThreadDepthIndent' => 9999]); + $renderer = new \Saito\Thread\Renderer\ThreadHtmlRenderer($this->EntryHelper, ['maxThreadDepthIndent' => 9999]); $result = $renderer->render($entries); @@ -80,11 +65,12 @@ public function testThreadMaxDepth() { 'SaitoUser', ['getMaxAccession', 'getId', 'hasBookmarks'] ); - $SaitoUser->ReadEntries = $this->getMock('ReadPostingsDummy'); + $SaitoUser->ReadEntries = $this->getMock('ReadPostings\ReadPostingsDummy'); $entry = [ 'Entry' => [ 'id' => 1, + 'pid' => 0, 'tid' => 0, 'subject' => 'a', 'text' => 'b', @@ -114,14 +100,10 @@ public function testThreadMaxDepth() { ]; $entries = $this->EntryHelper->createTreeObject($entries); - $entries = $entries->addDecorator(function ($node) use ($SaitoUser) { - $node = new PostingCurrentUserDecorator($node); - $node->setCurrentUser($SaitoUser); - return $node; - }); + $this->EntryHelper->dic->set('CU', $SaitoUser); // max depth should not apply - $renderer = new ThreadHtmlRenderer($this->EntryHelper, [ + $renderer = new \Saito\Thread\Renderer\ThreadHtmlRenderer($this->EntryHelper, [ 'maxThreadDepthIndent' => 9999 ]); $result = $renderer->render($entries); @@ -137,7 +119,9 @@ public function setUp() { $this->EntryHelper = $this->_setupEntryHelper(); $this->SaitoUser = new SaitoUser; - $this->SaitoUser->ReadEntries = new ReadPostingsDummy; + $this->SaitoUser->ReadEntries = new ReadPostings\ReadPostingsDummy; + + $this->EntryHelper->dic = \Saito\Test\DicSetup::getNewDic($this->SaitoUser); } protected function _setupEntryHelper() { diff --git a/app/Test/Case/Lib/SaitoUser/CategoryAuthTest.php b/app/Test/Case/Lib/Saito/User/Auth/CategoryAuthTest.php similarity index 80% rename from app/Test/Case/Lib/SaitoUser/CategoryAuthTest.php rename to app/Test/Case/Lib/Saito/User/Auth/CategoryAuthTest.php index db017330d..c9c29bc1d 100644 --- a/app/Test/Case/Lib/SaitoUser/CategoryAuthTest.php +++ b/app/Test/Case/Lib/Saito/User/Auth/CategoryAuthTest.php @@ -1,7 +1,7 @@ 1, 'user_type' => 'anon']); - $this->Lib = new CategoryAuth($User); + $this->Lib = new CategoryAuthorization($User); $result = $this->Lib->getAllowed('select'); $expected = array( diff --git a/app/Test/Case/Lib/SaitoUser/LastRefresh/LastRefreshDatabaseTest.php b/app/Test/Case/Lib/Saito/User/LastRefresh/LastRefreshDatabaseTest.php similarity index 92% rename from app/Test/Case/Lib/SaitoUser/LastRefresh/LastRefreshDatabaseTest.php rename to app/Test/Case/Lib/Saito/User/LastRefresh/LastRefreshDatabaseTest.php index 56780dabd..2f6c911fa 100644 --- a/app/Test/Case/Lib/SaitoUser/LastRefresh/LastRefreshDatabaseTest.php +++ b/app/Test/Case/Lib/Saito/User/LastRefresh/LastRefreshDatabaseTest.php @@ -1,6 +1,5 @@ CurrentUser = new CurrentUserComponent($Collection); - $this->LastRefresh = new LastRefreshDatabase($this->CurrentUser); + $this->LastRefresh = new \Saito\User\LastRefresh\LastRefreshDatabase($this->CurrentUser); } /** diff --git a/app/Test/Case/Lib/SaitoUser/ReadPostings/ReadPostingsCookieTest.php b/app/Test/Case/Lib/Saito/User/ReadPostings/ReadPostingsCookieTest.php similarity index 97% rename from app/Test/Case/Lib/SaitoUser/ReadPostings/ReadPostingsCookieTest.php rename to app/Test/Case/Lib/Saito/User/ReadPostings/ReadPostingsCookieTest.php index c20934627..5c0f608c9 100644 --- a/app/Test/Case/Lib/SaitoUser/ReadPostings/ReadPostingsCookieTest.php +++ b/app/Test/Case/Lib/Saito/User/ReadPostings/ReadPostingsCookieTest.php @@ -1,9 +1,10 @@ Ecach->getDataSource(); - if (stripos($DS->config['datasource'], 'MySQL') === false) { - return; - } - - $length = 10e6; - - $saveData = function($length) { - $data = str_pad('', $length, 0); - $this->Ecach->save(['key' => 'foo', 'value' => $data]); - }; - - $expectSavedLength = function($expected) { - $result = $this->Ecach->findByKey('foo'); - $this->assertEquals($expected, strlen($result['Ecach']['value'])); - }; - - $saveData($length); - // fixture has the same problem as schema.php: CakePHP only creates a BLOB - // for `binary` field type on MySQL - $expectSavedLength(65535); - - // this fix is run in schema.php's after() - $this->cakeMysqlMediumBlobFix(); - $saveData($length); - $expectSavedLength($length); - } - - /** - * setUp method - * - * @return void - */ - public function setUp() { - parent::setUp(); - $this->Ecach = ClassRegistry::init('Ecach'); - } - - /** - * tearDown method - * - * @return void - */ - public function tearDown() { - unset($this->Ecach); - - parent::tearDown(); - } - - } diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index d617b10db..0d6fda1c2 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -1,7 +1,6 @@ with('Model.Thread.create', $this->anything()); //# Setup CurrentUser - $SaitoUser = $this->getMock('SaitoUser', ['getId', 'hasBookmarks']); + $SaitoUser = $this->getMock('Saito\User\SaitoUser', ['getId', 'hasBookmarks']); $SaitoUser->expects($this->any()) ->method('getId') ->will($this->returnValue(1)); @@ -114,7 +112,7 @@ public function testCreateCategoryThreadCounterUpdate() { $category = 1; //# Setup CurrentUser - $SaitoUser = $this->getMock('SaitoUser', ['getId']); + $SaitoUser = $this->getMock('\Saito\User\SaitoUser', ['getId']); $SaitoUser->expects($this->any()) ->method('getId') ->will($this->returnValue(1)); @@ -225,6 +223,44 @@ public function testThreadMerge() { $this->assertEquals($appendedEntry, 1); } + /** + * test that a unpinned source thread is pinned after merge if target is pinned + */ + public function testThreadMergePin() { + //= unlock source the fixture thread + $this->Entry->id = 4; + $this->Entry->toggle('locked'); + $entry = $this->Entry->get(4); + $this->assertTrue($entry['Entry']['locked'] == false); + + //= lock the target fixture thread + $this->Entry->id = 2; + $this->Entry->toggle('locked'); + $entry = $this->Entry->get(4); + $this->assertTrue($entry['Entry']['locked'] == false); + + //= merge + $this->Entry->id = 4; + $this->Entry->threadMerge(2); + + $entry = $this->Entry->get(4); + $this->assertTrue($entry['Entry']['locked'] == true); + } + + /** + * test that a pinned source thread is unpinned before merge + */ + public function testThreadMergeUnpin() { + $entry = $this->Entry->get(4); + $this->assertTrue($entry['Entry']['locked'] == true); + + $this->Entry->id = 4; + $this->Entry->threadMerge(2); + + $entry = $this->Entry->get(4); + $this->assertTrue($entry['Entry']['locked'] == false); + } + public function testIdsForNode() { $expected = array(2, 3, 7, 9); $result = $this->Entry->getIdsForNode(2); @@ -268,7 +304,7 @@ public function testThreadIncrementViewOmitUser() { } public function testChangeThreadCategory() { - $SaitoUser = $this->getMock('SaitoUser'); + $SaitoUser = $this->getMock('\Saito\User\SaitoUser'); $SaitoUser->Categories = $this->getMock('Object', ['getAllowed']); $SaitoUser->Categories->expects($this->once()) ->method('getAllowed') @@ -311,7 +347,7 @@ public function testChangeThreadCategory() { } public function testChangeThreadCategoryNotAnExistingCategory() { - $SaitoUser = $this->getMock('SaitoUser'); + $SaitoUser = $this->getMock('\Saito\User\SaitoUser'); $SaitoUser->Categories = $this->getMock('Object', ['getAllowed']); $SaitoUser->Categories->expects($this->once()) ->method('getAllowed') @@ -429,189 +465,6 @@ public function testAnonymizeEntriesFromUser() { $this->assertEquals($result, $expected); } - public function testIsAnsweringForbidden() { - $entry = array('Entry' => array('locked' => 0)); - $result = $this->Entry->isAnsweringForbidden($entry); - $expected = false; - $this->assertSame($result, $expected); - $entry = array('Entry' => array('locked' => '0')); - $result = $this->Entry->isAnsweringForbidden($entry); - $expected = false; - $this->assertSame($result, $expected); - $entry = array('Entry' => array('locked' => false)); - $result = $this->Entry->isAnsweringForbidden($entry); - $expected = false; - $this->assertSame($result, $expected); - } - - public function testIsEditingForbiddenSuccess() { - $this->Entry->_editPeriod = 1200; - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - $this->Entry->_editPeriod + 1), - 'locked' => 0, - ) - ); - $user = array( - 'id' => 1, - 'user_type' => 'user', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertFalse($result); - } - - public function testIsEditingForbiddenEmptyUser() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60) + 1), - 'locked' => 0, - ) - ); - $user = null; - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertTrue($result); - } - - public function testIsEditingForbiddenAnon() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", time()), - ) - ); - $user = array( - 'id' => null, - 'user_type' => 'anon', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertTrue($result); - } - - public function testIsEditingForbiddenWrongUser() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", time()), - ) - ); - $user = array( - 'id' => 2, - 'user_type' => 'user', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertEquals($result, 'user'); - } - - public function testIsEditingForbiddenToLate() { - $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', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertEquals($result, 'time'); - } - - public function testIsEditingForbiddenLocked() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", time()), - 'locked' => 1, - ) - ); - $user = array( - 'id' => 1, - 'user_type' => 'user', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertEquals($result, 'locked'); - } - - public function testIsEditingForbiddenModToLateNotFixed() { - Configure::write('Saito.Settings.edit_period', 20); - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60) - 1), - 'fixed' => false, - ) - ); - $user = array( - 'id' => 1, - 'user_type' => 'mod', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertEquals($result, 'time'); - } - - public function testIsEditingForbiddenModToLateFixed() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60) - 1), - 'fixed' => true, - ) - ); - $user = array( - 'id' => 1, - 'user_type' => 'mod', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertFalse($result); - } - - public function testIsEditingForbiddenAdminToLateNotFixed() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60) - 1), - 'fixed' => false, - ) - ); - $user = array( - 'id' => 1, - 'user_type' => 'admin', - ); - $SaitoUser = new SaitoUser($user); - $user = $SaitoUser; - $result = $this->Entry->isEditingForbidden($entry, $user); - $this->assertFalse($result); - } - public function testIsRoot() { $this->Entry->id = 8; $result = $this->Entry->isRoot(); @@ -638,8 +491,10 @@ public function testIsRoot() { } public function testTreeForNode() { - $this->Entry = $this->getMock('Entry', array('getThreadId', 'treesForThreads'), - array(false, 'entries', 'test') + $this->Entry = $this->getMock( + 'Entry', + ['getThreadId', 'treesForThreads'], + [false, 'entries', 'test'] ); $this->Entry->expects($this->once()) @@ -647,28 +502,22 @@ public function testTreeForNode() { ->with(2) ->will($this->returnValue(1)); - $ar = array( - 0 => array( - 'Entry' => array( - 'id' => 1, - ), - '_children' => array( + $ar = [ + 0 => [ + 'Entry' => ['id' => 1], + '_children' => [ 0 => - array( - 'Entry' => array( - 'id' => 2, - ), - 'User' - ), - 'Entry' => array( - 'id' => 3, - ), - ) - ) - ); + [ + 'Entry' => ['id' => 2], + 'User' + ], + 'Entry' => ['id' => 3], + ] + ] + ]; $this->Entry->expects($this->once()) ->method('treesForThreads') - ->with(array(array('id' => 1))) + ->with([1]) ->will($this->returnValue($ar)); $result = $this->Entry->treeForNode(2); diff --git a/app/Test/Case/Model/SmileyCodeTest.php b/app/Test/Case/Model/SmileyCodeTest.php index c2a5305be..46b223f91 100644 --- a/app/Test/Case/Model/SmileyCodeTest.php +++ b/app/Test/Case/Model/SmileyCodeTest.php @@ -1,7 +1,8 @@ User->find('latest'); - $this->assertEquals($result['User']['id'], 8); + $this->assertEquals($result['User']['id'], 9); } public function testSetCategoryAll() { @@ -364,14 +364,14 @@ public function testActivateUserAlreadyActivated() { } public function testActivateUserWrongCode() { - $result = $this->User->activate(9, '123'); + $result = $this->User->activate(10, '123'); $this->assertFalse($result); } public function testActivateUserSuccess() { - $result = $this->User->activate(9, '1548'); + $result = $this->User->activate(10, '1548'); $this->assertEquals('activated', $result['status']); - $user = $this->User->findById(9); + $user = $this->User->findById(10); $this->assertEquals(0, $user['User']['activate_code']); $this->assertEquals($user['User'], $result['User']); } @@ -644,7 +644,7 @@ public function testRegisterValidationUsernameDisallowedChars() { $this->assertEquals($this->User->validationErrors, $expected); } - public function testRegisterValidationUsernameIsEqual() { + public function testRegisterValidationUsernameIsEqualDisallowed() { $data = [ 'User' => [ 'username' => 'Mitsch', @@ -673,6 +673,23 @@ public function testRegisterValidationUsernameIsEqual() { $this->assertEmpty($this->User->validationErrors); } + /** + * the validation should not trigger if two coliding but existing users + * with same name get values updated, but not the username + */ + public function testRegisterValidationUsernameIsEqualAllowed() { + $data = [ + 'User' => [ + 'id' => 9, + 'username' => 'Liane', + 'user_email' => 'new@example.com' + ] + ]; + $result = $this->User->save($data); + $this->assertEquals($data, $result); + $this->assertEmpty($this->User->validationErrors); + } + public function setUp() { Security::setHash('md5'); diff --git a/app/Test/Case/View/Helper/EntryHelperTest.php b/app/Test/Case/View/Helper/EntryHelperTest.php index 1f5adea08..47777e14c 100755 --- a/app/Test/Case/View/Helper/EntryHelperTest.php +++ b/app/Test/Case/View/Helper/EntryHelperTest.php @@ -1,9 +1,10 @@ set('LineCache', new ItemCache('test')); $this->EntryH = new EntryHHelper($View); + + $this->EntryH->dic = \Saito\Test\DicSetup::getNewDic(); } public function tearDown() { @@ -25,39 +28,31 @@ public function tearDown() { public function testGetFastLink() { $this->EntryH->webroot = 'localhost/'; - //* - $entry = array( 'Entry' => array( - 'id' => 3, - 'subject' => 'Subject', - 'text' => 'Text' - ) - ); + //= simple test + $entry = [ + 'Entry' => [ + 'id' => 3, + 'tid' => 1, + 'pid' => 1, + 'subject' => 'Subject', + 'text' => 'Text' + ] + ]; $expected = "Subject"; $result = $this->EntryH->getFastLink($entry); $this->assertEquals($expected, $result); - //* test n/t posting - $entry = array( 'Entry' => array( - 'id' => 1, - 'subject' => 'Subject', - 'text' => '' - ) - ); - $expected = "Subject n/t"; - $result = $this->EntryH->getFastLink($entry); - $this->assertEquals($expected, $result); - - //* test 'class' input - $entry = array( 'Entry' => array( - 'id' => 3, - 'subject' => 'Subject', - 'text' => 'Text' - ) - ); + //= test 'class' input $class = 'my_test_class foo'; $expected = "Subject"; $result = $this->EntryH->getFastLink($entry, array( 'class' => $class )); $this->assertEquals($expected, $result); + + //* test n/t posting + $entry['Entry']['text'] = ''; + $expected = "Subject n/t"; + $result = $this->EntryH->getFastLink($entry); + $this->assertEquals($expected, $result); } } diff --git a/app/Test/Fixture/EcachFixture.php b/app/Test/Fixture/EcachFixture.php deleted file mode 100644 index 486e6d48c..000000000 --- a/app/Test/Fixture/EcachFixture.php +++ /dev/null @@ -1,36 +0,0 @@ - array('type' => 'string', 'null' => false, 'default' => null, 'length' => 128, 'key' => 'primary', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'), - 'value' => array('type' => 'binary', 'null' => false, 'default' => null), - 'indexes' => array( - 'PRIMARY' => array('column' => 'key', 'unique' => 1) - ), - 'tableParameters' => array('charset' => 'utf8', - 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB') - ); - - /** - * Records - * - * @var array - */ - public $records = array( - array( - 'key' => 'Lorem ipsum dolor sit amet', - 'value' => 'Lorem ipsum dolor sit amet' - ), - ); - - } diff --git a/app/Test/Fixture/UserFixture.php b/app/Test/Fixture/UserFixture.php index 199750280..5446ccb83 100755 --- a/app/Test/Fixture/UserFixture.php +++ b/app/Test/Fixture/UserFixture.php @@ -128,6 +128,11 @@ class UserFixture extends CakeTestFixture { ], [ 'id' => 9, + 'username' => 'Liane', + 'user_email' => 'liane@example.com' + ], + [ + 'id' => 10, 'username' => 'Diane', 'user_email' => 'diane@example.com', 'activate_code' => 1548 diff --git a/app/Test/Selenium/Lib/SaitoSeleniumTestCase.php b/app/Test/Selenium/Lib/SaitoSeleniumTestCase.php index c68c427a7..b8efa7ae7 100644 --- a/app/Test/Selenium/Lib/SaitoSeleniumTestCase.php +++ b/app/Test/Selenium/Lib/SaitoSeleniumTestCase.php @@ -60,7 +60,6 @@ class SaitoSeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase { */ public $fixtures = array( 'app.bookmark', - 'app.ecach', 'app.user', 'app.user_online', 'app.entry', diff --git a/app/View/Admins/admin_logs.ctp b/app/View/Admins/admin_logs.ctp index 11b669dbf..a9ea5afad 100644 --- a/app/View/Admins/admin_logs.ctp +++ b/app/View/Admins/admin_logs.ctp @@ -2,7 +2,11 @@

    - $content) : ?> -

    - Admin->formatCakeLog($content) ?> - + $content) { + echo $this->Html->tag('h3', $title); + echo $this->Admin->formatCakeLog($content) ; + } diff --git a/app/View/Admins/admin_plugins.ctp b/app/View/Admins/admin_plugins.ctp new file mode 100644 index 000000000..8186bbad7 --- /dev/null +++ b/app/View/Admins/admin_plugins.ctp @@ -0,0 +1,15 @@ +Html->addCrumb(__('Plugins'), '/admin/plugins'); + + echo $this->Html->tag('h1', __('Plugins')); + $items = SaitoEventManager::getInstance() + ->dispatch('Request.Saito.View.Admin.plugins'); + if ($items) { + foreach ($items as $item) { + $plugins[] = $this->Html->link( + $item['title'], + $item['url'] + ); + } + echo $this->Html->nestedList($plugins); + } diff --git a/app/View/Bookmarks/edit.ctp b/app/View/Bookmarks/edit.ctp index e5e802300..6728a3949 100644 --- a/app/View/Bookmarks/edit.ctp +++ b/app/View/Bookmarks/edit.ctp @@ -8,14 +8,7 @@ Layout->panelHeading(__('Edit Bookmark'), ['pageHeading' => true]) ?>
    - $this->request->data['Entry'], - 'Category' => $this->request->data['Entry']['Category'], - 'User' => $this->request->data['Entry']['User'], - ); - echo $this->element('/entry/view_content', array( - 'entry' => $entry)); ?> + element('/entry/view_content', ['entry' => $entry]); ?>