diff --git a/app/Config/version.php b/app/Config/version.php index 36337a2fb..53e04613a 100644 --- a/app/Config/version.php +++ b/app/Config/version.php @@ -1,2 +1,2 @@ 'entries/view/', 'atBaseUrl' => 'users/name/', - 'server' => Router::baseURL(), + 'server' => Router::fullBaseUrl(), 'webroot' => $this->webroot ] ); diff --git a/app/Controller/Component/CacheSupportComponent.php b/app/Controller/Component/CacheSupportComponent.php index 4abb173d9..49f94b8a9 100644 --- a/app/Controller/Component/CacheSupportComponent.php +++ b/app/Controller/Component/CacheSupportComponent.php @@ -1,66 +1,37 @@ _CacheSupport = new CacheSupport(); + $this->CacheTree = CacheTree::getInstance(); $this->CacheTree->initialize($Controller); } - public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { - $this->CacheTree->beforeRedirect($Controller, $url, $status, $exit); - } - - public function shutdown(Controller $Controller) { - $this->CacheTree->shutdown($Controller); - } - - public function clearAll() { - $this->clearSaito(); - $this->clearApc(); - $this->clearCake(); - } - - public function clearSaito() { - Cache::clear(false, 'default'); - Cache::clear(false, 'short'); - $this->clearTrees(); - } - - public function clearCake() { - Cache::clearGroup('persistent'); - Cache::clearGroup('models'); - Cache::clearGroup('views'); - } - - public function clearTree($id) { - $this->_clearEntries(); - $this->CacheTree->delete($id); + public function beforeRender(Controller $Controller) { + $Controller->set('CacheTree', $this->CacheTree); } - public function clearTrees() { - $this->_clearEntries(); - $this->CacheTree->reset(); + public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { + $this->CacheTree->saveCache(); } - protected function _clearEntries() { - Cache::clear(false, 'entries'); + public function shutdown(Controller $Controller) { + $this->CacheTree->saveCache(); } - /** - * Clears out the APC if available - */ - public function clearApc() { - if (function_exists('apc_store')) { - apc_clear_cache(); - apc_clear_cache('user'); - apc_clear_cache('opcode'); + public function __call($method, $params) { + $proxy = [$this->_CacheSupport, $method]; + if (is_callable($proxy)) { + return call_user_func_array($proxy, $params); } } - } diff --git a/app/Controller/Component/CurrentUserComponent.php b/app/Controller/Component/CurrentUserComponent.php index 158200150..b6e526fb0 100755 --- a/app/Controller/Component/CurrentUserComponent.php +++ b/app/Controller/Component/CurrentUserComponent.php @@ -140,7 +140,7 @@ protected function _markOnline() { Stopwatch::start('CurrentUser->_markOnline()'); $id = $this->getId(); - if ($this->isLoggedIn() == false): + if ($this->isLoggedIn() === false): // don't count search bots as guests if ($this->_isBot()) { return; @@ -166,8 +166,8 @@ protected function _isBot() { protected function _cookieRelogin() { $cookie = $this->PersistentCookie->get(); - // is_array -> if cookie could no be correctly deciphered it's just an random string if ($cookie) { + // is_array -> if cookie could no be correctly deciphered it's just an random string if (!is_null($cookie) && is_array($cookie)): if ($this->_Controller->Auth->login($cookie)): return; @@ -203,7 +203,7 @@ public function login() { public function refresh($user = null) { parent::set($this->_Controller->Auth->user()); - // all session should must use current user data (locked, user_type, …) + // all session have to use current user data (locked, user_type, …) if ($this->isLoggedIn()) { $this->_User->id = $this->getId(); parent::set($this->_User->getProfile($this->getId())); @@ -368,12 +368,18 @@ public function __construct(SaitoUser $currentUser, User $user) { $this->user = $user; } - public function forceSet() { - $this->_set(date("Y-m-d H:i:s")); - } - - public function set() { - $this->_set($this->currentUser['last_refresh_tmp']); + /** + * @param mixed $timestamp + * + * null|'now'|<`Y-m-d H:i:s` timestamp> + */ + public function set($timestamp = null) { + if ($timestamp === 'now') { + $timestamp = date('Y-m-d H:i:s'); + } elseif ($timestamp === null) { + $timestamp = $this->currentUser['last_refresh_tmp']; + } + $this->_set($timestamp); } public function setMarker() { diff --git a/app/Controller/Component/EmailNotificationComponent.php b/app/Controller/Component/EmailNotificationComponent.php index 2f552cf13..dab187a31 100644 --- a/app/Controller/Component/EmailNotificationComponent.php +++ b/app/Controller/Component/EmailNotificationComponent.php @@ -39,65 +39,66 @@ public function dispatchEvent($event) { } protected function _modelEntryReplyToThread($event, array $recipients) { - // get parent entry - foreach ( $recipients as $recipient ): - // don't send answer if new entry belongs to the user itself - if ( Configure::read('debug') === 0 && (int)$recipient['id'] === (int)$event->data['data']['Entry']['user_id'] ) { - continue; - } - $event->subject()->contain(); - $rootEntry = $event->subject()->findById($event->data['data']['Entry']['tid']); - try { - $this->_Controller->SaitoEmail->email(array( - 'recipient' => array('User' => $recipient), - 'subject' => __('New reply to "%s"', $rootEntry['Entry']['subject']), - 'sender' => array( - 'User' => array( - 'user_email' => Configure::read('Saito.Settings.forum_email'), - 'username' => Configure::read('Saito.Settings.forum_name')), - ), - 'template' => Configure::read('Config.language') . DS . 'notification-model-entry-afterReply', - 'viewVars' => array( - 'recipient' => $recipient, - 'parentEntry' => $rootEntry, - 'newEntry' => $event->data['data'], - 'notification' => $recipient['Esnotification'], - ), - )); - } catch ( Exception $exc ) { + $event->subject()->contain(); + $rootEntry = $event->subject()->findById($event->data['data']['Entry']['tid']); + $config = [ + 'subject' => __( + 'New reply to "%s"', + $rootEntry['Entry']['subject'] + ), + 'sender' => array( + 'User' => array( + 'user_email' => Configure::read('Saito.Settings.forum_email'), + 'username' => Configure::read('Saito.Settings.forum_name') + ), + ), + 'template' => Configure::read( + 'Config.language' + ) . DS . 'notification-model-entry-afterReply', + 'viewVars' => array( + 'parentEntry' => $rootEntry, + 'newEntry' => $event->data['data'], + ), + ]; + foreach ( $recipients as $recipient ): + if ($this->_shouldRecipientReceiveReplyMessage($recipient, $event->data['data']['Entry'])) { + $config['recipient'] = ['User' => $recipient]; + $config['viewVars']['recipient'] = $recipient; + $config['viewVars']['notification'] = $recipient['Esnotification']; + $this->_email($config); } endforeach; } protected function _modelEntryReplyToEntry($event, array $recipients) { - // get parent entry + $event->subject()->contain(); + $parentEntry = $event->subject()->findById($event->data['data']['Entry']['pid']); + $config = [ + 'subject' => __( + 'New reply to "%s"', + $parentEntry['Entry']['subject'] + ), + 'sender' => array( + 'User' => array( + 'user_email' => Configure::read('Saito.Settings.forum_email'), + 'username' => Configure::read('Saito.Settings.forum_name') + ), + ), + 'template' => Configure::read( + 'Config.language' + ) . DS . 'notification-model-entry-afterReply', + 'viewVars' => array( + 'parentEntry' => $parentEntry, + 'newEntry' => $event->data['data'] + ) + ]; foreach ( $recipients as $recipient ): - // don't send answer if new entry belongs to the user itself - if ( Configure::read('debug') === 0 && (int)$recipient['id'] === (int)$event->data['data']['Entry']['user_id'] ) { - continue; - } - $event->subject()->contain(); - $parentEntry = $event->subject()->findById($event->data['data']['Entry']['pid']); - try { - $this->_Controller->SaitoEmail->email(array( - 'recipient' => array('User' => $recipient), - 'subject' => __('New reply to "%s"', $parentEntry['Entry']['subject']), - 'sender' => array( - 'User' => array( - 'user_email' => Configure::read('Saito.Settings.forum_email'), - 'username' => Configure::read('Saito.Settings.forum_name')), - ), - 'template' => Configure::read('Config.language') . DS . 'notification-model-entry-afterReply', - 'viewVars' => array( - 'recipient' => $recipient, - 'parentEntry' => $parentEntry, - 'newEntry' => $event->data['data'], - 'notification' => $recipient['Esnotification'], - ), - )); - } catch ( Exception $exc ) { - + if ($this->_shouldRecipientReceiveReplyMessage($recipient, $event->data['data']['Entry'])) { + $config['recipient'] = ['User' => $recipient]; + $config['viewVars']['recipient'] = $recipient; + $config['viewVars']['notification'] = $recipient['Esnotification']; + $this->_email($config); } endforeach; } @@ -107,25 +108,52 @@ public function userActivatedAdminNotice($event) { if ( !is_array($recipients) ) return; $new_user = $event->data['User']; + $config = [ + 'subject' => __('Successfull registration'), + 'sender' => array( + 'User' => array( + 'user_email' => Configure::read('Saito.Settings.forum_email'), + 'username' => Configure::read('Saito.Settings.forum_name') + ), + ), + 'template' => Configure::read( + 'Config.language' + ) . DS . 'notification-admin-user_activated', + 'viewVars' => array('user' => $new_user, 'ip' => env('REMOTE_ADDR')), + + ]; foreach ( $recipients as $recipient ) : - try { - $this->_Controller->SaitoEmail->email(array( - 'recipient' => $recipient, - 'subject' => __('Successfull registration'), - 'sender' => array( - 'User' => array( - 'user_email' => Configure::read('Saito.Settings.forum_email'), - 'username' => Configure::read('Saito.Settings.forum_name')), - ), - 'template' => Configure::read('Config.language') . DS . 'notification-admin-user_activated', - 'viewVars' => array('user' => $new_user, 'ip' => env('REMOTE_ADDR')), - )); - } catch ( Exception $exc ) { - - } + $config['recipient'] = $recipient; + $this->_email($config); endforeach; } + protected function _shouldRecipientReceiveReplyMessage($entry, $recipient) { + if (Configure::read('debug') === 0 && + // don't send answer if new entry belongs to the user itself + (int)$recipient['id'] === (int)$entry['user_id'] + ) { + return false; + } else { + return true; + } + + } + + protected function _email($config) { + try { + $this->_Controller->SaitoEmail->email($config); + } catch (Exception $exc) { + $this->log( + sprintf( + "Error %s in EmailNotificationComponent::_email() with %s", + $exc, + print_r($config, true) + ) + ); + } + } + protected function _debug($event, array $receivers) { debug($event); foreach ( $receivers as $receiver ) { @@ -134,5 +162,3 @@ protected function _debug($event, array $receivers) { } } - -?> \ No newline at end of file diff --git a/app/Controller/Component/SaitoEmailComponent.php b/app/Controller/Component/SaitoEmailComponent.php index 64c24cc6f..01556a1dd 100644 --- a/app/Controller/Component/SaitoEmailComponent.php +++ b/app/Controller/Component/SaitoEmailComponent.php @@ -64,7 +64,7 @@ protected function _resetConfig() { protected function _config($options = array()) { $defaults = array( 'viewVars'=> array( - 'webroot' => Router::baseURL() . $this->_webroot, + 'webroot' => Router::fullBaseUrl() . $this->_webroot, ), ); extract(array_merge_recursive($defaults, $options)); diff --git a/app/Controller/EntriesController.php b/app/Controller/EntriesController.php index 1b9a950c1..7cb8ac193 100644 --- a/app/Controller/EntriesController.php +++ b/app/Controller/EntriesController.php @@ -12,8 +12,6 @@ class EntriesController extends AppController { 'Text', ); public $components = [ - 'CacheTree', - 'CacheSupport', 'Flattr', 'Search.Prg', 'Shouts' @@ -52,7 +50,7 @@ public function index() { $cachedThreads = []; $uncachedThreads = []; foreach($initialThreads as $thread) { - if ($this->CacheTree->isCacheValid($thread)) { + if ($this->CacheSupport->CacheTree->isCacheValid($thread)) { $cachedThreads[$thread['id']] = $thread; } else { $uncachedThreads[$thread['id']] = $thread; @@ -158,7 +156,7 @@ public function mix($tid) { */ public function update() { $this->autoRender = false; - $this->CurrentUser->LastRefresh->forceSet(); + $this->CurrentUser->LastRefresh->set('now'); $this->redirect('/entries/index'); } @@ -190,8 +188,8 @@ public function view($id=null) { return $this->redirect(array( 'action' => 'index' )); } - $this->Entry->id = $id; - $this->request->data = $this->Entry->find('entry', array('conditions' => array('Entry.id' => $id))); + $this->Entry->id = $id; + $this->request->data = $this->Entry->get($id); //* redirect if posting doesn't exists if ( $this->request->data == false ): @@ -245,7 +243,7 @@ public function add($id = null) { // inserting new posting was successful if ($new_posting !== false) : - $this->_afterNewEntry($new_posting); + $this->_setNotifications($new_posting); if ($this->request->is('ajax')) : // Ajax request came from front answer on front page /entries/index if ($this->localReferer('action') === 'index') { @@ -310,7 +308,7 @@ public function add($id = null) { $this->request->data = null; if ($id !== null) { - $this->request->data = $this->Entry->getUnsanitized($id); + $this->request->data = $this->Entry->get($id, true); } if (!empty($this->request->data)): // answer to existing posting @@ -372,29 +370,28 @@ public function threadLine($id = null) { public function edit($id = null) { - if (!$id && empty($this->request->data)) { - throw new NotFoundException(); + if (empty($id)) { + throw new BadRequestException(); } - $old_entry = $this->Entry->getUnsanitized($id); - - if (!$old_entry) { + $oldEntry = $this->Entry->get($id, true); + if (!$oldEntry) { throw new NotFoundException(); } - $forbidden = $this->Entry->isEditingForbidden($old_entry, $this->CurrentUser); - - switch ($forbidden) { + switch ($oldEntry['rights']['isEditingForbidden']) { case 'time': $this->Session->setFlash( 'Stand by your word bro\', it\'s too late. @lo', 'flash/error' ); - return $this->redirect(array('action' => 'view', $id)); + $this->redirect(['action' => 'view', $id]); + return; break; case 'user': $this->Session->setFlash('Not your horse, Hoss! @lo', 'flash/error'); - return $this->redirect(array('action' => 'view', $id)); + $this->redirect(['action' => 'view', $id]); + return; break; case true : $this->Session->setFlash( @@ -404,42 +401,45 @@ public function edit($id = null) { return; } + // try to save edit if (!empty($this->request->data)) { - $this->Entry->id = $id; - $new_entry = $this->Entry->update($this->request->data); + $data = $this->request->data; + $data['Entry']['id'] = $id; + $new_entry = $this->Entry->update($data); if ($new_entry) { - $this->_afterNewEntry(am($this->request['data'], $old_entry)); - return $this->redirect(array('action' => 'view', $id)); + $this->_setNotifications(am($this->request['data'], $oldEntry)); + $this->redirect(['action' => 'view', $id]); + return; } else { $this->Session->setFlash(__('Something clogged the tubes. Could not save entry. Try again.')); } } - $forbiddenAsNormalUser = $this->Entry->isEditingForbidden($old_entry, $this->CurrentUser->mockUserType('user')); - if($forbiddenAsNormalUser) { + // show editing form + if($oldEntry['rights']['isEditingAsUserForbidden']) { $this->Session->setFlash(__('notice_you_are_editing_as_mod'), 'flash/warning'); } - $this->request->data = am($old_entry, $this->request->data); + $this->request->data = am($oldEntry, $this->request->data); // get text of parent entry for citation - $parent_entry_id = $old_entry['Entry']['pid']; + $parent_entry_id = $oldEntry['Entry']['pid']; if ($parent_entry_id > 0) { - $parent_entry = $this->Entry->getUnsanitized($parent_entry_id); + $parent_entry = $this->Entry->get($parent_entry_id, true); $this->set('citeText', $parent_entry['Entry']['text']); } // get notifications $notis = $this->Entry->Esevent->checkEventsForUser( - $old_entry['Entry']['user_id'], + $oldEntry['Entry']['user_id'], array( array( - 'subject' => $old_entry['Entry']['id'], + 'subject' => $oldEntry['Entry']['id'], 'event' => 'Model.Entry.replyToEntry', 'receiver' => 'EmailNotification', ), array( - 'subject' => $old_entry['Entry']['tid'], + 'subject' => $oldEntry['Entry']['tid'], 'event' => 'Model.Entry.replyToThread', 'receiver' => 'EmailNotification', ), @@ -481,12 +481,11 @@ public function delete($id = null) { // Redirect if ($success) { - $this->CacheSupport->clearTree($entry['Entry']['tid']); if ($this->Entry->isRoot($entry)) { - $this->Session->setFlash(__('delete_tree_success'), 'flash/notice'); + $this->Session->setFlash(__('delete_tree_success'), 'flash/success'); $this->redirect('/'); } else { - $this->Session->setFlash(__('delete_subtree_success'), 'flash/notice'); + $this->Session->setFlash(__('delete_subtree_success'), 'flash/success'); $this->redirect('/entries/view/' . $entry['Entry']['pid']); } } else { @@ -702,11 +701,8 @@ public function merge($id = null) { $targetId = $this->request->data['Entry']['targetId']; $this->Entry->id = $id; if ($this->Entry->threadMerge($targetId)) { - // success - $this->Entry->contain(); - $targetEntry = $this->Entry->findById($targetId); - $this->CacheSupport->clearTree($targetEntry['Entry']['id']); - return $this->redirect('/entries/view/' . $id); + $this->redirect('/entries/view/' . $id); + return; } else { $this->Session->setFlash(__("Error"), 'flash/error'); } @@ -740,8 +736,6 @@ public function ajax_toggle($id = null, $toggle = null) { else { $this->Entry->id = $id; $this->request->data = $this->Entry->toggle($toggle); - $tid = $this->Entry->field('tid'); - $this->CacheSupport->clearTree($tid); return ($this->request->data == 0) ? __d('nondynamic', $toggle . '_set_entry_link') : __d('nondynamic', $toggle . '_unset_entry_link'); } @@ -837,9 +831,7 @@ protected function _prepareSlidetabData() { } } - protected function _afterNewEntry($newEntry) { - $this->CacheSupport->clearTree($newEntry['Entry']['tid']); - // set notifications + protected function _setNotifications($newEntry) { if (isset($newEntry['Event'])) { $notis = [ [ @@ -871,57 +863,8 @@ protected function _afterNewEntry($newEntry) { */ protected function _getInitialThreads(CurrentUserComponent $User, $order) { Stopwatch::start('Entries->_getInitialThreads() Paginate'); - // default for logged-in and logged-out users - $cats = $this->Entry->Category->getCategoriesForAccession($User->getMaxAccession()); - - // get data for category chooser - $categoryChooser = $this->Entry->Category->getCategoriesSelectForAccession( - $User->getMaxAccession()); - $this->set('categoryChooser', $categoryChooser); - - $catCT = __('All Categories'); - $catC_isUsed = false; - - // category chooser - if ($User->isLoggedIn()) { - if (Configure::read('Saito.Settings.category_chooser_global') - || (Configure::read('Saito.Settings.category_chooser_user_override') && $User['user_category_override']) - ) { - $catC_isUsed = true; - /* merge the user-cats with all-cats to include categories which are - * new since the user updated his custum-cats the last time - * array (4 => '4', 7 => '7', 13 => '13') + array (4 => true, 7 => '0') - * becomes - * array (4 => true, 7 => '0', 13 => '13') - * with 13 => '13' trueish */ - $user_cats = $User['user_category_custom'] + $cats; - /* then filter for zeros to get only the user categories - * array (4 => true, 13 => '13') */ - $user_cats = array_filter($user_cats); - $user_cats = array_intersect_key($user_cats, $cats); - $this->set('categoryChooserChecked', $user_cats); - - if ($User->isLoggedIn() === false) { - // non logged in user sees his accessions i.e. the default set - } elseif ((int)$User['user_category_active'] === -1) { - // user has choosen to see all available categories i.e. the default set - } elseif ((int)$User['user_category_active'] > 0) { - // logged in users sees his active group if he has access rights - $cats = array_intersect_key($cats, - array($User['user_category_active'] => 1)); - $catCT = $User['user_category_active']; - } elseif (empty($User['user_category_custom'])) { - // for whatever reason we should see a custom category, but there are no set yet - } elseif (empty($User['user_category_custom']) === false) { - // but if he has no active group and a custom groups set he sees his custom group - $cats = array_keys($user_cats); - $catCT = __('Custom'); - } - $this->set('categoryChooserTitleId', $catCT); - } - } - $this->set('categoryChooserIsUsed', $catC_isUsed); + $categories = $this->_setupCategoryChooser($User); $this->paginate = array( /* Whenever you change the conditions here check if you have to adjust @@ -930,7 +873,7 @@ protected function _getInitialThreads(CurrentUserComponent $User, $order) { */ 'conditions' => array( 'pid' => 0, - 'Entry.category' => $cats, + 'Entry.category' => $categories, ), 'contain' => false, 'fields' => 'id, pid, tid, time, last_answer', @@ -950,6 +893,50 @@ protected function _getInitialThreads(CurrentUserComponent $User, $order) { return $initial_threads_new; } + protected function _setupCategoryChooser(SaitoUser $User) { + $categories = $this->Entry->Category->getCategoriesForAccession( + $User->getMaxAccession() + ); + + $is_used = $User->isLoggedIn() && + ( + Configure::read('Saito.Settings.category_chooser_global') || + ( + Configure::read( + 'Saito.Settings.category_chooser_user_override' + ) && $User['user_category_override'] + ) + ); + + if ($is_used) { + // @todo find right place for this; also: User::getCategories(); + App::uses('UserCategories', 'Lib'); + $UserCategories = new UserCategories($User->getSettings(), $categories); + list($categories, $type, $custom) = $UserCategories->get(); + + $this->set('categoryChooserChecked', $custom); + + switch ($type) { + case 'single': + $title = $User['user_category_active']; + break; + case 'custom': + $title = __('Custom'); + break; + default: + $title = __('All Categories'); + } + $this->set('categoryChooserTitleId', $title); + $this->set( + 'categoryChooser', + $this->Entry->Category->getCategoriesSelectForAccession( + $User->getMaxAccession() + ) + ); + } + return $categories; + } + protected function _teardownAdd() { //* find categories for dropdown $categories = $this->Entry->Category->getCategoriesSelectForAccession($this->CurrentUser->getMaxAccession()); diff --git a/app/Controller/SettingsController.php b/app/Controller/SettingsController.php index cce715776..15d1635e9 100755 --- a/app/Controller/SettingsController.php +++ b/app/Controller/SettingsController.php @@ -5,9 +5,10 @@ class SettingsController extends AppController { public $name = 'Settings'; - public $helpers = array( - 'TimeH', - ); + public $helpers = [ + 'Setting', + 'TimeH' + ]; /** * Subset of MLF settings currently used by Saito @@ -61,6 +62,7 @@ class SettingsController extends AppController { public function admin_index() { $settings = $this->request->data = $this->Setting->getSettings(); $settings = array_intersect_key($settings, $this->_currentlyUsedSettings); + $settings['userranks_ranks'] = $this->Setting->pipeMerger($settings['userranks_ranks']); $this->set('Settings', $settings); } @@ -86,7 +88,8 @@ public function admin_edit($id = NULL) { $this->Setting->id = $id; if ( $this->Setting->save($this->request->data) ) { $this->Session->setFlash('Saved. @lo', 'flash/notice'); - $this->redirect(array( 'action' => 'index', $id )); + $this->redirect(['action' => 'index', '#' => $id]); + return; } else { $this->Session->setFlash('Something went wrong @lo', 'flash/error'); } diff --git a/app/Controller/ToolsController.php b/app/Controller/ToolsController.php index 1f1952d70..16b60d25c 100644 --- a/app/Controller/ToolsController.php +++ b/app/Controller/ToolsController.php @@ -9,10 +9,6 @@ */ class ToolsController extends AppController { - public $components = [ - 'CacheSupport' - ]; - public $helpers = array( 'JasmineJs.JasmineJs' ); @@ -21,7 +17,7 @@ class ToolsController extends AppController { * Emtpy out all caches */ public function admin_emptyCaches() { - $this->CacheSupport->clearAll(); + $this->CacheSupport->clear(); $this->Session->setFlash(__('Caches have been emptied.'), 'flash/notice'); return $this->redirect($this->referer()); } @@ -42,7 +38,7 @@ public function testJs() { */ public function clearCache() { if (in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) { - $this->CacheSupport->clearApc(); + $this->CacheSupport->clear('Apc'); echo json_encode(array('APC Clear Cache' => true)); } exit; diff --git a/app/Controller/UploadsController.php b/app/Controller/UploadsController.php index ee352dd7b..7eda3887c 100755 --- a/app/Controller/UploadsController.php +++ b/app/Controller/UploadsController.php @@ -104,7 +104,7 @@ public function delete($id = null) { $this->Upload->id = (int)$id; $file = $this->Upload->read(); if ( $file - && $file['Upload']['user_id'] === $this->CurrentUser->getId() + && (int)$file['Upload']['user_id'] === $this->CurrentUser->getId() ) { if (!$this->Upload->delete(null, false)) { $this->JsData->addAppJsMessage( diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index c13da9380..82813095a 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -5,9 +5,6 @@ class UsersController extends AppController { public $name = 'Users'; - public $components = [ - 'CacheSupport' - ]; public $helpers = [ 'Farbtastic', @@ -359,8 +356,7 @@ public function admin_delete($id = null) { elseif ($id == 1): $this->Session->setFlash(__("You can't delete the installation account."), 'flash/error'); elseif ($this->User->deleteAllExceptEntries($id)): - $this->CacheSupport->clearTrees(); - $this->Session->setFlash(__('User %s deleted.', $readUser['User']['username']), 'flash/notice'); + $this->Session->setFlash(__('User %s deleted.', $readUser['User']['username']), 'flash/success'); $this->redirect('/'); return; else: diff --git a/app/Lib/CacheSupport.php b/app/Lib/CacheSupport.php new file mode 100644 index 000000000..d82db9a14 --- /dev/null +++ b/app/Lib/CacheSupport.php @@ -0,0 +1,119 @@ +addCache( + [ + 'Apc' => 'ApcCacheSupportCachelet', + 'Cake' => 'CakeCacheSupportCachelet', + 'Saito' => 'SaitoCacheSupportCachelet', + 'Thread' => 'ThreadCacheSupportCachelet' + ] + ); + } + + public function clear($cache = null, $id = null) { + if ($cache === null) { + foreach ($this->_Caches as $Cachelet) { + $Cachelet->clear(); + } + } else { + $this->_Caches[$cache]->clear($id); + } + } + + public function addCache($cache) { + foreach ($cache as $key => $class_name) { + $this->_addCachelet($key, new $class_name); + } + } + + protected function _addCachelet($key, CacheSupportCacheletInterface $cachelet) { + if (!isset($this->_Caches[$key])) { + $this->_Caches[$key] = $cachelet; + } + } + } + + interface CacheSupportCacheletInterface { + public function clear($id = null); + } + + App::uses('CakeEvent', 'Event'); + App::uses('CakeEventListener', 'Event'); + App::uses('CacheTree', 'Lib/CacheTree'); + class ThreadCacheSupportCachelet implements CacheSupportCacheletInterface, + CakeEventListener { + + protected $_CacheTree; + + public function __construct() { + $this->_CacheTree = CacheTree::getInstance(); + CakeEventManager::instance()->attach($this); + } + + public function implementedEvents() { + return [ + 'Model.Thread.reset' => 'onThreadsReset', + 'Model.Thread.change' => 'onThreadChanged', + 'Model.Entry.replyToEntry' => 'onEntryChanged', + 'Model.Entry.update' => 'onEntryChanged', + 'Model.Category.update' => 'onThreadsReset', + 'Model.Category.delete' => 'onThreadsReset' + ]; + } + + public function onThreadsReset($event) { + $this->clear(); + } + + public function onThreadChanged($event) { + $this->clear($event->data['subject']); + } + + public function onEntryChanged($event) { + $model_alias = $event->subject()->alias; + if (!isset($event->data['data'][$model_alias]['tid'])) { + throw new InvalidArgumentException('No thread-id in event data.'); + } + $thread_id = $event->data['data'][$model_alias]['tid']; + $this->clear($thread_id); + } + + public function clear($id = null) { + Cache::clear(false, 'entries'); + if ($id === null) { + $this->_CacheTree->reset(); + } else { + $this->_CacheTree->delete($id); + } + } + } + + class SaitoCacheSupportCachelet implements CacheSupportCacheletInterface { + public function clear($id = null) { + Cache::clear(false, 'default'); + Cache::clear(false, 'short'); + } + } + + class ApcCacheSupportCachelet implements CacheSupportCacheletInterface { + public function clear($id = null) { + if (function_exists('apc_store')) { + apc_clear_cache(); + apc_clear_cache('user'); + apc_clear_cache('opcode'); + } + } + } + + class CakeCacheSupportCachelet implements CacheSupportCacheletInterface { + public function clear($id = null) { + Cache::clearGroup('persistent'); + Cache::clearGroup('models'); + Cache::clearGroup('views'); + } + } \ No newline at end of file diff --git a/app/Controller/Component/CacheTreeComponent.php b/app/Lib/CacheTree/CacheTree.php old mode 100755 new mode 100644 similarity index 76% rename from app/Controller/Component/CacheTreeComponent.php rename to app/Lib/CacheTree/CacheTree.php index 5f49d7b47..3df8f4fc0 --- a/app/Controller/Component/CacheTreeComponent.php +++ b/app/Lib/CacheTree/CacheTree.php @@ -1,14 +1,11 @@ _CurrentUser = $Controller->CurrentUser; $cache_config = Cache::settings(); - if ($cache_config['engine'] === 'File') { - $this->_CacheEngine = new CacheTreeDbCacheEngine; - } else { + if ($cache_config['engine'] === 'Apc') { $this->_CacheEngine = new CacheTreeAppCacheEngine; + } else { + $this->_CacheEngine = new CacheTreeDbCacheEngine; } if ( @@ -57,21 +65,6 @@ public function initialize(Controller $Controller) { $this->readCache(); } - public function beforeRedirect(Controller $Controller, $url, $status = null, $exit = true) { - parent::beforeRedirect($Controller, $url); - $this->saveCache(); - } - - public function beforeRender(Controller $Controller) { - parent::beforeRender($Controller); - $Controller->set('CacheTree', $this); - } - - public function shutdown(Controller $Controller) { - parent::shutdown($Controller); - $this->saveCache(); - } - public function isCacheUpdatable(array $entry) { if ( !$this->_allowUpdate ) { return false; @@ -90,7 +83,8 @@ public function isCacheValid(array $entry) { return $this->_validEntries[$entry['id']]; endif; - if ( isset($this->_cachedEntries[$entry['id']]) && strtotime($entry['last_answer']) <= $this->_cachedEntries[$entry['id']]['metadata']['content_last_updated']) { + if (isset($this->_cachedEntries[(int)$entry['id']]) && + strtotime($entry['last_answer']) <= $this->_cachedEntries[(int)$entry['id']]['metadata']['content_last_updated']) { if ($this->_isEntryOldForUser($entry)) { $isCacheValid = true; } @@ -110,7 +104,7 @@ protected function _isEntryOldForUser(array $entry) { public function delete($id) { $this->_isUpdated = TRUE; $this->readCache(); - unset($this->_cachedEntries[$id]); + unset($this->_cachedEntries[(int)$id]); } public function reset() { @@ -125,8 +119,8 @@ public function read($id = null) { return $this->_cachedEntries; } - if ( isset($this->_cachedEntries[$id]) ) { - return $this->_cachedEntries[$id]['content']; + if ( isset($this->_cachedEntries[(int)$id]) ) { + return $this->_cachedEntries[(int)$id]['content']; } return FALSE; @@ -148,11 +142,11 @@ public function update($id, $content, $timestamp = null) { $this->_isUpdated = TRUE; $this->readCache(); $metadata = array( - 'created' => $now, - 'content_last_updated' => $timestamp, + 'created' => $now, + 'content_last_updated' => $timestamp, ); $data = array( 'metadata' => $metadata, 'content' => $content ); - $this->_cachedEntries[$id] = $data; + $this->_cachedEntries[(int)$id] = $data; } public function readCache() { @@ -180,7 +174,7 @@ public function saveCache() { $this->_gc(); $this->_CacheEngine->write((array)$this->_cachedEntries); - } + } /** * Garbage collection @@ -195,13 +189,14 @@ protected function _gc() { if ( $number_of_cached_entries > $this->_maxNumberOfEntries ) { // descending time sort uasort($this->_cachedEntries, function($a, $b) { - if ($a['metadata']['content_last_updated'] == $b['metadata']['content_last_updated']) { - return 0; - } - return ($a['metadata']['content_last_updated'] < $b['metadata']['content_last_updated']) ? 1 : -1; + if ($a['metadata']['content_last_updated'] == $b['metadata']['content_last_updated']) { + return 0; + } + return ($a['metadata']['content_last_updated'] < $b['metadata']['content_last_updated']) ? 1 : -1; }); $this->_cachedEntries = array_slice($this->_cachedEntries, 0, $this->_maxNumberOfEntries, true); } } + } \ No newline at end of file diff --git a/app/Lib/CacheTree/CacheTreeAppCacheEngine.php b/app/Lib/CacheTree/CacheTreeAppCacheEngine.php index 7ccd2a7fe..49db7355b 100644 --- a/app/Lib/CacheTree/CacheTreeAppCacheEngine.php +++ b/app/Lib/CacheTree/CacheTreeAppCacheEngine.php @@ -1,8 +1,8 @@ assertEqual( - Router::baseURL() . $this->controller->request->webroot . $url, + Router::fullBaseUrl() . $this->controller->request->webroot . $url, $this->headers['Location'] ); } diff --git a/app/Lib/SaitoUser.php b/app/Lib/SaitoUser.php index 88413f6c2..5dc126b84 100755 --- a/app/Lib/SaitoUser.php +++ b/app/Lib/SaitoUser.php @@ -69,7 +69,7 @@ public function set($user) { if ( !empty($user) && is_array($user) ) : if (empty($user['id']) === false) { - $this->_id = $user['id']; + $this->_id = (int)$user['id']; $this->_isLoggedIn = true; } $this->_settings = $user; diff --git a/app/Lib/UserCategories.php b/app/Lib/UserCategories.php new file mode 100644 index 000000000..6ca4e441e --- /dev/null +++ b/app/Lib/UserCategories.php @@ -0,0 +1,77 @@ +_user = $user; + $this->_categories = $categories; + } + + protected function _isAll() { + return (int)$this->_user['user_category_active'] === -1; + } + + protected function _isSingle() { + return (int)$this->_user['user_category_active'] > 0; + } + + protected function _isCustom() { + return (int)$this->_user['user_category_active'] == 0 && + empty($this->_user['user_category_custom']) === false; + } + + protected function _filterOutNonExisting($categories) { + return array_intersect_key($categories, $this->_categories); + } + + protected function _getCustom() { + // add new categories to custom set + // + // [4 => true, 7 => '0'] + [4 => '4', 7 => '7', 13 => '13'] + // becomes + // [4 => true, 7 => '0', 13 => '13'] + // with 13 => '13' trueish + $custom = $this->_user['user_category_custom'] + $this->_categories; + + // then filter for zeros to get only the user categories + // [4 => true, 13 => '13'] + $custom = array_filter($custom); + + $custom = $this->_filterOutNonExisting($custom); + + $keys = array_keys($custom); + return array_combine($keys, $keys); + } + + /** + * @return array + * + * $categories: array with categories for the active type [cat_id1, cat_id2, …] + * $type: active type: 'all', 'single' or 'custom' + * $custom: categories for 'custom' [cat_id1, cat_id2, …] + */ + public function get() { + $custom = $this->_getCustom(); + if ($this->_isSingle()) { + $type = 'single'; + $categories = $this->_filterOutNonExisting( + [$this->_user['user_category_active'] => $this->_user['user_category_active']] + ); + } elseif ($this->_isCustom()) { + $type = 'custom'; + $categories = $custom; + } else { + $type = 'all'; + $categories = $this->_categories; + } + return [$categories, $type, $custom]; + } + } + diff --git a/app/Locale/deu/LC_MESSAGES/default.po b/app/Locale/deu/LC_MESSAGES/default.po index 7efef771f..acb263f76 100644 --- a/app/Locale/deu/LC_MESSAGES/default.po +++ b/app/Locale/deu/LC_MESSAGES/default.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-05-30 12:55+0100\n" -"PO-Revision-Date: 2013-06-06 19:45+0100\n" +"POT-Creation-Date: 2013-08-12 09:09+0100\n" +"PO-Revision-Date: 2013-08-12 09:09+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: de_DE\n" @@ -14,7 +14,7 @@ msgstr "" "X-Poedit-Basepath: /Users/siezi/Sites/private/personal_projects/" "macnemo_2_github/app/\n" "X-Poedit-SourceCharset: UTF-8\n" -"X-Generator: Poedit 1.5.5\n" +"X-Generator: Poedit 1.5.7\n" "X-Poedit-SearchPath-0: .\n" #: Plugin/Install/View/Install/adminuser.ctp:2 @@ -98,8 +98,8 @@ msgstr "Einträge" msgid "User Registrations" msgstr "Benutzerregistrierungen" -#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:323 -#: Controller/EntriesController.php:371 +#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:298 +#: Controller/EntriesController.php:351 msgid "back_to_overview_linkname" msgstr "Zurück zur Übersicht" @@ -124,6 +124,7 @@ msgid "Bookmarks" msgstr "Lesezeichen" #: View/Bookmarks/index.ctp:24 View/Categories/view.ctp:64 +#: View/Entries/add.ctp:92 msgid "Subject" msgstr "Betreff" @@ -157,9 +158,9 @@ msgstr "Absenden" #: View/Categories/add.ctp:16 View/Categories/admin_index.ctp:16 #: View/Categories/edit.ctp:17 View/Categories/index.ctp:11 #: View/Categories/index.ctp:51 View/Categories/view.ctp:37 -#: View/Categories/view.ctp:73 View/Settings/admin_index.ctp:7 -#: View/SmileyCodes/admin_add.ctp:14 View/SmileyCodes/admin_index.ctp:12 -#: View/Smilies/admin_add.ctp:18 View/Smilies/admin_index.ctp:14 +#: View/Categories/view.ctp:73 View/SmileyCodes/admin_add.ctp:14 +#: View/SmileyCodes/admin_index.ctp:12 View/Smilies/admin_add.ctp:18 +#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:67 msgid "Actions" msgstr "Aktion" @@ -238,7 +239,7 @@ msgid "New Category" msgstr "Neue Kategorie" #: View/Categories/admin_index.ctp:34 View/Categories/index.ctp:30 -#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:230 +#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:37 #: View/SmileyCodes/admin_index.ctp:29 View/Smilies/admin_index.ctp:31 #: View/Users/view.ctp:190 msgid "Edit" @@ -252,7 +253,7 @@ msgid "Are you sure you want to delete # %s?" msgstr "# %s wirklich löschen?" #: View/Categories/index.ctp:29 View/Categories/view.ctp:106 -#: View/Settings/admin_index.ctp:254 +#: View/Settings/admin_index.ctp:43 msgid "View" msgstr "Anzeigen" @@ -390,7 +391,7 @@ msgstr "" "einzelne Kategorie ohne Auswahländerung aktivieren." #: View/Elements/entry/category-chooser.ctp:42 View/Entries/search.ctp:82 -#: Controller/EntriesController.php:889 +#: Controller/EntriesController.php:927 msgid "All Categories" msgstr "Alle Kategorien" @@ -445,7 +446,7 @@ msgstr "Antworten" #: View/Elements/entry/view_posting.ctp:76 #: View/Elements/entry/view_posting.ctp:104 -#: Controller/EntriesController.php:479 +#: Controller/EntriesController.php:457 msgid "edit_linkname" msgstr "Bearbeiten" @@ -541,12 +542,12 @@ msgstr "Keine Hilfe für diese Seite verfügbar." msgid "search_submit" msgstr "Suchen" -#: View/Elements/layout/slidetab_recententries.ctp:10 +#: View/Elements/layout/slidetab_recententries.ctp:9 #, fuzzy msgid "Recent entries" msgstr "Letzte Einträge" -#: View/Elements/layout/slidetab_recentposts.ctp:12 View/Users/view.ctp:229 +#: View/Elements/layout/slidetab_recentposts.ctp:13 View/Users/view.ctp:229 msgid "user_recentposts" msgstr "Letzte Beiträge" @@ -629,38 +630,42 @@ msgstr "" "Um Ihr Benutzerkonto zu aktivieren, besuchen Sie bitte folgenden Verweis " "innerhalb der nächsten 24 Stunden: %s" -#: View/Entries/add.ctp:31 View/Entries/add.ctp:220 +#: View/Entries/add.ctp:34 View/Entries/add.ctp:190 msgid "preview" msgstr "Vorschau" -#: View/Entries/add.ctp:79 Controller/UsersController.php:485 +#: View/Entries/add.ctp:88 Controller/UsersController.php:477 msgid "error_subject_empty" msgstr "Betreff darf nicht leer sein." -#: View/Entries/add.ctp:116 +#: View/Entries/add.ctp:89 +msgid "error_subject_max_length" +msgstr "Betreff ist zu lang." + +#: View/Entries/add.ctp:133 msgid "Cite" msgstr "Zitieren" -#: View/Entries/add.ctp:138 +#: View/Entries/add.ctp:158 View/Entries/add.ctp:178 View/Entries/add.ctp:292 +msgid "submit_button" +msgstr "Eintragen" + +#: View/Entries/add.ctp:205 msgid "Notify on reply" msgstr "Beitrag abonnieren" -#: View/Entries/add.ctp:146 +#: View/Entries/add.ctp:219 msgid "Notify on thread replies" msgstr "Thread abonnieren" -#: View/Entries/add.ctp:153 View/Helper/EntryHHelper.php:159 +#: View/Entries/add.ctp:228 View/Helper/EntryHHelper.php:159 msgid "entry_nsfw_title" msgstr "Nicht bürosicher (NBS)" -#: View/Entries/add.ctp:161 +#: View/Entries/add.ctp:241 msgid "entry_flattr_this_posting" msgstr "Flattr-Knopf anzeigen" -#: View/Entries/add.ctp:200 View/Entries/add.ctp:209 View/Entries/add.ctp:239 -msgid "submit_button" -msgstr "Eintragen" - #: View/Entries/index.ctp:6 msgid "new_entry_linkname" msgstr "Neuer Eintrag" @@ -727,7 +732,7 @@ msgid "Overview" msgstr "Übersicht" #: View/Layouts/admin.ctp:41 View/Settings/admin_edit.ctp:2 -#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:11 +#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:113 #: View/Users/edit.ctp:163 msgid "Settings" msgstr "Einstellungen" @@ -757,415 +762,57 @@ msgstr "" msgid "RSS Feed" msgstr "RSS Feed" -#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:121 +#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:110 msgid "Last entries" msgstr "Letzte Einträge" -#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:117 +#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:106 msgid "Last started threads" msgstr "Letzte Threads" -#: View/Settings/admin_edit.ctp:27 View/Settings/admin_timezone.ctp:24 -msgid "_exp" -msgstr "" - -# View/Settings/admin_index.ctp: -#: View/Settings/admin_index.ctp:4 -msgid "Key" -msgstr "Schlüssel" - -# View/Settings/admin_index.ctp: -#: View/Settings/admin_index.ctp:5 -msgid "Value" -msgstr "Wert" - -# View/Settings/admin_index.ctp: -#: View/Settings/admin_index.ctp:6 -msgid "Explanation" -msgstr "Beschreibung" - -#: View/Settings/admin_index.ctp:12 +#: View/Settings/admin_index.ctp:7 msgid "Deactivate Forum" msgstr "Forum deaktivieren" -#: View/Settings/admin_index.ctp:17 -msgid "forum_disabled" -msgstr "Forum deaktivieren" - -#: View/Settings/admin_index.ctp:23 -msgid "forum_disabled_exp" -msgstr "Forum deaktivieren." - -#: View/Settings/admin_index.ctp:27 View/Settings/admin_index.ctp:46 -#: View/Settings/admin_index.ctp:70 View/Settings/admin_index.ctp:89 -#: View/Settings/admin_index.ctp:113 View/Settings/admin_index.ctp:140 -#: View/Settings/admin_index.ctp:159 View/Settings/admin_index.ctp:178 -#: View/Settings/admin_index.ctp:202 View/Settings/admin_index.ctp:221 -#: View/Settings/admin_index.ctp:245 View/Settings/admin_index.ctp:269 -#: View/Settings/admin_index.ctp:288 View/Settings/admin_index.ctp:307 -#: View/Settings/admin_index.ctp:326 View/Settings/admin_index.ctp:345 -#: View/Settings/admin_index.ctp:364 View/Settings/admin_index.ctp:383 -#: View/Settings/admin_index.ctp:402 View/Settings/admin_index.ctp:421 -#: View/Settings/admin_index.ctp:440 View/Settings/admin_index.ctp:459 -#: View/Settings/admin_index.ctp:478 View/Settings/admin_index.ctp:502 -#: View/Settings/admin_index.ctp:521 View/Settings/admin_index.ctp:545 -#: View/Settings/admin_index.ctp:564 View/Settings/admin_index.ctp:588 -#: View/Settings/admin_index.ctp:607 View/Settings/admin_index.ctp:630 -#: View/Settings/admin_index.ctp:649 View/Settings/admin_index.ctp:668 -#: View/Settings/admin_index.ctp:687 View/Settings/admin_index.ctp:697 -#: View/Settings/admin_index.ctp:716 -msgid "edit" -msgstr "Bearbeiten" - -#: View/Settings/admin_index.ctp:36 -msgid "forum_disabled_text" -msgstr "Forum deaktivieren Infotext" - -#: View/Settings/admin_index.ctp:42 -msgid "forum_disabled_text_exp" -msgstr "Infotext, welcher auf der Hinweisseite mit ausgegeben wird." - -#: View/Settings/admin_index.ctp:55 +#: View/Settings/admin_index.ctp:13 msgid "Base Preferences" msgstr "Grundeinstellungen" -#: View/Settings/admin_index.ctp:60 -msgid "forum_name" -msgstr "Forumtitel" - -#: View/Settings/admin_index.ctp:66 -msgid "forum_name_exp" -msgstr "Der Name dieses Forums." - -#: View/Settings/admin_index.ctp:79 -msgid "timezone" -msgstr "Zeitzone" - -#: View/Settings/admin_index.ctp:85 -msgid "timezone_exp" -msgstr "Zeitzone, indem sich das Forum befindet." - -#: View/Settings/admin_index.ctp:98 +#: View/Settings/admin_index.ctp:19 #, fuzzy msgid "Email" msgstr "E-Mail" -#: View/Settings/admin_index.ctp:103 -#, fuzzy -msgid "forum_email" -msgstr "Email" - -#: View/Settings/admin_index.ctp:109 -#, fuzzy -msgid "forum_email_exp" -msgstr "" -"Emailadresse, welche als Absenderadresse für vom Forum versendet Emails " -"(bspw. bei der Registrierung) verwendet wird. " - -#: View/Settings/admin_index.ctp:122 -msgid "email_admin_config_exp" -msgstr "" -"Erweiterte Email-Einstellungen sind durch das Anlegen einer $saito-Konfiguration in app/config/email.php möglich. Als " -"Vorlage sollte app/config/email.php.default verwendet werden. " -"Siehe auch." - -#: View/Settings/admin_index.ctp:125 +#: View/Settings/admin_index.ctp:25 msgid "Moderation" msgstr "Moderation" -# -#: View/Settings/admin_index.ctp:130 -msgid "block_user_ui" -msgstr "Nutzer sperren" - -# -#: View/Settings/admin_index.ctp:136 -msgid "block_user_ui_exp" -msgstr "Moderatoren dürfen Benutzer sperren." - -#: View/Settings/admin_index.ctp:149 -msgid "store_ip" -msgstr "IP speichern" - -#: View/Settings/admin_index.ctp:155 -msgid "store_ip_exp" -msgstr "" -"Beim Einträgen ins Forum werden die IP-Adresse des Eintragenden gespeichert. " -"Moderatoren wird die Adresse im Eintrag angezeigt." - -#: View/Settings/admin_index.ctp:168 -msgid "store_ip_anonymized" -msgstr "IP anonymisieren" - -#: View/Settings/admin_index.ctp:174 -msgid "store_ip_anonymized_exp" -msgstr "Anonymisieren der gespeicherten IP-Adresse." - -#: View/Settings/admin_index.ctp:187 +#: View/Settings/admin_index.ctp:31 #, fuzzy msgid "Registration" msgstr "Registrierung" -#: View/Settings/admin_index.ctp:192 -#, fuzzy -msgid "tos_enabled" -msgstr "Nutzungsbedingungen aktivieren" - -#: View/Settings/admin_index.ctp:198 -#, fuzzy -msgid "tos_enabled_exp" -msgstr "" -"Setzt einen Verweis und eine zu bestätigende Checkbox für die " -"Nutzungsbedingungen auf die Registrierungsseite." - -#: View/Settings/admin_index.ctp:211 -msgid "tos_url" -msgstr "Nutzungsbedingungen-URL" - -#: View/Settings/admin_index.ctp:217 -#, fuzzy -msgid "tos_url_exp" -msgstr "" -"URL zu den Nutzungsbedingen. Falls leer, werden die eingebauten " -"Standardseiten verwendet." - -# -#: View/Settings/admin_index.ctp:235 -msgid "edit_period" -msgstr "Nachbearbeitungszeit" - -# -#: View/Settings/admin_index.ctp:241 -msgid "edit_period_exp" -msgstr "" -"Minuten, die ein neuer Eintrag nach dem Anlegen nachbearbeitet werden kann." - -# -#: View/Settings/admin_index.ctp:259 -msgid "edit_delay" -msgstr "Nachbearbeiten anzeigen" - -# -#: View/Settings/admin_index.ctp:265 -msgid "edit_delay_exp" -msgstr "" -"Minuten, die ein Eintrag nach dem Anlegen ohne Bearbeitungshinweis " -"nachbearbeitet werden kann." - -#: View/Settings/admin_index.ctp:278 -msgid "topics_per_page" -msgstr "Threads pro Seite" - -#: View/Settings/admin_index.ctp:284 -msgid "topics_per_page_exp" -msgstr "Anzahl der Threads, die auf der Startseite angezeigt werden." - -#: View/Settings/admin_index.ctp:297 -msgid "thread_depth_indent" -msgstr "Einrücktiefe Threads" - -#: View/Settings/admin_index.ctp:303 -msgid "thread_depth_indent_exp" -msgstr "" -"Ab dieser Tiefe werden werden Einträge in der Thread-Ansicht nicht weiter " -"nach rechts eingerückt dargestellt." - -#: View/Settings/admin_index.ctp:316 -msgid "autolink" -msgstr "Autolink" - -#: View/Settings/admin_index.ctp:322 -msgid "autolink_exp" -msgstr "Verweise im Text automatisch erkennen und verlinken." - -#: View/Settings/admin_index.ctp:335 -msgid "bbcode_img" -msgstr "Bilder" - -#: View/Settings/admin_index.ctp:341 -msgid "bbcode_img_exp" -msgstr "Einbinden von Bildern erlauben." - -#: View/Settings/admin_index.ctp:354 -msgid "quote_symbol" -msgstr "Zitatzeichen" - -#: View/Settings/admin_index.ctp:360 -msgid "quote_symbol_exp" -msgstr "Zitatzeichen" - -#: View/Settings/admin_index.ctp:373 -msgid "signature_separator" -msgstr "Signaturtrenner" - -#: View/Settings/admin_index.ctp:379 -msgid "signature_separator_exp" -msgstr "Signaturtrenner" - -#: View/Settings/admin_index.ctp:392 -msgid "subject_maxlength" -msgstr "Betrefflänge" - -#: View/Settings/admin_index.ctp:398 -msgid "subject_maxlength_exp" -msgstr "Maximale Zeichenanzahl in Betreff." - -#: View/Settings/admin_index.ctp:411 -msgid "text_word_maxlength" -msgstr "Wordlänge" - -#: View/Settings/admin_index.ctp:417 -msgid "text_word_maxlength_exp" -msgstr "Maximale Zeichenanzahl pro Wort." - -#: View/Settings/admin_index.ctp:430 -msgid "userranks_show" -msgstr "Ränge anzeigen" - -#: View/Settings/admin_index.ctp:436 -msgid "userranks_show_exp" -msgstr "Benutzerränge anzeigen." - -#: View/Settings/admin_index.ctp:449 -msgid "userranks_ranks" -msgstr "Ränge" - -#: View/Settings/admin_index.ctp:455 -msgid "userranks_ranks_exp" -msgstr "Benutzerrang basierend auf Anzahl der gemachten Beiträge." - -# -#: View/Settings/admin_index.ctp:468 -msgid "video_domains_allowed" -msgstr "Einbettbare (Video-) Quellen" - -# -#: View/Settings/admin_index.ctp:474 -msgid "video_domains_allowed_exp" -msgstr "" -"Domainnamen einbindbar für Video und iFrame. Ein Asterisk * bedeuted freies " -"Einbinden (Vorsicht, dies kann ebenfalls zum Einbinden von Schadsoftware " -"genutzt werden!)." - -#: View/Settings/admin_index.ctp:487 +#: View/Settings/admin_index.ctp:61 #, fuzzy msgid "Category Chooser" msgstr "Kategorienfilter" -#: View/Settings/admin_index.ctp:492 -msgid "category_chooser_global" -msgstr "Standard" - -#: View/Settings/admin_index.ctp:498 -msgid "category_chooser_global_exp" -msgstr "Kategorienfilter auf der Frontseite immer anzeigen." - -#: View/Settings/admin_index.ctp:511 -msgid "category_chooser_user_override" -msgstr "Benutzereinstellung" - -#: View/Settings/admin_index.ctp:517 -msgid "category_chooser_user_override_exp" -msgstr "" -"Erlaubt einem Benutzern den Kategorienfilter in seinen Einstellungen zu " -"aktivieren, selbst wenn der Filter standardmässig deaktiviert ist." - -#: View/Settings/admin_index.ctp:535 -msgid "shoutbox_enabled" -msgstr "Aktivieren" - -#: View/Settings/admin_index.ctp:541 -msgid "shoutbox_enabled_exp" -msgstr "Shoutbox aktivieren" - -#: View/Settings/admin_index.ctp:554 -msgid "shoutbox_max_shouts" -msgstr "Anzahl" - -#: View/Settings/admin_index.ctp:560 -msgid "shoutbox_max_shouts_exp" -msgstr "Anzahl der angezeigten Shoutboxeinträge" - -#: View/Settings/admin_index.ctp:578 -msgid "upload_max_img_size" -msgstr "Uploadgröße maximal" - -#: View/Settings/admin_index.ctp:584 -msgid "upload_max_img_size_exp" -msgstr "Maximale Größe, die ein Upload besitzen darf in kB." - -#: View/Settings/admin_index.ctp:597 -msgid "upload_max_number_of_uploads" -msgstr "Anzahl an Uploads pro Benutzer" - -#: View/Settings/admin_index.ctp:603 -msgid "upload_max_number_of_uploads_exp" -msgstr "" -"Maximale Anzahl von Dateien, die ein einzelner Benutzer gleichzeitig auf dem " -"Server hochgeladen haben kann." - -#: View/Settings/admin_index.ctp:620 -msgid "flattr_enabled" -msgstr "Flattr aktivieren" - -#: View/Settings/admin_index.ctp:626 -msgid "flattr_enabled_exp" -msgstr "Ermöglicht es Benutzern flattr zu verwenden." - -#: View/Settings/admin_index.ctp:639 -msgid "flattr_language" -msgstr "Flattr-Sprache" - -#: View/Settings/admin_index.ctp:645 -msgid "flattr_language_exp" -msgstr "" -"Sprache, die flattr den hereinzeigenden Verweisen zuordnet. Siehe Doku auf " -"flattr.com für Kürzel." - -#: View/Settings/admin_index.ctp:658 -msgid "flattr_category" -msgstr "Flattr-Kategorie" - -#: View/Settings/admin_index.ctp:664 -msgid "flattr_category_exp" +#: View/Settings/admin_index.ctp:67 +msgid "Shoutbox" msgstr "" -"Kategorie, die Flattr den hereinzeigenden Verweisen zuordnet. Siehe flattr." -"com für verfügbare Kategorien." -#: View/Settings/admin_index.ctp:683 -msgid "embedly_enabled" -msgstr "Embed.ly aktivieren" - -#: View/Settings/admin_index.ctp:685 -msgid "embedly_enabled_exp" -msgstr "" -"Aktiviert den [embed]-BBcode, welcher seinen Inhalt durch Embed.ly darstellt." - -#: View/Settings/admin_index.ctp:693 -msgid "embedly_key" -msgstr "Embed.ly Key" - -# -#: View/Settings/admin_index.ctp:695 -msgid "embedly_key_exp" -msgstr "API-Key, den man nach der Registrierung bei Embed.ly erhält." +#: View/Settings/admin_index.ctp:73 +#, fuzzy +msgid "Uploads" +msgstr "Hochladen" -#: View/Settings/admin_index.ctp:706 +#: View/Settings/admin_index.ctp:93 msgid "Debug" msgstr "" -#: View/Settings/admin_index.ctp:712 -msgid "stopwatch_get" -msgstr "Stopuhr" - -#: View/Settings/admin_index.ctp:714 -#, fuzzy -msgid "stopwatch_get_exp" +#: View/Settings/admin_timezone.ctp:24 +msgid "_exp" msgstr "" -"Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ an " -"URL." #: View/SmileyCodes/admin_add.ctp:4 #, fuzzy @@ -1574,7 +1221,7 @@ msgid "register_success_content" msgstr "" "Ihre Registrierung war erfolgreich. Sie können sich nun einloggen. Viel Spaß!" -#: View/Users/register.ctp:23 Controller/EntriesController.php:328 +#: View/Users/register.ctp:23 Controller/EntriesController.php:305 msgid "js-required" msgstr "Zum Nutzen des Forums bitte JavaScript aktivieren." @@ -1698,55 +1345,51 @@ msgstr "Flattr-Kategorie" msgid "Category deleted." msgstr "Kategorie" -#: Controller/EntriesController.php:103 +#: Controller/EntriesController.php:88 msgid "page" msgstr "Seite" -#: Controller/EntriesController.php:204 Controller/EntriesController.php:213 +#: Controller/EntriesController.php:187 Controller/EntriesController.php:196 msgid "Invalid post" msgstr "Posting konnte nicht gefunden werden." -#: Controller/EntriesController.php:255 +#: Controller/EntriesController.php:238 msgid "new_entry_linktitle" msgstr "Neuen Eintrag verfassen" -#: Controller/EntriesController.php:260 -msgid "logged_in_users_only" -msgstr "Nur für eingeloggte Benutzer." - -#: Controller/EntriesController.php:318 Controller/EntriesController.php:436 +#: Controller/EntriesController.php:293 Controller/EntriesController.php:414 msgid "Something clogged the tubes. Could not save entry. Try again." msgstr "" -#: Controller/EntriesController.php:364 Controller/EntriesController.php:476 +#: Controller/EntriesController.php:344 Controller/EntriesController.php:454 msgid "back_to_posting_from_linkname" msgstr "zurück zum Posting von %s" -#: Controller/EntriesController.php:375 +#: Controller/EntriesController.php:355 msgid "answer_marking" msgstr "Antwort verfassen" -#: Controller/EntriesController.php:442 +#: Controller/EntriesController.php:420 msgid "notice_you_are_editing_as_mod" msgstr "Achtung: Die folgende Aktion wird als Moderator vorgenommen!" -#: Controller/EntriesController.php:508 +#: Controller/EntriesController.php:485 msgid "delete_tree_success" msgstr "Thread erfolgreich entfernt." -#: Controller/EntriesController.php:511 +#: Controller/EntriesController.php:488 msgid "delete_subtree_success" msgstr "Subthread erfolgreich entfernt." -#: Controller/EntriesController.php:515 +#: Controller/EntriesController.php:492 msgid "delete_tree_error" msgstr "Thread konnte nicht gelöscht werden." -#: Controller/EntriesController.php:732 +#: Controller/EntriesController.php:707 msgid "Error" msgstr "" -#: Controller/EntriesController.php:925 +#: Controller/EntriesController.php:924 msgid "Custom" msgstr "Angepasst" @@ -1817,7 +1460,7 @@ msgstr "" msgid "Smily was not deleted" msgstr "" -#: Controller/ToolsController.php:30 +#: Controller/ToolsController.php:21 msgid "Caches have been emptied." msgstr "Caches wurden geleert." @@ -1825,98 +1468,98 @@ msgstr "Caches wurden geleert." msgid "upload_max_number_of_uploads_failed" msgstr "Maximale Anzahl an Uploads erreicht (%s)." -#: Controller/UsersController.php:56 Controller/UsersController.php:343 +#: Controller/UsersController.php:37 Controller/UsersController.php:329 #, php-format msgid "User %s is locked." msgstr "Benutzer %s wurde gesperrt." -#: Controller/UsersController.php:62 +#: Controller/UsersController.php:43 msgid "auth_loginerror" msgstr "Fehler beim Login!" -#: Controller/UsersController.php:112 +#: Controller/UsersController.php:92 msgid "register_email_subject" msgstr "Willkommen zu %s!" -#: Controller/UsersController.php:190 Controller/UsersController.php:212 -#: Controller/UsersController.php:248 +#: Controller/UsersController.php:172 Controller/UsersController.php:195 +#: Controller/UsersController.php:233 msgid "Invalid user" msgstr "Ungültiger Benutzer" -#: Controller/UsersController.php:239 +#: Controller/UsersController.php:224 msgid "User :name" msgstr "Benutzer :name" -#: Controller/UsersController.php:293 +#: Controller/UsersController.php:278 msgid "The user could not be saved. Please, try again." msgstr "Der Benutzer konnte nicht gespeichert werden." -#: Controller/UsersController.php:325 Controller/UsersController.php:362 +#: Controller/UsersController.php:311 Controller/UsersController.php:348 msgid "User not found." msgstr "Benutzer nicht gefunden." -#: Controller/UsersController.php:333 +#: Controller/UsersController.php:320 msgid "You can't lock yourself." msgstr "Man kann sich nicht selbst sperren." -#: Controller/UsersController.php:335 +#: Controller/UsersController.php:322 msgid "You can't lock administrators." msgstr "Administratoren können nicht gesperrt werden." -#: Controller/UsersController.php:345 +#: Controller/UsersController.php:331 #, php-format msgid "User %s is unlocked." msgstr "Benutzer %s wurde freigegeben." -#: Controller/UsersController.php:349 +#: Controller/UsersController.php:335 msgid "Error while un/locking." msgstr "" -#: Controller/UsersController.php:368 +#: Controller/UsersController.php:355 msgid "You can't delete yourself." msgstr "Man kann sich nicht selbst löschen." -#: Controller/UsersController.php:370 +#: Controller/UsersController.php:357 msgid "You can't delete the installation account." msgstr "Der Installationsbenutzer kann nicht gelöscht werden." -#: Controller/UsersController.php:372 +#: Controller/UsersController.php:359 #, php-format msgid "User %s deleted." msgstr "Nutzer %s gelöscht." -#: Controller/UsersController.php:375 +#: Controller/UsersController.php:363 msgid "Couldn't delete user." msgstr "" -#: Controller/UsersController.php:400 +#: Controller/UsersController.php:387 msgid "change_password_success" msgstr "Passwort erfolgreich geändert." -#: Controller/UsersController.php:463 +#: Controller/UsersController.php:455 msgid "error_email_not-valid" msgstr "Keine gültige Email-Adresse." -#: Controller/UsersController.php:510 +#: Controller/UsersController.php:502 msgid "Message was send." msgstr "Nachricht wurde versandt." -#: Controller/UsersController.php:513 +#: Controller/UsersController.php:507 #, fuzzy msgid "Message couldn't be send! " msgstr "Fehler, Nachricht konnte nicht versandt werden." -#: Controller/Component/CurrentUserComponent.php:258 +#: Controller/Component/CurrentUserComponent.php:308 msgid "auth_autherror" msgstr "Der Zugriff auf diese Funktion ist eingeschränkt!" -#: Controller/Component/EmailNotificationComponent.php:53 -#: Controller/Component/EmailNotificationComponent.php:85 +#: Controller/Component/EmailNotificationComponent.php:47 +#: Controller/Component/EmailNotificationComponent.php:79 #, php-format msgid "New reply to \"%s\"" msgstr "Neu Antwort zu \"%s\"" -#: Controller/Component/EmailNotificationComponent.php:114 +#: Controller/Component/EmailNotificationComponent.php:112 msgid "Successfull registration" msgstr "" @@ -1997,7 +1640,7 @@ msgstr "" msgid "Abend" msgstr "" -#: View/Helper/BbcodeHelper.php:566 +#: View/Helper/BbcodeHelper.php:580 #, fuzzy msgid "" "Your browser does not support HTML5 video. Please updgrade to a " @@ -2007,7 +1650,7 @@ msgstr "" "Ihr Browser unterstützt kein HTML5-Video. Bitte verwenden Sie einen modernen " "Browser." -#: View/Helper/BbcodeHelper.php:584 +#: View/Helper/BbcodeHelper.php:601 #, fuzzy msgid "" "Your browser does not support HTML5 audio. Please updgrade to a " @@ -2017,17 +1660,17 @@ msgstr "" "Ihr Browser unterstützt kein HTML5-Video. Bitte verwenden Sie einen modernen " "Browser." -#: View/Helper/BbcodeHelper.php:881 +#: View/Helper/BbcodeHelper.php:926 #, fuzzy msgid "[embed] tag not enabled." msgstr "Embed.ly aktivieren" -#: View/Helper/BbcodeHelper.php:971 +#: View/Helper/BbcodeHelper.php:1012 #, php-format msgid "Domain %s not allowed for embedding video." msgstr "Domain %s bei Videos nicht erlaubt." -#: View/Helper/BbcodeHelper.php:979 +#: View/Helper/BbcodeHelper.php:1020 msgid "Video domain is not allowed." msgstr "" @@ -2127,6 +1770,25 @@ msgstr "Hochladen" msgid "Media" msgstr "Einbinden" +#: View/Helper/SettingHelper.php:53 +msgid "edit" +msgstr "Bearbeiten" + +# View/Settings/admin_index.ctp: +#: View/Helper/SettingHelper.php:64 +msgid "Key" +msgstr "Schlüssel" + +# View/Settings/admin_index.ctp: +#: View/Helper/SettingHelper.php:65 +msgid "Value" +msgstr "Wert" + +# View/Settings/admin_index.ctp: +#: View/Helper/SettingHelper.php:66 +msgid "Explanation" +msgstr "Beschreibung" + #: View/Helper/TimeHHelper.php:100 View/Helper/TimeHHelper.php:119 msgid "yesterday" msgstr "Gestern" @@ -2215,6 +1877,267 @@ msgstr "" msgid "token test" msgstr "" +#~ msgid "forum_disabled" +#~ msgstr "Forum deaktivieren" + +#~ msgid "forum_disabled_exp" +#~ msgstr "Forum deaktivieren." + +#~ msgid "forum_disabled_text" +#~ msgstr "Forum deaktivieren Infotext" + +#~ msgid "forum_disabled_text_exp" +#~ msgstr "Infotext, welcher auf der Hinweisseite mit ausgegeben wird." + +#~ msgid "forum_name" +#~ msgstr "Forumtitel" + +#~ msgid "forum_name_exp" +#~ msgstr "Der Name dieses Forums." + +#~ msgid "timezone" +#~ msgstr "Zeitzone" + +#~ msgid "timezone_exp" +#~ msgstr "Zeitzone, indem sich das Forum befindet." + +#, fuzzy +#~ msgid "forum_email" +#~ msgstr "Email" + +#, fuzzy +#~ msgid "forum_email_exp" +#~ msgstr "" +#~ "Emailadresse, welche als Absenderadresse für vom Forum versendet Emails " +#~ "(bspw. bei der Registrierung) verwendet wird. " + +#~ msgid "email_admin_config_exp" +#~ msgstr "" +#~ "Erweiterte Email-Einstellungen sind durch das Anlegen einer $saito-Konfiguration in app/config/email.php möglich. Als " +#~ "Vorlage sollte app/config/email.php.default verwendet " +#~ "werden. Siehe auch." + +# +#~ msgid "block_user_ui" +#~ msgstr "Nutzer sperren" + +# +#~ msgid "block_user_ui_exp" +#~ msgstr "Moderatoren dürfen Benutzer sperren." + +#~ msgid "store_ip" +#~ msgstr "IP speichern" + +#~ msgid "store_ip_exp" +#~ msgstr "" +#~ "Beim Einträgen ins Forum werden die IP-Adresse des Eintragenden " +#~ "gespeichert. Moderatoren wird die Adresse im Eintrag angezeigt." + +#~ msgid "store_ip_anonymized" +#~ msgstr "IP anonymisieren" + +#~ msgid "store_ip_anonymized_exp" +#~ msgstr "Anonymisieren der gespeicherten IP-Adresse." + +#, fuzzy +#~ msgid "tos_enabled" +#~ msgstr "Nutzungsbedingungen aktivieren" + +#, fuzzy +#~ msgid "tos_enabled_exp" +#~ msgstr "" +#~ "Setzt einen Verweis und eine zu bestätigende Checkbox für die " +#~ "Nutzungsbedingungen auf die Registrierungsseite." + +#~ msgid "tos_url" +#~ msgstr "Nutzungsbedingungen-URL" + +#, fuzzy +#~ msgid "tos_url_exp" +#~ msgstr "" +#~ "URL zu den Nutzungsbedingen. Falls leer, werden die eingebauten " +#~ "Standardseiten verwendet." + +# +#~ msgid "edit_period" +#~ msgstr "Nachbearbeitungszeit" + +# +#~ msgid "edit_period_exp" +#~ msgstr "" +#~ "Minuten, die ein neuer Eintrag nach dem Anlegen nachbearbeitet werden " +#~ "kann." + +# +#~ msgid "edit_delay" +#~ msgstr "Nachbearbeiten anzeigen" + +# +#~ msgid "edit_delay_exp" +#~ msgstr "" +#~ "Minuten, die ein Eintrag nach dem Anlegen ohne Bearbeitungshinweis " +#~ "nachbearbeitet werden kann." + +#~ msgid "topics_per_page" +#~ msgstr "Threads pro Seite" + +#~ msgid "topics_per_page_exp" +#~ msgstr "Anzahl der Threads, die auf der Startseite angezeigt werden." + +#~ msgid "thread_depth_indent" +#~ msgstr "Einrücktiefe Threads" + +#~ msgid "thread_depth_indent_exp" +#~ msgstr "" +#~ "Ab dieser Tiefe werden werden Einträge in der Thread-Ansicht nicht weiter " +#~ "nach rechts eingerückt dargestellt." + +#~ msgid "autolink" +#~ msgstr "Autolink" + +#~ msgid "autolink_exp" +#~ msgstr "Verweise im Text automatisch erkennen und verlinken." + +#~ msgid "bbcode_img" +#~ msgstr "Bilder" + +#~ msgid "bbcode_img_exp" +#~ msgstr "Einbinden von Bildern erlauben." + +#~ msgid "quote_symbol" +#~ msgstr "Zitatzeichen" + +#~ msgid "quote_symbol_exp" +#~ msgstr "Zitatzeichen" + +#~ msgid "signature_separator" +#~ msgstr "Signaturtrenner" + +#~ msgid "signature_separator_exp" +#~ msgstr "Signaturtrenner" + +#~ msgid "subject_maxlength_exp" +#~ msgstr "Maximale Zeichenanzahl in Betreff." + +#~ msgid "text_word_maxlength" +#~ msgstr "Wordlänge" + +#~ msgid "text_word_maxlength_exp" +#~ msgstr "Maximale Zeichenanzahl pro Wort." + +#~ msgid "userranks_show" +#~ msgstr "Ränge anzeigen" + +#~ msgid "userranks_show_exp" +#~ msgstr "Benutzerränge anzeigen." + +#~ msgid "userranks_ranks" +#~ msgstr "Ränge" + +#~ msgid "userranks_ranks_exp" +#~ msgstr "Benutzerrang basierend auf Anzahl der gemachten Beiträge." + +# +#~ msgid "video_domains_allowed" +#~ msgstr "Einbettbare (Video-) Quellen" + +# +#~ msgid "video_domains_allowed_exp" +#~ msgstr "" +#~ "Domainnamen einbindbar für Video und iFrame. Ein Asterisk * bedeuted " +#~ "freies Einbinden (Vorsicht, dies kann ebenfalls zum Einbinden von " +#~ "Schadsoftware genutzt werden!)." + +#~ msgid "category_chooser_global" +#~ msgstr "Standard" + +#~ msgid "category_chooser_global_exp" +#~ msgstr "Kategorienfilter auf der Frontseite immer anzeigen." + +#~ msgid "category_chooser_user_override" +#~ msgstr "Benutzereinstellung" + +#~ msgid "category_chooser_user_override_exp" +#~ msgstr "" +#~ "Erlaubt einem Benutzern den Kategorienfilter in seinen Einstellungen zu " +#~ "aktivieren, selbst wenn der Filter standardmässig deaktiviert ist." + +#~ msgid "shoutbox_enabled" +#~ msgstr "Aktivieren" + +#~ msgid "shoutbox_enabled_exp" +#~ msgstr "Shoutbox aktivieren" + +#~ msgid "shoutbox_max_shouts" +#~ msgstr "Anzahl" + +#~ msgid "shoutbox_max_shouts_exp" +#~ msgstr "Anzahl der angezeigten Shoutboxeinträge" + +#~ msgid "upload_max_img_size" +#~ msgstr "Uploadgröße maximal" + +#~ msgid "upload_max_img_size_exp" +#~ msgstr "Maximale Größe, die ein Upload besitzen darf in kB." + +#~ msgid "upload_max_number_of_uploads" +#~ msgstr "Anzahl an Uploads pro Benutzer" + +#~ msgid "upload_max_number_of_uploads_exp" +#~ msgstr "" +#~ "Maximale Anzahl von Dateien, die ein einzelner Benutzer gleichzeitig auf " +#~ "dem Server hochgeladen haben kann." + +#~ msgid "flattr_enabled" +#~ msgstr "Flattr aktivieren" + +#~ msgid "flattr_enabled_exp" +#~ msgstr "Ermöglicht es Benutzern flattr zu verwenden." + +#~ msgid "flattr_language" +#~ msgstr "Flattr-Sprache" + +#~ msgid "flattr_language_exp" +#~ msgstr "" +#~ "Sprache, die flattr den hereinzeigenden Verweisen zuordnet. Siehe Doku " +#~ "auf flattr.com für Kürzel." + +#~ msgid "flattr_category" +#~ msgstr "Flattr-Kategorie" + +#~ msgid "flattr_category_exp" +#~ msgstr "" +#~ "Kategorie, die Flattr den hereinzeigenden Verweisen zuordnet. Siehe " +#~ "flattr.com für verfügbare Kategorien." + +#~ msgid "embedly_enabled" +#~ msgstr "Embed.ly aktivieren" + +#~ msgid "embedly_enabled_exp" +#~ msgstr "" +#~ "Aktiviert den [embed]-BBcode, welcher seinen Inhalt durch Embed.ly " +#~ "darstellt." + +#~ msgid "embedly_key" +#~ msgstr "Embed.ly Key" + +# +#~ msgid "embedly_key_exp" +#~ msgstr "API-Key, den man nach der Registrierung bei Embed.ly erhält." + +#~ msgid "stopwatch_get" +#~ msgstr "Stopuhr" + +#~ msgid "stopwatch_get_exp" +#~ msgstr "" +#~ "Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ " +#~ "an URL in Production-Modus für eingeloggte Nutzer." + +#~ msgid "logged_in_users_only" +#~ msgstr "Nur für eingeloggte Benutzer." + #~ msgid "user_edit_success" #~ msgstr "Die Daten wurden gespeichert." diff --git a/app/Locale/deu/LC_MESSAGES/nondynamic.po b/app/Locale/deu/LC_MESSAGES/nondynamic.po index f97ac07cb..eb3049a5f 100644 --- a/app/Locale/deu/LC_MESSAGES/nondynamic.po +++ b/app/Locale/deu/LC_MESSAGES/nondynamic.po @@ -91,3 +91,287 @@ msgstr "Hinweis" # msgid "success" msgstr "Erfolg" + +#******************************************************************************* +#* App Settings Start +#******************************************************************************/ + +# +msgid "forum_disabled" +msgstr "Forum deaktivieren" + +# +msgid "forum_disabled_exp" +msgstr "Forum deaktivieren." + +# +msgid "forum_disabled_text" +msgstr "Forum deaktivieren Infotext" + +# +msgid "forum_disabled_text_exp" +msgstr "Infotext, welcher auf der Hinweisseite mit ausgegeben wird." + +# +msgid "forum_name" +msgstr "Forumtitel" + +# +msgid "forum_name_exp" +msgstr "Der Name dieses Forums." + +# +msgid "timezone" +msgstr "Zeitzone" + +# +msgid "timezone_exp" +msgstr "Zeitzone, indem sich das Forum befindet." + +# +msgid "forum_email" +msgstr "Email" + +# +msgid "forum_email_exp" +msgstr "Emailadresse, welche als Absenderadresse für vom Forum versendet Emails (bspw. bei der Registrierung) verwendet wird. " + +# +msgid "block_user_ui" +msgstr "Nutzer sperren" + +# +msgid "block_user_ui_exp" +msgstr "Moderatoren dürfen Benutzer sperren." + +# +msgid "store_ip" +msgstr "IP speichern" + +# +msgid "store_ip_exp" +msgstr "Beim Einträgen ins Forum werden die IP-Adresse des Eintragenden gespeichert. Moderatoren wird die Adresse im Eintrag angezeigt." + +# +msgid "store_ip_anonymized" +msgstr "IP anonymisieren" + +# +msgid "store_ip_anonymized_exp" +msgstr "Anonymisieren der gespeicherten IP-Adresse." + +# +msgid "tos_enabled" +msgstr "Nutzungsbedingungen aktivieren" + +# +msgid "tos_enabled_exp" +msgstr "Setzt einen Verweis und eine zu bestätigende Checkbox für die Nutzungsbedingungen auf die Registrierungsseite." + +# +msgid "tos_url" +msgstr "Nutzungsbedingungen-URL" + +# +msgid "tos_url_exp" +msgstr "URL zu den Nutzungsbedingen. Falls leer, werden die eingebauten Standardseiten verwendet." + +# +msgid "edit_period" +msgstr "Nachbearbeitungszeit" + +# +msgid "edit_period_exp" +msgstr "Minuten, die ein neuer Eintrag nach dem Anlegen nachbearbeitet werden kann." + +# +msgid "edit_delay" +msgstr "Nachbearbeiten anzeigen" + +# +msgid "edit_delay_exp" +msgstr "Minuten, die ein Eintrag nach dem Anlegen ohne Bearbeitungshinweis nachbearbeitet werden kann." + +# +msgid "topics_per_page" +msgstr "Threads pro Seite" + +# +msgid "topics_per_page_exp" +msgstr "Anzahl der Threads, die auf der Startseite angezeigt werden." + +# +msgid "thread_depth_indent" +msgstr "Einrücktiefe Threads" + +# +msgid "thread_depth_indent_exp" +msgstr "Ab dieser Tiefe werden werden Einträge in der Thread-Ansicht nicht weiter nach rechts eingerückt dargestellt." + +# +msgid "autolink" +msgstr "Autolink" + +# +msgid "autolink_exp" +msgstr "Verweise im Text automatisch erkennen und verlinken." + +# +msgid "bbcode_img" +msgstr "Bilder" + +# +msgid "bbcode_img_exp" +msgstr "Einbinden von Bildern erlauben." + +# +msgid "quote_symbol" +msgstr "Zitatzeichen" + +# +msgid "quote_symbol_exp" +msgstr "Zitatzeichen" + +# +msgid "signature_separator" +msgstr "Signaturtrenner" + +# +msgid "signature_separator_exp" +msgstr "Signaturtrenner" + +# +msgid "subject_maxlength" +msgstr "Betrefflänge" + +# +msgid "subject_maxlength_exp" +msgstr "Maximale Zeichenanzahl in Betreff." + +# +msgid "text_word_maxlength" +msgstr "Wordlänge" + +# +msgid "text_word_maxlength_exp" +msgstr "Maximale Zeichenanzahl pro Wort." + +# +msgid "userranks_show" +msgstr "Ränge anzeigen" + +# +msgid "userranks_show_exp" +msgstr "Benutzerränge anzeigen." + +# +msgid "userranks_ranks" +msgstr "Ränge" + +# +msgid "userranks_ranks_exp" +msgstr "Benutzerrang basierend auf Anzahl der gemachten Beiträge." + +# +msgid "video_domains_allowed" +msgstr "Einbettbare (Video-) Quellen" + +# +msgid "video_domains_allowed_exp" +msgstr "Domainnamen einbindbar für Video und iFrame. Ein Asterisk * bedeuted freies Einbinden (Vorsicht, dies kann ebenfalls zum Einbinden von Schadsoftware genutzt werden!)." + +# +msgid "category_chooser_global" +msgstr "Standard" + +# +msgid "category_chooser_global_exp" +msgstr "Kategorienfilter auf der Frontseite immer anzeigen." + +# +msgid "category_chooser_user_override" +msgstr "Benutzereinstellung" + +# +msgid "category_chooser_user_override_exp" +msgstr "Erlaubt einem Benutzern den Kategorienfilter in seinen Einstellungen zu aktivieren, selbst wenn der Filter standardmässig deaktiviert ist." + +# +msgid "shoutbox_enabled" +msgstr "Aktivieren" + +# +msgid "shoutbox_enabled_exp" +msgstr "Shoutbox aktivieren" + +# +msgid "shoutbox_max_shouts" +msgstr "Anzahl" + +# +msgid "shoutbox_max_shouts_exp" +msgstr "Anzahl der angezeigten Shoutboxeinträge" + +# +msgid "upload_max_img_size" +msgstr "Uploadgröße maximal" + +# +msgid "upload_max_img_size_exp" +msgstr "Maximale Größe, die ein Upload besitzen darf in kB." + +# +msgid "upload_max_number_of_uploads" +msgstr "Anzahl an Uploads pro Benutzer" + +# +msgid "upload_max_number_of_uploads_exp" +msgstr "Maximale Anzahl von Dateien, die ein einzelner Benutzer gleichzeitig auf dem Server hochgeladen haben kann." + +# +msgid "flattr_enabled" +msgstr "Flattr aktivieren" + +# +msgid "flattr_enabled_exp" +msgstr "Ermöglicht es Benutzern flattr zu verwenden." + +# +msgid "flattr_language" +msgstr "Flattr-Sprache" + +# +msgid "flattr_language_exp" +msgstr "Sprache, die flattr den hereinzeigenden Verweisen zuordnet. Siehe Doku auf flattr.com für Kürzel." + +# +msgid "flattr_category" +msgstr "Flattr-Kategorie" + +# +msgid "flattr_category_exp" +msgstr "Kategorie, die Flattr den hereinzeigenden Verweisen zuordnet. Siehe flattr.com für verfügbare Kategorien." + +# +msgid "embedly_enabled" +msgstr "Embed.ly aktivieren" + +# +msgid "embedly_enabled_exp" +msgstr "Aktiviert den [embed]-BBcode, welcher seinen Inhalt durch Embed.ly darstellt." + +# +msgid "embedly_key" +msgstr "Embed.ly Key" + +# +msgid "embedly_key_exp" +msgstr "API-Key, den man nach der Registrierung bei Embed.ly erhält." + +# +msgid "stopwatch_get" +msgstr "Stopuhr" + +# +msgid "stopwatch_get_exp" +msgstr "Ausgabe der Stopuhrzeiten durch Anfügen von /stopwatch:true/ an URL in Production-Modus für eingeloggte Nutzer." diff --git a/app/Locale/eng/LC_MESSAGES/default.po b/app/Locale/eng/LC_MESSAGES/default.po index 577ace188..50fdd1315 100644 --- a/app/Locale/eng/LC_MESSAGES/default.po +++ b/app/Locale/eng/LC_MESSAGES/default.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: Macnemo_2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-05 13:35+0100\n" -"PO-Revision-Date: 2013-03-08 12:16+0100\n" +"POT-Creation-Date: 2013-08-12 09:10+0100\n" +"PO-Revision-Date: 2013-08-12 09:10+0100\n" "Last-Translator: Schlaefer \n" "Language-Team: \n" "Language: en_US\n" @@ -15,7 +15,7 @@ msgstr "" "macnemo_2_github/app/\n" "'auth_autherror', true;X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n" -"X-Generator: Poedit 1.5.5\n" +"X-Generator: Poedit 1.5.7\n" "X-Poedit-SearchPath-0: .\n" #: Plugin/Install/View/Install/adminuser.ctp:2 @@ -64,12 +64,12 @@ msgstr "" #: Plugin/Install/View/Install/index.ctp:24 #, php-format -msgid "PHP version %s > 5.3" +msgid "PHP version %s > 5.4" msgstr "" #: Plugin/Install/View/Install/index.ctp:27 #, php-format -msgid "PHP version %s < 5.3" +msgid "PHP version %s < 5.4" msgstr "" #: Plugin/Install/View/Install/index.ctp:33 @@ -85,7 +85,7 @@ msgstr "" msgid "Empty Caches" msgstr "" -#: View/Admins/admin_stats.ctp:8 View/Layouts/admin.ctp:58 +#: View/Admins/admin_stats.ctp:8 View/Layouts/admin.ctp:53 msgid "Stats" msgstr "" @@ -98,8 +98,8 @@ msgstr "" msgid "User Registrations" msgstr "Registration" -#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:323 -#: Controller/EntriesController.php:375 +#: View/Bookmarks/edit.ctp:4 Controller/EntriesController.php:298 +#: Controller/EntriesController.php:351 msgid "back_to_overview_linkname" msgstr "Back to Overview" @@ -124,6 +124,7 @@ msgid "Bookmarks" msgstr "" #: View/Bookmarks/index.ctp:24 View/Categories/view.ctp:64 +#: View/Entries/add.ctp:92 msgid "Subject" msgstr "" @@ -134,7 +135,7 @@ msgstr "" #: View/Bookmarks/index.ctp:49 View/Categories/admin_index.ctp:39 #: View/Categories/edit.ctp:20 View/Categories/index.ctp:31 #: View/Categories/view.ctp:108 View/SmileyCodes/admin_index.ctp:30 -#: View/Smilies/admin_index.ctp:32 View/Users/view.ctp:192 +#: View/Smilies/admin_index.ctp:32 View/Users/view.ctp:200 msgid "Delete" msgstr "" @@ -150,16 +151,16 @@ msgstr "" #: View/Categories/add.ctp:13 View/Categories/admin_edit.ctp:26 #: View/Categories/edit.ctp:14 View/Settings/admin_edit.ctp:20 #: View/SmileyCodes/admin_edit.ctp:16 View/Smilies/admin_add.ctp:12 -#: View/Smilies/admin_edit.ctp:13 View/Users/contact.ctp:45 +#: View/Smilies/admin_edit.ctp:13 View/Users/contact.ctp:51 msgid "Submit" msgstr "" #: View/Categories/add.ctp:16 View/Categories/admin_index.ctp:16 #: View/Categories/edit.ctp:17 View/Categories/index.ctp:11 #: View/Categories/index.ctp:51 View/Categories/view.ctp:37 -#: View/Categories/view.ctp:73 View/Settings/admin_index.ctp:7 -#: View/SmileyCodes/admin_add.ctp:14 View/SmileyCodes/admin_index.ctp:12 -#: View/Smilies/admin_add.ctp:18 View/Smilies/admin_index.ctp:14 +#: View/Categories/view.ctp:73 View/SmileyCodes/admin_add.ctp:14 +#: View/SmileyCodes/admin_index.ctp:12 View/Smilies/admin_add.ctp:18 +#: View/Smilies/admin_index.ctp:14 View/Helper/SettingHelper.php:67 msgid "Actions" msgstr "" @@ -182,7 +183,7 @@ msgstr "" #: View/Categories/admin_add.ctp:1 View/Categories/admin_delete.ctp:1 #: View/Categories/admin_edit.ctp:1 View/Categories/admin_index.ctp:1 #: View/Categories/admin_index.ctp:3 View/Categories/index.ctp:2 -#: View/Layouts/admin.ctp:52 webroot/js/main-prod.js:79 +#: View/Layouts/admin.ctp:47 webroot/js/main-prod.js:100 #: webroot/js/views/categoryChooser.js:18 msgid "Categories" msgstr "" @@ -221,7 +222,7 @@ msgstr "" msgid "Make It So" msgstr "" -#: View/Categories/admin_delete.ctp:76 View/Users/admin_delete.ctp:54 +#: View/Categories/admin_delete.ctp:77 View/Users/admin_delete.ctp:55 msgid "Abort" msgstr "" @@ -236,9 +237,9 @@ msgid "New Category" msgstr "" #: View/Categories/admin_index.ctp:34 View/Categories/index.ctp:30 -#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:230 +#: View/Categories/view.ctp:107 View/Settings/admin_index.ctp:37 #: View/SmileyCodes/admin_index.ctp:29 View/Smilies/admin_index.ctp:31 -#: View/Users/view.ctp:182 +#: View/Users/view.ctp:190 msgid "Edit" msgstr "" @@ -250,7 +251,7 @@ msgid "Are you sure you want to delete # %s?" msgstr "" #: View/Categories/index.ctp:29 View/Categories/view.ctp:106 -#: View/Settings/admin_index.ctp:254 +#: View/Settings/admin_index.ctp:43 msgid "View" msgstr "" @@ -369,23 +370,23 @@ msgstr "" msgid "email_footer" msgstr "This email was send by %s (%s)." -#: View/Elements/entry/category-chooser.ctp:12 +#: View/Elements/entry/category-chooser.ctp:18 #, fuzzy msgid "category_chooser_context_exp" msgstr "" "Choose your custom categories.

Click on a name to quickly show this " "category without changing your preferences." -#: View/Elements/entry/category-chooser.ctp:36 View/Entries/search.ctp:82 -#: Controller/EntriesController.php:881 +#: View/Elements/entry/category-chooser.ctp:42 View/Entries/search.ctp:82 +#: Controller/EntriesController.php:927 msgid "All Categories" msgstr "" -#: View/Elements/entry/category-chooser.ctp:83 +#: View/Elements/entry/category-chooser.ctp:89 msgid "Apply" msgstr "" -#: View/Elements/entry/thread_cached_init.ctp:8 View/Entries/view.ctp:31 +#: View/Elements/entry/thread_cached_init.ctp:8 msgid "btn-showThreadInMixView" msgstr "mix" @@ -414,36 +415,36 @@ msgstr "" msgid "views_headline" msgstr "views" -#: View/Elements/entry/view_posting.ctp:59 +#: View/Elements/entry/view_posting.ctp:57 #: View/Elements/layout/header_login.ctp:38 -#: View/Elements/layout/header_login.ctp:43 View/Entries/index.ctp:19 -#: View/Entries/search.ctp:22 View/Users/view.ctp:155 +#: View/Elements/layout/header_login.ctp:43 View/Entries/index.ctp:21 +#: View/Entries/search.ctp:22 View/Users/view.ctp:163 msgid "Help" msgstr "" -#: View/Elements/entry/view_posting.ctp:60 +#: View/Elements/entry/view_posting.ctp:58 msgid "answering_forbidden_locked_shp" msgstr "Answering forbidden by moderator." -#: View/Elements/entry/view_posting.ctp:65 +#: View/Elements/entry/view_posting.ctp:63 msgid "forum_answer_linkname" msgstr "Answer" -#: View/Elements/entry/view_posting.ctp:79 -#: View/Elements/entry/view_posting.ctp:107 -#: Controller/EntriesController.php:481 +#: View/Elements/entry/view_posting.ctp:76 +#: View/Elements/entry/view_posting.ctp:104 +#: Controller/EntriesController.php:457 msgid "edit_linkname" msgstr "Edit" -#: View/Elements/entry/view_posting.ctp:147 +#: View/Elements/entry/view_posting.ctp:144 msgid "merge_tree_link" msgstr "Merge" -#: View/Elements/entry/view_posting.ctp:158 +#: View/Elements/entry/view_posting.ctp:155 msgid "delete_tree_link" msgstr "Delete" -#: View/Elements/entry/view_posting.ctp:161 +#: View/Elements/entry/view_posting.ctp:158 msgid "delete_tree_link_confirm_message" msgstr "" "Deleting a (sub)thread can't be undone! Are you sure you want to delete the " @@ -456,7 +457,7 @@ msgstr "" #: View/Elements/layout/disclaimer.ctp:7 #: View/Themed/Macnemo/Elements/layout/disclaimer.ctp:10 -#: View/Users/view.ctp:46 +#: View/Users/view.ctp:45 msgid "Contact" msgstr "" @@ -508,7 +509,7 @@ msgid "Forum Settings" msgstr "" #: View/Elements/layout/header_login.ctp:22 View/Users/edit.ctp:133 -#: View/Users/view.ctp:88 View/Users/view.ctp:124 +#: View/Users/view.ctp:93 View/Users/view.ctp:132 msgid "user_profile" msgstr "User Profile" @@ -520,34 +521,34 @@ msgstr "Logout" msgid "No help for this page available." msgstr "" -#: View/Elements/layout/header_search.ctp:16 View/Entries/search.ctp:26 +#: View/Elements/layout/header_search.ctp:28 View/Entries/search.ctp:26 #: View/Entries/search.ctp:105 msgid "search_submit" msgstr "Search" -#: View/Elements/layout/slidetab_recententries.ctp:11 +#: View/Elements/layout/slidetab_recententries.ctp:9 msgid "Recent entries" msgstr "" -#: View/Elements/layout/slidetab_recentposts.ctp:13 View/Users/view.ctp:219 +#: View/Elements/layout/slidetab_recentposts.ctp:13 View/Users/view.ctp:229 msgid "user_recentposts" msgstr "recent posts" -#: View/Elements/layout/slidetab_userlist.ctp:18 +#: View/Elements/layout/slidetab_userlist.ctp:17 msgid "user_area_linkname" msgstr "User" -#: View/Elements/layout/slidetab_userlist.ctp:16 +#: View/Elements/layout/slidetab_userlist.ctp:15 #, php-format msgid "%s online (%s)" msgstr "" -#: View/Elements/layout/slidetab_userlist.ctp:32 +#: View/Elements/layout/slidetab_userlist.ctp:31 #: View/Helper/UserHHelper.php:116 msgid "ud_admin" msgstr "Administrator" -#: View/Elements/layout/slidetab_userlist.ctp:33 +#: View/Elements/layout/slidetab_userlist.ctp:32 #: View/Helper/UserHHelper.php:114 msgid "ud_mod" msgstr "Moderator" @@ -611,43 +612,47 @@ msgstr "" "To activate your account please click the following link: %s within the next " "24 hours." -#: View/Entries/add.ctp:31 View/Entries/add.ctp:220 +#: View/Entries/add.ctp:34 View/Entries/add.ctp:190 msgid "preview" msgstr "Preview" -#: View/Entries/add.ctp:79 Controller/UsersController.php:444 +#: View/Entries/add.ctp:88 Controller/UsersController.php:477 msgid "error_subject_empty" msgstr "Subject must not be empty." -#: View/Entries/add.ctp:116 +#: View/Entries/add.ctp:89 +msgid "error_subject_max_length" +msgstr "Subject is to long." + +#: View/Entries/add.ctp:133 msgid "Cite" msgstr "" -#: View/Entries/add.ctp:138 +#: View/Entries/add.ctp:158 View/Entries/add.ctp:178 View/Entries/add.ctp:292 +msgid "submit_button" +msgstr "Submit" + +#: View/Entries/add.ctp:205 msgid "Notify on reply" msgstr "" -#: View/Entries/add.ctp:146 +#: View/Entries/add.ctp:219 msgid "Notify on thread replies" msgstr "" -#: View/Entries/add.ctp:153 View/Helper/EntryHHelper.php:196 +#: View/Entries/add.ctp:228 View/Helper/EntryHHelper.php:159 msgid "entry_nsfw_title" msgstr "NSFW" -#: View/Entries/add.ctp:161 +#: View/Entries/add.ctp:241 msgid "entry_flattr_this_posting" msgstr "flattr-Button" -#: View/Entries/add.ctp:200 View/Entries/add.ctp:209 View/Entries/add.ctp:239 -msgid "submit_button" -msgstr "Submit" - -#: View/Entries/index.ctp:5 +#: View/Entries/index.ctp:6 msgid "new_entry_linkname" msgstr "New Entry" -#: View/Entries/index.ctp:20 +#: View/Entries/index.ctp:22 msgid "btn_manualy_mark_as_read_shp" msgstr "Marks all new entries as read." @@ -701,33 +706,33 @@ msgstr "Since" msgid "search_simple" msgstr "Simple Search" -#: View/Entries/search.ctp:163 +#: View/Entries/search.ctp:167 msgid "search_nothing_found" msgstr "Nothing found." -#: View/Layouts/admin.ctp:41 +#: View/Layouts/admin.ctp:36 msgid "Overview" msgstr "" -#: View/Layouts/admin.ctp:46 View/Settings/admin_edit.ctp:2 -#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:11 -#: View/Users/edit.ctp:164 +#: View/Layouts/admin.ctp:41 View/Settings/admin_edit.ctp:2 +#: View/Settings/admin_index.ctp:2 View/Settings/admin_index.ctp:113 +#: View/Users/edit.ctp:163 msgid "Settings" msgstr "" -#: View/Layouts/admin.ctp:49 View/Users/admin_add.ctp:2 +#: View/Layouts/admin.ctp:44 View/Users/admin_add.ctp:2 #: View/Users/admin_index.ctp:1 View/Users/admin_index.ctp:3 msgid "Users" msgstr "" -#: View/Layouts/admin.ctp:55 View/SmileyCodes/admin_edit.ctp:1 +#: View/Layouts/admin.ctp:50 View/SmileyCodes/admin_edit.ctp:1 #: View/SmileyCodes/admin_index.ctp:1 View/Smilies/admin_add.ctp:2 #: View/Smilies/admin_edit.ctp:1 View/Smilies/admin_index.ctp:1 #: View/Smilies/admin_index.ctp:3 msgid "Smilies" msgstr "" -#: View/Layouts/admin.ctp:65 +#: View/Layouts/admin.ctp:60 msgid "Forum" msgstr "" @@ -739,389 +744,54 @@ msgstr "" msgid "RSS Feed" msgstr "" -#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:111 +#: View/Pages/rss_feeds.ctp:4 Controller/EntriesController.php:110 msgid "Last entries" msgstr "" -#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:107 +#: View/Pages/rss_feeds.ctp:7 Controller/EntriesController.php:106 msgid "Last started threads" msgstr "" -#: View/Settings/admin_edit.ctp:27 View/Settings/admin_timezone.ctp:24 -msgid "_exp" -msgstr "" - -#: View/Settings/admin_index.ctp:4 -msgid "Key" -msgstr "" - -#: View/Settings/admin_index.ctp:5 -msgid "Value" -msgstr "" - -#: View/Settings/admin_index.ctp:6 -msgid "Explanation" -msgstr "" - -#: View/Settings/admin_index.ctp:12 +#: View/Settings/admin_index.ctp:7 msgid "Deactivate Forum" msgstr "" -#: View/Settings/admin_index.ctp:17 -msgid "forum_disabled" -msgstr "" - -#: View/Settings/admin_index.ctp:23 -msgid "forum_disabled_exp" -msgstr "" - -#: View/Settings/admin_index.ctp:27 View/Settings/admin_index.ctp:46 -#: View/Settings/admin_index.ctp:70 View/Settings/admin_index.ctp:89 -#: View/Settings/admin_index.ctp:113 View/Settings/admin_index.ctp:140 -#: View/Settings/admin_index.ctp:159 View/Settings/admin_index.ctp:178 -#: View/Settings/admin_index.ctp:202 View/Settings/admin_index.ctp:221 -#: View/Settings/admin_index.ctp:245 View/Settings/admin_index.ctp:269 -#: View/Settings/admin_index.ctp:288 View/Settings/admin_index.ctp:307 -#: View/Settings/admin_index.ctp:326 View/Settings/admin_index.ctp:345 -#: View/Settings/admin_index.ctp:364 View/Settings/admin_index.ctp:383 -#: View/Settings/admin_index.ctp:402 View/Settings/admin_index.ctp:421 -#: View/Settings/admin_index.ctp:440 View/Settings/admin_index.ctp:459 -#: View/Settings/admin_index.ctp:478 View/Settings/admin_index.ctp:502 -#: View/Settings/admin_index.ctp:521 View/Settings/admin_index.ctp:545 -#: View/Settings/admin_index.ctp:564 View/Settings/admin_index.ctp:588 -#: View/Settings/admin_index.ctp:607 View/Settings/admin_index.ctp:630 -#: View/Settings/admin_index.ctp:649 View/Settings/admin_index.ctp:668 -#: View/Settings/admin_index.ctp:687 View/Settings/admin_index.ctp:697 -#: View/Settings/admin_index.ctp:716 -msgid "edit" -msgstr "" - -#: View/Settings/admin_index.ctp:36 -msgid "forum_disabled_text" -msgstr "" - -#: View/Settings/admin_index.ctp:42 -msgid "forum_disabled_text_exp" -msgstr "" - -#: View/Settings/admin_index.ctp:55 +#: View/Settings/admin_index.ctp:13 msgid "Base Preferences" msgstr "" -#: View/Settings/admin_index.ctp:60 -msgid "forum_name" -msgstr "Forum Title" - -#: View/Settings/admin_index.ctp:66 -msgid "forum_name_exp" -msgstr "The name of this forum." - -#: View/Settings/admin_index.ctp:79 -msgid "timezone" -msgstr "" - -#: View/Settings/admin_index.ctp:85 -msgid "timezone_exp" -msgstr "" - -#: View/Settings/admin_index.ctp:98 +#: View/Settings/admin_index.ctp:19 msgid "Email" msgstr "" -#: View/Settings/admin_index.ctp:103 -msgid "forum_email" -msgstr "Email" - -#: View/Settings/admin_index.ctp:109 -msgid "forum_email_exp" -msgstr "" -"Email address used as sender address for emails send by the forum (e.g. when " -"registering)." - -#: View/Settings/admin_index.ctp:122 -msgid "email_admin_config_exp" -msgstr "" -"Additional settings are possible by providing a $saito config " -"in app/config/email.php. You should use app/config/email." -"php.default as template. See also." - -#: View/Settings/admin_index.ctp:125 +#: View/Settings/admin_index.ctp:25 msgid "Moderation" msgstr "Moderation" -#: View/Settings/admin_index.ctp:130 -msgid "block_user_ui" -msgstr "Lock Users" - -# -#: View/Settings/admin_index.ctp:136 -msgid "block_user_ui_exp" -msgstr "Moderators are allowed to block users." - -#: View/Settings/admin_index.ctp:149 -msgid "store_ip" -msgstr "Store IP" - -#: View/Settings/admin_index.ctp:155 -msgid "store_ip_exp" -msgstr "Stores the IP-address for new entries. The IP is shown do moderators." - -#: View/Settings/admin_index.ctp:168 -msgid "store_ip_anonymized" -msgstr "IP Anonymized" - -#: View/Settings/admin_index.ctp:174 -msgid "store_ip_anonymized_exp" -msgstr "Strore IP anonymized." - -#: View/Settings/admin_index.ctp:187 +#: View/Settings/admin_index.ctp:31 msgid "Registration" msgstr "Registration" -#: View/Settings/admin_index.ctp:192 -msgid "tos_enabled" -msgstr "ToS Enabled" - -#: View/Settings/admin_index.ctp:198 -msgid "tos_enabled_exp" -msgstr "" -"Puts a Term of Service checkbox and link on the user registration page." - -#: View/Settings/admin_index.ctp:211 -msgid "tos_url" -msgstr "ToS Url" - -#: View/Settings/admin_index.ctp:217 -msgid "tos_url_exp" -msgstr "" -"Sets the url for the ToS link. If empty, use the build-in default pages." - -# -#: View/Settings/admin_index.ctp:235 -msgid "edit_period" -msgstr "Edit Period" - -# -#: View/Settings/admin_index.ctp:241 -msgid "edit_period_exp" -msgstr "Minutes a user can edit a new entry." - -# -#: View/Settings/admin_index.ctp:259 -msgid "edit_delay" -msgstr "Show Edit" - -# -#: View/Settings/admin_index.ctp:265 -msgid "edit_delay_exp" -msgstr "Minutes a new entry can be edited without being marked as edited." - -#: View/Settings/admin_index.ctp:278 -msgid "topics_per_page" -msgstr "Topics per Page" - -#: View/Settings/admin_index.ctp:284 -msgid "topics_per_page_exp" -msgstr "Number of topics shown on the frontpage." - -#: View/Settings/admin_index.ctp:297 -msgid "thread_depth_indent" -msgstr "Thread Indent Depth" - -#: View/Settings/admin_index.ctp:303 -msgid "thread_depth_indent_exp" -msgstr "" -"Max. indent to the the right for threads in thread-view. After that threads " -"appear on the same level." - -#: View/Settings/admin_index.ctp:316 -msgid "autolink" -msgstr "Autolink" - -#: View/Settings/admin_index.ctp:322 -msgid "autolink_exp" -msgstr "Detect and automatically generate links." - -#: View/Settings/admin_index.ctp:335 -msgid "bbcode_img" -msgstr "Images" - -#: View/Settings/admin_index.ctp:341 -msgid "bbcode_img_exp" -msgstr "Allow embedding of images." - -#: View/Settings/admin_index.ctp:354 -msgid "quote_symbol" -msgstr "Quote Symbol" - -#: View/Settings/admin_index.ctp:360 -msgid "quote_symbol_exp" -msgstr "Quote symbol." - -#: View/Settings/admin_index.ctp:373 -msgid "signature_separator" -msgstr "Signature Separator" - -#: View/Settings/admin_index.ctp:379 -msgid "signature_separator_exp" -msgstr "Signature separator." - -#: View/Settings/admin_index.ctp:392 -msgid "subject_maxlength" -msgstr "Subject Length" - -#: View/Settings/admin_index.ctp:398 -msgid "subject_maxlength_exp" -msgstr "Number of chars allowed in subject." - -#: View/Settings/admin_index.ctp:411 -msgid "text_word_maxlength" -msgstr "Word Length" - -#: View/Settings/admin_index.ctp:417 -msgid "text_word_maxlength_exp" -msgstr "Max numbers of chars allowed in one word." - -#: View/Settings/admin_index.ctp:430 -msgid "userranks_show" -msgstr "Enable Ranks" - -#: View/Settings/admin_index.ctp:436 -msgid "userranks_show_exp" -msgstr "Show user ranks." - -#: View/Settings/admin_index.ctp:449 -msgid "userranks_ranks" -msgstr "Ranks" - -#: View/Settings/admin_index.ctp:455 -msgid "userranks_ranks_exp" -msgstr "User ranks based on number of postings." - -# -#: View/Settings/admin_index.ctp:468 -msgid "video_domains_allowed" -msgstr "Embeddable Video Sources" - -# -#: View/Settings/admin_index.ctp:474 -msgid "video_domains_allowed_exp" -msgstr "" -"Domain names embeddable for video/iframe. Asterisk * means all sources " -"allowed (This could pose a security risk if malicious code is embedded!)." - -#: View/Settings/admin_index.ctp:487 +#: View/Settings/admin_index.ctp:61 msgid "Category Chooser" msgstr "" -#: View/Settings/admin_index.ctp:492 -msgid "category_chooser_global" -msgstr "Default" - -#: View/Settings/admin_index.ctp:498 -msgid "category_chooser_global_exp" -msgstr "Always show the category chooser on the front-page." - -#: View/Settings/admin_index.ctp:511 -msgid "category_chooser_user_override" -msgstr "User Override" - -#: View/Settings/admin_index.ctp:517 -msgid "category_chooser_user_override_exp" -msgstr "" -"If the category chooser is off be default this allows users to enable it in " -"their user preferences." - -#: View/Settings/admin_index.ctp:535 -msgid "shoutbox_enabled" -msgstr "Enable" - -#: View/Settings/admin_index.ctp:541 -msgid "shoutbox_enabled_exp" -msgstr "Shoutbox is enabled." - -#: View/Settings/admin_index.ctp:554 -msgid "shoutbox_max_shouts" -msgstr "Number" - -#: View/Settings/admin_index.ctp:560 -msgid "shoutbox_max_shouts_exp" -msgstr "Number of entries shown in the Shoutbox." - -#: View/Settings/admin_index.ctp:578 -msgid "upload_max_img_size" -msgstr "" - -#: View/Settings/admin_index.ctp:584 -msgid "upload_max_img_size_exp" -msgstr "" - -#: View/Settings/admin_index.ctp:597 -msgid "upload_max_number_of_uploads" -msgstr "" - -#: View/Settings/admin_index.ctp:603 -msgid "upload_max_number_of_uploads_exp" +#: View/Settings/admin_index.ctp:67 +msgid "Shoutbox" msgstr "" -#: View/Settings/admin_index.ctp:620 -msgid "flattr_enabled" -msgstr "Flattr Enabled" - -#: View/Settings/admin_index.ctp:626 -msgid "flattr_enabled_exp" -msgstr "" -"Allows users to add a flattr button to their profil and their postings." - -#: View/Settings/admin_index.ctp:639 -msgid "flattr_language" -msgstr "Flattr Language" - -#: View/Settings/admin_index.ctp:645 -msgid "flattr_language_exp" -msgstr "" -"Flattr expects an language key for the content send. See flattr " -"documentation for available language keys." - -#: View/Settings/admin_index.ctp:658 -msgid "flattr_category" -msgstr "Flattr Category" - -#: View/Settings/admin_index.ctp:664 -msgid "flattr_category_exp" -msgstr "See flattr documentation for available categories." - -#: View/Settings/admin_index.ctp:683 -msgid "embedly_enabled" -msgstr "Enable Embedly" - -#: View/Settings/admin_index.ctp:685 -msgid "embedly_enabled_exp" -msgstr "Enables the [embed] BBcode which parses content through Embed.ly" - -#: View/Settings/admin_index.ctp:693 -msgid "embedly_key" -msgstr "Embedly Key" - -#: View/Settings/admin_index.ctp:695 -msgid "embedly_key_exp" -msgstr "" -"You have to register at Embed.ly to receive an API-key, which you enter here." +#: View/Settings/admin_index.ctp:73 +#, fuzzy +msgid "Uploads" +msgstr "Upload" -#: View/Settings/admin_index.ctp:706 +#: View/Settings/admin_index.ctp:93 msgid "Debug" msgstr "" -#: View/Settings/admin_index.ctp:712 -msgid "stopwatch_get" -msgstr "Stopwatch" - -#: View/Settings/admin_index.ctp:714 -#, fuzzy -msgid "stopwatch_get_exp" +#: View/Settings/admin_timezone.ctp:24 +msgid "_exp" msgstr "" -"Outputs stopwatch times by appending /stopwatch:true/ to url." #: View/SmileyCodes/admin_add.ctp:4 msgid "Add Smiley Code" @@ -1171,7 +841,7 @@ msgid "" "geheuert, :loggedin an Deck, :anon Blinde Passagiere" msgstr "" -#: View/Uploads/add.ctp:10 webroot/js/templates/uploadNew.html:23 +#: View/Uploads/add.ctp:10 webroot/js/templates/uploadNew.html:22 msgid "upload_btn" msgstr "Upload" @@ -1202,7 +872,7 @@ msgid "New User" msgstr "" #: View/Users/admin_index.ctp:10 View/Users/edit.ctp:32 View/Users/edit.ctp:80 -#: View/Users/index.ctp:14 View/Users/view.ctp:26 +#: View/Users/index.ctp:14 View/Users/view.ctp:25 msgid "username_marking" msgstr "Name" @@ -1257,15 +927,15 @@ msgstr "Contact %s" msgid "user_contact_sender-contact" msgstr "Your Contact Address" -#: View/Users/contact.ctp:18 +#: View/Users/contact.ctp:21 msgid "user_contact_subject" msgstr "Subject" -#: View/Users/contact.ctp:23 +#: View/Users/contact.ctp:29 msgid "user_contact_message" msgstr "Message" -#: View/Users/contact.ctp:37 +#: View/Users/contact.ctp:43 msgid "user_contact_send_carbon_copy" msgstr "Send copy to yourself." @@ -1278,7 +948,7 @@ msgstr "" msgid "Profil" msgstr "" -#: View/Users/edit.ctp:37 View/Users/edit.ctp:85 View/Users/view.ctp:53 +#: View/Users/edit.ctp:37 View/Users/edit.ctp:85 View/Users/view.ctp:52 msgid "userlist_email" msgstr "Email" @@ -1303,7 +973,7 @@ msgstr "" msgid "user_show_email_exp" msgstr "" -#: View/Users/edit.ctp:114 View/Users/view.ctp:39 +#: View/Users/edit.ctp:114 View/Users/view.ctp:38 msgid "user_real_name" msgstr "Real Name" @@ -1311,7 +981,7 @@ msgstr "Real Name" msgid "user_real_name_exp" msgstr "" -#: View/Users/edit.ctp:121 View/Users/view.ctp:60 +#: View/Users/edit.ctp:121 View/Users/view.ctp:59 msgid "user_hp" msgstr "Homepage" @@ -1319,7 +989,7 @@ msgstr "Homepage" msgid "user_hp_exp" msgstr "" -#: View/Users/edit.ctp:128 View/Users/view.ctp:67 +#: View/Users/edit.ctp:128 View/Users/view.ctp:66 msgid "user_place" msgstr "Place" @@ -1331,7 +1001,7 @@ msgstr "" msgid "user_profile_exp" msgstr "" -#: View/Users/edit.ctp:144 View/Users/view.ctp:96 +#: View/Users/edit.ctp:144 View/Users/view.ctp:101 msgid "user_signature" msgstr "Signature" @@ -1339,162 +1009,162 @@ msgstr "Signature" msgid "user_signature_exp" msgstr "" -#: View/Users/edit.ctp:174 +#: View/Users/edit.ctp:173 msgid "user_sort_last_answer" msgstr "" -#: View/Users/edit.ctp:180 +#: View/Users/edit.ctp:179 msgid "user_sort_last_answer_time" msgstr "" -#: View/Users/edit.ctp:181 +#: View/Users/edit.ctp:180 msgid "user_sort_last_answer_last_answer" msgstr "" -#: View/Users/edit.ctp:189 +#: View/Users/edit.ctp:188 msgid "user_sort_last_answer_exp" msgstr "" -#: View/Users/edit.ctp:194 +#: View/Users/edit.ctp:193 msgid "user_automaticaly_mark_as_read" msgstr "" -#: View/Users/edit.ctp:195 +#: View/Users/edit.ctp:194 msgid "user_automaticaly_mark_as_read_exp" msgstr "" -#: View/Users/edit.ctp:199 +#: View/Users/edit.ctp:198 msgid "user_signatures_hide" msgstr "" -#: View/Users/edit.ctp:201 +#: View/Users/edit.ctp:200 msgid "user_signatures_hide_exp" msgstr "" -#: View/Users/edit.ctp:203 +#: View/Users/edit.ctp:202 msgid "user_signatures_images_hide_exp" msgstr "" -#: View/Users/edit.ctp:208 +#: View/Users/edit.ctp:207 msgid "user_forum_refresh_time" msgstr "" -#: View/Users/edit.ctp:221 +#: View/Users/edit.ctp:220 msgid "user_forum_refresh_time_exp" msgstr "" -#: View/Users/edit.ctp:227 +#: View/Users/edit.ctp:226 msgid "user_colors" msgstr "" -#: View/Users/edit.ctp:229 +#: View/Users/edit.ctp:228 msgid "user_color_new_postings_exp" msgstr "" -#: View/Users/edit.ctp:231 +#: View/Users/edit.ctp:230 msgid "user_color_old_postinings_exp" msgstr "" -#: View/Users/edit.ctp:233 +#: View/Users/edit.ctp:232 msgid "user_color_actual_posting_exp" msgstr "" -#: View/Users/edit.ctp:255 +#: View/Users/edit.ctp:254 msgid "inline_view_on_click" msgstr "" -#: View/Users/edit.ctp:258 +#: View/Users/edit.ctp:257 msgid "inline_view_on_click_exp" msgstr "" -#: View/Users/edit.ctp:262 +#: View/Users/edit.ctp:261 #, fuzzy msgid "user_show_thread_collapsed" msgstr "Show User's Entries" -#: View/Users/edit.ctp:265 +#: View/Users/edit.ctp:264 msgid "user_show_thread_collapsed_exp" msgstr "" -#: View/Users/edit.ctp:270 +#: View/Users/edit.ctp:269 msgid "user_pers_msg" msgstr "" -#: View/Users/edit.ctp:271 +#: View/Users/edit.ctp:270 msgid "user_pers_msg_exp" msgstr "" -#: View/Users/edit.ctp:278 +#: View/Users/edit.ctp:277 #, fuzzy msgid "user_category_override" msgstr "Category-Chooser" -#: View/Users/edit.ctp:283 +#: View/Users/edit.ctp:282 #, fuzzy msgid "user_category_override_exp" msgstr "Activates the category-chooser on the front-page." -#: View/Users/edit.ctp:291 +#: View/Users/edit.ctp:290 msgid "user_time_diff" msgstr "" -#: View/Users/edit.ctp:292 +#: View/Users/edit.ctp:291 msgid "user_time_diff_exp" msgstr "" -#: View/Users/edit.ctp:297 +#: View/Users/edit.ctp:296 msgid "admin_mod_notif" msgstr "" -#: View/Users/edit.ctp:299 +#: View/Users/edit.ctp:298 msgid "admin_mod_notif_exp" msgstr "" -#: View/Users/edit.ctp:300 +#: View/Users/edit.ctp:299 msgid "new_posting_notify_exp" msgstr "" -#: View/Users/edit.ctp:302 +#: View/Users/edit.ctp:301 msgid "new_user_notify_exp" msgstr "" -#: View/Users/edit.ctp:309 +#: View/Users/edit.ctp:308 msgid "user_font_size" msgstr "" -#: View/Users/edit.ctp:326 +#: View/Users/edit.ctp:325 msgid "user_font_size_exp" msgstr "" -#: View/Users/edit.ctp:341 View/Users/view.ctp:104 +#: View/Users/edit.ctp:339 View/Users/view.ctp:112 msgid "flattr" msgstr "" -#: View/Users/edit.ctp:350 +#: View/Users/edit.ctp:348 msgid "flattr_uid" msgstr "" -#: View/Users/edit.ctp:354 +#: View/Users/edit.ctp:352 msgid "flattr_allow_user" msgstr "" -#: View/Users/edit.ctp:355 +#: View/Users/edit.ctp:353 msgid "flattr_allow_user_exp" msgstr "" -#: View/Users/edit.ctp:358 +#: View/Users/edit.ctp:356 msgid "flattr_allow_posting" msgstr "" -#: View/Users/edit.ctp:359 +#: View/Users/edit.ctp:357 msgid "flattr_allow_posting_exp" msgstr "" -#: View/Users/edit.ctp:365 +#: View/Users/edit.ctp:363 msgid "button_save" msgstr "" -#: View/Users/index.ctp:1 +#: View/Users/index.ctp:5 msgid "reg_users_hl" msgstr "Registered Users" @@ -1511,7 +1181,7 @@ msgid "register_success_content" msgstr "" "Your registration is now finished. You are able to loggin now. Have fun!" -#: View/Users/register.ctp:23 Controller/EntriesController.php:330 +#: View/Users/register.ctp:23 Controller/EntriesController.php:305 msgid "js-required" msgstr "To use this forum please enable JavaScript." @@ -1523,47 +1193,51 @@ msgstr "Terms of Service" msgid "register_tos_label" msgstr "I agree to the %s" -#: View/Users/view.ctp:12 -msgid "user_show_entries" -msgstr "Show User's Entries" - -#: View/Users/view.ctp:33 +#: View/Users/view.ctp:32 #, fuzzy msgid "user_block" msgstr "Place" -#: View/Users/view.ctp:74 +#: View/Users/view.ctp:73 msgid "user_since" msgstr "Registered" -#: View/Users/view.ctp:75 +#: View/Users/view.ctp:74 msgid "date_short" msgstr "%b. %d, %Y" -#: View/Users/view.ctp:78 +#: View/Users/view.ctp:77 msgid "user_postings" msgstr "Number of Postings" -#: View/Users/view.ctp:146 +#: View/Users/view.ctp:82 +msgid "user_show_entries" +msgstr "Show User's Entries" + +#: View/Users/view.ctp:154 msgid "edit_userdata" msgstr "Edit" -#: View/Users/view.ctp:156 +#: View/Users/view.ctp:164 msgid "button_mod_panel_shp" msgstr "Moderator Menu" -#: View/Users/view.ctp:170 +#: View/Users/view.ctp:178 msgid "Unlock" msgstr "" -#: View/Users/view.ctp:170 +#: View/Users/view.ctp:178 msgid "Lock" msgstr "" -#: View/Users/view.ctp:236 +#: View/Users/view.ctp:241 msgid "No entries created yet." msgstr "" +#: View/Users/view.ctp:253 +msgid "Show all" +msgstr "" + #: Controller/AdminsController.php:45 msgid "New" msgstr "" @@ -1622,55 +1296,51 @@ msgstr "" msgid "Category deleted." msgstr "" -#: Controller/EntriesController.php:93 +#: Controller/EntriesController.php:88 msgid "page" msgstr "" -#: Controller/EntriesController.php:218 Controller/EntriesController.php:227 +#: Controller/EntriesController.php:187 Controller/EntriesController.php:196 msgid "Invalid post" msgstr "" -#: Controller/EntriesController.php:268 +#: Controller/EntriesController.php:238 msgid "new_entry_linktitle" msgstr "New Entry" -#: Controller/EntriesController.php:271 -msgid "logged_in_users_only" -msgstr "" - -#: Controller/EntriesController.php:321 Controller/EntriesController.php:443 +#: Controller/EntriesController.php:293 Controller/EntriesController.php:414 msgid "Something clogged the tubes. Could not save entry. Try again." msgstr "" -#: Controller/EntriesController.php:368 Controller/EntriesController.php:477 +#: Controller/EntriesController.php:344 Controller/EntriesController.php:454 msgid "back_to_posting_from_linkname" msgstr "Zurück zum Eintrag von %s" -#: Controller/EntriesController.php:379 +#: Controller/EntriesController.php:355 msgid "answer_marking" msgstr "Answer" -#: Controller/EntriesController.php:429 +#: Controller/EntriesController.php:420 msgid "notice_you_are_editing_as_mod" msgstr "You're now editing as moderator!" -#: Controller/EntriesController.php:512 +#: Controller/EntriesController.php:485 msgid "delete_tree_success" msgstr "Thread successfully removed." -#: Controller/EntriesController.php:515 +#: Controller/EntriesController.php:488 msgid "delete_subtree_success" msgstr "Subthread successfully removed." -#: Controller/EntriesController.php:519 +#: Controller/EntriesController.php:492 msgid "delete_tree_error" msgstr "" -#: Controller/EntriesController.php:721 +#: Controller/EntriesController.php:707 msgid "Error" msgstr "" -#: Controller/EntriesController.php:917 +#: Controller/EntriesController.php:924 msgid "Custom" msgstr "" @@ -1736,105 +1406,107 @@ msgstr "" msgid "Smily was not deleted" msgstr "" -#: Controller/ToolsController.php:30 +#: Controller/ToolsController.php:21 msgid "Caches have been emptied." msgstr "" -#: Controller/UploadsController.php:35 Controller/UploadsController.php:89 +#: Controller/UploadsController.php:35 msgid "upload_max_number_of_uploads_failed" msgstr "No more uploads possible (max: %s)." -#: Controller/UsersController.php:54 Controller/UsersController.php:310 +#: Controller/UsersController.php:37 Controller/UsersController.php:329 #, php-format msgid "User %s is locked." msgstr "" -#: Controller/UsersController.php:60 +#: Controller/UsersController.php:43 msgid "auth_loginerror" msgstr "Username or Password wrong." -#: Controller/UsersController.php:110 +#: Controller/UsersController.php:92 msgid "register_email_subject" msgstr "Welcome to %s!" -#: Controller/UsersController.php:196 Controller/UsersController.php:217 +#: Controller/UsersController.php:172 Controller/UsersController.php:195 +#: Controller/UsersController.php:233 msgid "Invalid user" msgstr "" -#: Controller/UsersController.php:251 -msgid "user_edit_success" -msgstr "Preferences were saved." +#: Controller/UsersController.php:224 +#, fuzzy +msgid "User :name" +msgstr "Username" -#: Controller/UsersController.php:264 +#: Controller/UsersController.php:278 msgid "The user could not be saved. Please, try again." msgstr "" -#: Controller/UsersController.php:292 Controller/UsersController.php:329 +#: Controller/UsersController.php:311 Controller/UsersController.php:348 #, fuzzy msgid "User not found." msgstr "Nothing found." -#: Controller/UsersController.php:300 +#: Controller/UsersController.php:320 msgid "You can't lock yourself." msgstr "" -#: Controller/UsersController.php:302 +#: Controller/UsersController.php:322 msgid "You can't lock administrators." msgstr "" -#: Controller/UsersController.php:312 +#: Controller/UsersController.php:331 #, php-format msgid "User %s is unlocked." msgstr "" -#: Controller/UsersController.php:316 +#: Controller/UsersController.php:335 msgid "Error while un/locking." msgstr "" -#: Controller/UsersController.php:335 +#: Controller/UsersController.php:355 msgid "You can't delete yourself." msgstr "" -#: Controller/UsersController.php:337 +#: Controller/UsersController.php:357 msgid "You can't delete the installation account." msgstr "" -#: Controller/UsersController.php:339 +#: Controller/UsersController.php:359 #, php-format msgid "User %s deleted." msgstr "" -#: Controller/UsersController.php:342 +#: Controller/UsersController.php:363 msgid "Couldn't delete user." msgstr "" -#: Controller/UsersController.php:367 +#: Controller/UsersController.php:387 msgid "change_password_success" msgstr "Password was changed." -#: Controller/UsersController.php:429 +#: Controller/UsersController.php:455 msgid "error_email_not-valid" msgstr "No valid email address." -#: Controller/UsersController.php:463 +#: Controller/UsersController.php:502 msgid "Message was send." msgstr "" -#: Controller/UsersController.php:466 -msgid "Error, message couldn't be send! " +#: Controller/UsersController.php:507 +msgid "Message couldn't be send! " msgstr "" -#: Controller/Component/CurrentUserComponent.php:258 +#: Controller/Component/CurrentUserComponent.php:308 msgid "auth_autherror" msgstr "Access to this function is restricted!" -#: Controller/Component/EmailNotificationComponent.php:53 -#: Controller/Component/EmailNotificationComponent.php:85 +#: Controller/Component/EmailNotificationComponent.php:47 +#: Controller/Component/EmailNotificationComponent.php:79 #, php-format msgid "New reply to \"%s\"" msgstr "" -#: Controller/Component/EmailNotificationComponent.php:114 +#: Controller/Component/EmailNotificationComponent.php:112 msgid "Successfull registration" msgstr "" @@ -1914,31 +1586,31 @@ msgstr "" msgid "Abend" msgstr "" -#: View/Helper/BbcodeHelper.php:486 +#: View/Helper/BbcodeHelper.php:580 msgid "" "Your browser does not support HTML5 video. Please updgrade to a " "modernbrowser. In order to watch this stream you need an HTML5 capable " "browser." msgstr "" -#: View/Helper/BbcodeHelper.php:504 +#: View/Helper/BbcodeHelper.php:601 msgid "" "Your browser does not support HTML5 audio. Please updgrade to a " "modernbrowser. In order to watch this stream you need an HTML5 capable " "browser." msgstr "" -#: View/Helper/BbcodeHelper.php:741 +#: View/Helper/BbcodeHelper.php:926 #, fuzzy msgid "[embed] tag not enabled." msgstr "Enable Embedly" -#: View/Helper/BbcodeHelper.php:849 +#: View/Helper/BbcodeHelper.php:1012 #, php-format msgid "Domain %s not allowed for embedding video." msgstr "" -#: View/Helper/BbcodeHelper.php:857 +#: View/Helper/BbcodeHelper.php:1020 msgid "Video domain is not allowed." msgstr "" @@ -1950,15 +1622,15 @@ msgstr "" msgid "Bookmark this entry" msgstr "" -#: View/Helper/EntryHHelper.php:193 +#: View/Helper/EntryHHelper.php:156 msgid "fixed" msgstr "" -#: View/Helper/EntryHHelper.php:208 +#: View/Helper/EntryHHelper.php:171 msgid "cateogry" msgstr "Category" -#: View/Helper/EntryHHelper.php:211 +#: View/Helper/EntryHHelper.php:174 msgid "error_category_empty" msgstr "Category must not be empty." @@ -2026,8 +1698,8 @@ msgstr "" msgid "geshi_picture_popup" msgstr "" -#: View/Helper/MarkitupEditorHelper.php:80 webroot/js/main-prod.js:79 -#: webroot/js/views/uploads.js:71 +#: View/Helper/MarkitupEditorHelper.php:80 webroot/js/main-prod.js:100 +#: webroot/js/views/uploadNew.js:99 webroot/js/views/uploads.js:71 #, fuzzy msgid "Upload" msgstr "Upload" @@ -2036,6 +1708,22 @@ msgstr "Upload" msgid "Media" msgstr "" +#: View/Helper/SettingHelper.php:53 +msgid "edit" +msgstr "" + +#: View/Helper/SettingHelper.php:64 +msgid "Key" +msgstr "" + +#: View/Helper/SettingHelper.php:65 +msgid "Value" +msgstr "" + +#: View/Helper/SettingHelper.php:66 +msgid "Explanation" +msgstr "" + #: View/Helper/TimeHHelper.php:100 View/Helper/TimeHHelper.php:119 msgid "yesterday" msgstr "" @@ -2060,19 +1748,31 @@ msgstr "" msgid "Nothing recognized." msgstr "" -#: webroot/js/templates/uploadNew.html:9 +#: webroot/js/templates/uploadNew.html:8 msgid "upload_new_title" msgstr "Drop Image Here" -#: webroot/js/templates/uploadNew.html:11 +#: webroot/js/templates/uploadNew.html:10 msgid "upload_info" msgstr "Max. size: :size; type: jpg, jpeg, png, gif" -#: webroot/js/main-prod.js:79 webroot/js/views/uploadNew.js:66 +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:69 msgid "upload_fileTypeNotAllowed" msgstr "File type not allowed." -#: webroot/js/main-prod.js:79 +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:73 +msgid "upload_fileToLarge" +msgstr "File :name to large." + +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:78 +msgid "upload_browserNotSupported" +msgstr "Browser not supported." + +#: webroot/js/main-prod.js:100 webroot/js/views/uploadNew.js:81 +msgid "upload_toManyFiles" +msgstr "To many files." + +#: webroot/js/main-prod.js:100 msgid "" "Enter link or embedding code:') %>\n" " \n" @@ -2095,7 +1795,7 @@ msgid "" "\n" msgstr "" -#: webroot/js/main-prod.js:79 webroot/js/views/mediaInsert.js:68 +#: webroot/js/main-prod.js:100 webroot/js/views/mediaInsert.js:68 msgid "Multimedia" msgstr "" @@ -2107,48 +1807,49 @@ msgstr "" msgid "tr" msgstr "" -#: webroot/js/tests/lib/jquery.i18n.extendSpec.js:28 +#: webroot/js/tests/lib/jquery.i18n.extendSpec.js:30 msgid "token test" msgstr "" -#: webroot/js/views/uploadNew.js:69 -msgid "upload_fileToLarge" -msgstr "File :name to large." - -#: webroot/js/views/uploadNew.js:72 -msgid "upload_browserNotSupported" -msgstr "Browser not supported." +#~ msgid "forum_disabled" +#~ msgstr "Disable Forum" -#: webroot/js/views/uploadNew.js:75 -msgid "upload_toManyFiles" -msgstr "To many files." +#~ msgid "forum_disabled_exp" +#~ msgstr "Shows the maintenance page except for logged in admins." -#~ msgid "whole_thread_marking" -#~ msgstr "Whole Thread" +#~ msgid "forum_disabled_text" +#~ msgstr "Maintenance Notice" -#~ msgid "register_email_send_content" -#~ msgstr "" -#~ "Thanks for registering. A email with a link was send to you. Please click " -#~ "that link to finish the registration. Prior to that you're not able to " -#~ "login!" +#~ msgid "forum_disabled_text_exp" +#~ msgstr "Text notice shown on the forum disabled page." -#~ msgid "fixed_set_entry_link" -#~ msgstr "Pin" +#~ msgid "forum_name" +#~ msgstr "Forum Title" -#~ msgid "fixed_unset_entry_link" -#~ msgstr "Unpin" +#~ msgid "forum_name_exp" +#~ msgstr "The name of this forum." -#~ msgid "locked_set_entry_link" -#~ msgstr "Lock" +#~ msgid "timezone" +#~ msgstr "Timezone" -#~ msgid "locked_unset_entry_link" -#~ msgstr "Unlock" +#~ msgid "timezone_exp" +#~ msgstr "All time values displayed are offset to match this timezone." -#~ msgid "board_edited_marking" -#~ msgstr "edited by" +#~ msgid "forum_email" +#~ msgstr "Email" -#~ msgid "register_ip" -#~ msgstr "Register IP" +#~ msgid "forum_email_exp" +#~ msgstr "" +#~ "Email address used as sender address for emails send by the forum (e.g. " +#~ "when registering)." -#~ msgid "back_to_posting_linkname" -#~ msgstr "Back to Posting" +#~ msgid "email_admin_config_exp" +#~ msgstr "" +#~ "Additional settings are possible by providing a $saito " +#~ "config in app/config/email.php. You should use app/" +#~ "config/email.php.default as template. See " +#~ "also." + +#~ msgid "block_user_ui" +#~ msgstr "Lock Users" diff --git a/app/Locale/eng/LC_MESSAGES/nondynamic.po b/app/Locale/eng/LC_MESSAGES/nondynamic.po index 16722064b..1723b2c8a 100644 --- a/app/Locale/eng/LC_MESSAGES/nondynamic.po +++ b/app/Locale/eng/LC_MESSAGES/nondynamic.po @@ -82,3 +82,288 @@ msgstr "notice" # msgid "success" msgstr "Success" + +#******************************************************************************* +#* App Settings Start +#******************************************************************************/ + +# +msgid "forum_disabled" +msgstr "Disable Forum" + +# +msgid "forum_disabled_exp" +msgstr "Shows the maintenance page except for logged in admins." + +# +msgid "forum_disabled_text" +msgstr "Maintenance Notice" + +# +msgid "forum_disabled_text_exp" +msgstr "Text notice shown on the forum disabled page." + +# +msgid "forum_name" +msgstr "Forum Title" + +# +msgid "forum_name_exp" +msgstr "The name of this forum." + +# +msgid "timezone" +msgstr "Timezone" + +# +msgid "timezone_exp" +msgstr "All time values displayed are offset to match this timezone." + +# +msgid "forum_email" +msgstr "Email" + +# +msgid "forum_email_exp" +msgstr "Email address used as sender address for emails send by the forum (e.g. when registering)." + +# +msgid "block_user_ui" +msgstr "Lock Users" + +# +msgid "block_user_ui_exp" +msgstr "Moderators are allowed to block users." + +# +msgid "store_ip" +msgstr "Store IP" + +# +msgid "store_ip_exp" +msgstr "Stores the IP-address for new entries. The IP is shown do moderators." + +# +msgid "store_ip_anonymized" +msgstr "IP Anonymized" + +# +msgid "store_ip_anonymized_exp" +msgstr "Strore IP anonymized." + +# +msgid "tos_enabled" +msgstr "ToS Enabled" + +# +msgid "tos_enabled_exp" +msgstr "Puts a Term of Service checkbox and link on the user registration page." + +# +msgid "tos_url" +msgstr "ToS Url" + +# +msgid "tos_url_exp" +msgstr "Sets the url for the ToS link. If empty, use the build-in default pages." + +# +msgid "edit_period" +msgstr "Edit Period" + +# +msgid "edit_period_exp" +msgstr "Minutes a user can edit a new entry." + +# +msgid "edit_delay" +msgstr "Show Edit" + +# +msgid "edit_delay_exp" +msgstr "Minutes a new entry can be edited without being marked as edited." + +# +msgid "topics_per_page" +msgstr "Topics per Page" + +# +msgid "topics_per_page_exp" +msgstr "Number of topics shown on the frontpage." + +# +msgid "thread_depth_indent" +msgstr "Thread Indent Depth" + +# +msgid "thread_depth_indent_exp" +msgstr "Max. indent to the the right for threads in thread-view. After that threads appear on the same level." + +# +msgid "autolink" +msgstr "Autolink" + +# +msgid "autolink_exp" +msgstr "Detect and automatically generate links." + +# +msgid "bbcode_img" +msgstr "Images" + +# +msgid "bbcode_img_exp" +msgstr "Allow embedding of images." + +# +msgid "quote_symbol" +msgstr "Quote Symbol" + +# +msgid "quote_symbol_exp" +msgstr "Quote symbol." + +# +msgid "signature_separator" +msgstr "Signature Separator" + +# +msgid "signature_separator_exp" +msgstr "Signature separator." + +# +msgid "subject_maxlength" +msgstr "Subject Length" + +# +msgid "subject_maxlength_exp" +msgstr "Number of chars allowed in subject." + +# +msgid "text_word_maxlength" +msgstr "Word Length" + +# +msgid "text_word_maxlength_exp" +msgstr "Max numbers of chars allowed in one word." + +# +msgid "userranks_show" +msgstr "Enable Ranks" + +# +msgid "userranks_show_exp" +msgstr "Show user ranks." + +# +msgid "userranks_ranks" +msgstr "Ranks" + +# +msgid "userranks_ranks_exp" +msgstr "User ranks based on number of postings." + +# +msgid "video_domains_allowed" +msgstr "Embeddable Video Sources" + +# +msgid "video_domains_allowed_exp" +msgstr "Domain names embeddable for video/iframe. Asterisk * means all sources allowed (This could pose a security risk if malicious code is embedded!)." + +# +msgid "category_chooser_global" +msgstr "Default" + +# +msgid "category_chooser_global_exp" +msgstr "Always show the category chooser on the front-page." + +# +msgid "category_chooser_user_override" +msgstr "User Override" + +# +msgid "category_chooser_user_override_exp" +msgstr "If the category chooser is off be default this allows users to enable it in their user preferences." + +# +msgid "shoutbox_enabled" +msgstr "Enable" + +# +msgid "shoutbox_enabled_exp" +msgstr "Shoutbox is enabled." + +# +msgid "shoutbox_max_shouts" +msgstr "Number" + +# +msgid "shoutbox_max_shouts_exp" +msgstr "Number of entries shown in the Shoutbox." + +# +msgid "upload_max_img_size" +msgstr "Upload Max Image Size " + +# +msgid "upload_max_img_size_exp" +msgstr "Max image size in kB for uploaded content." + +# +msgid "upload_max_number_of_uploads" +msgstr "Max Number of Uploads" + +# +msgid "upload_max_number_of_uploads_exp" +msgstr "Max number of uploaded files for a single user." + +# +msgid "flattr_enabled" +msgstr "Flattr Enabled" + +# +msgid "flattr_enabled_exp" +msgstr "Allows users to add a flattr button to their profil and their postings." + +# +msgid "flattr_language" +msgstr "Flattr Language" + +# +msgid "flattr_language_exp" +msgstr "Flattr expects an language key for the content send. See flattr documentation for available language keys." + +# +msgid "flattr_category" +msgstr "Flattr Category" + +# +msgid "flattr_category_exp" +msgstr "See flattr documentation for available categories." + +# +msgid "embedly_enabled" +msgstr "Enable Embedly" + +# +msgid "embedly_enabled_exp" +msgstr "Enables the [embed] BBcode which parses content through Embed.ly" + +# +msgid "embedly_key" +msgstr "Embedly Key" + +# +msgid "embedly_key_exp" +msgstr "You have to register at Embed.ly to receive an API-key, which you enter here." + +# +msgid "stopwatch_get" +msgstr "Stopwatch" + +# +msgid "stopwatch_get_exp" +msgstr "Outputs stopwatch times in production mode for logged in users by appending /stopwatch:true/ to url." + diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index a7769b52c..46b952cc0 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -3,6 +3,7 @@ App::uses('Model', 'Model'); App::uses('Sanitize', 'Utility'); App::uses('SaitoUser', 'Lib'); + App::uses('CakeEvent', 'Event'); // import here so that `cake schema ...` cli works App::import('Lib', 'Stopwatch.Stopwatch'); @@ -12,6 +13,11 @@ class AppModel extends Model { # Entry->User->UserOnline public $recursive = 1; + /* + * Lock to disable sanitation permanently + */ + static $sanitizeEnabled = true; + static $sanitize = true; /** @@ -43,11 +49,13 @@ protected function _sanitizeFields($results) { public function afterFind($results, $primary = false) { parent::afterFind($results, $primary); - if (self::$sanitize) { - $results = $this->_sanitizeFields($results); - } elseif (self::$_lock_no_sanitize === $this->alias) { - // sanitizing can only be disabled for one find - $this->sanitize(true); + if (self::$sanitizeEnabled) { + if (self::$sanitize) { + $results = $this->_sanitizeFields($results); + } elseif (self::$_lock_no_sanitize === $this->alias) { + // sanitizing can only be disabled for one find + $this->sanitize(true); + } } return $results; } @@ -70,6 +78,14 @@ public function toggle($key) { return $value; } + public function pipeMerger(array $data) { + $out = []; + foreach ($data as $key => $value) { + $out[] = "$key=$value"; + } + return implode(' | ', $out); + } + /** * Splits String 'a=b|c=d|e=f' into an array('a'=>'b', 'c'=>'d', 'e'=>'f') * @@ -78,10 +94,10 @@ public function toggle($key) { */ protected function _pipeSplitter($pipeString) { $unpipedArray = array(); - $ranks = explode("|", $pipeString); + $ranks = explode('|', $pipeString); foreach ( $ranks as $rank ) : $matches = array(); - $matched = preg_match('/(\d+)\s*=\s*(.*)/', trim($rank), $matches); + $matched = preg_match('/(\w+)\s*=\s*(.*)/', trim($rank), $matches); if ($matched) { $unpipedArray[$matches[1]] = $matches[2]; } @@ -100,6 +116,20 @@ protected static function _getIp() { return $ip; } + /** + * Dispatches an event + * + * - Always passes the issuing model class as subject + * - Wrapper for CakeEvent boilerplate code + * - Easier to test + * + * @param string $event event identifier `Model..` + * @param array $data additional event data + */ + protected function _dispatchEvent($event, $data = []) { + $this->getEventManager()->dispatch(new CakeEvent($event, $this, $data)); + } + /** * Rough and tough ip anonymizer * diff --git a/app/Model/Category.php b/app/Model/Category.php index 6b3aad6d9..50ba4f76b 100755 --- a/app/Model/Category.php +++ b/app/Model/Category.php @@ -54,18 +54,8 @@ public function getCategoriesSelectForAccession($accession) { return $categories; } - /* - public function afterSave($created) { - debug($this->data); - if ($created || - $this->clearCache(); - ) - } - */ - protected function _getCategoriesForAccession($accession) { if (!isset($this->_cache[$accession])) { - // $this->_cache = Cache::read('Saito.Cache.catForAccession'); if (empty($this->_cache[$accession])) { $this->_cache[$accession] = $this->find('list', array( @@ -77,17 +67,10 @@ protected function _getCategoriesForAccession($accession) { ) ); } - // Cache::write('Saito.Cache.catForAccession', $this->_cache); } return $this->_cache[$accession]; } - /* - public function clearCache() { - - } - */ - public function mergeIntoCategory($targetCategory) { if (!isset($this->id)) return false; @@ -121,4 +104,17 @@ public function updateThreadCounter() { return $c; } + public function afterDelete() { + $this->_dispatchEvent('Model.Category.delete'); + } + + public function afterSave($created) { + // don't empty cache if it's only a thread count update + if (!isset($this->data[$this->alias]['thread_count']) && + isset($this->data[$this->alias]['category']) + ) { + $this->_dispatchEvent('Model.Category.update'); + } + } + } \ No newline at end of file diff --git a/app/Model/Entry.php b/app/Model/Entry.php index a3c9a1674..d11cec629 100644 --- a/app/Model/Entry.php +++ b/app/Model/Entry.php @@ -1,7 +1,6 @@ true, - 'entry' => true + 'feed' => true, + 'entry' => true, + 'unsanitized' => true ); /** @@ -81,8 +81,7 @@ class Entry extends AppModel { 'rule' => 'notEmpty', ), 'maxLength' => array( - // set to Saito admin pref in beforeValidate() - 'rule' => array('maxLength', 100), + 'rule' => 'validateSubjectMaxLength', ), ), 'category' => array( @@ -102,7 +101,7 @@ class Entry extends AppModel { 'views' => array( 'rule' => array('comparison', '>=', 0), ), - 'name' => array(), + 'name' => array() ); protected $fieldsToSanitize = array( @@ -206,6 +205,12 @@ class Entry extends AppModel { ] ]; + protected $_isInitialized = false; + + protected $_editPeriod = 1200; + + protected $_subjectMaxLenght = 100; + /** * Caching for isRoot() * @@ -213,6 +218,11 @@ class Entry extends AppModel { */ protected $_isRoot = []; + public function __construct($id = false, $table = null, $ds = null) { + $this->_initialize(); + return parent::__construct($id, $table, $ds); + } + public function getRecentEntries(array $options = [], SaitoUser $User) { Stopwatch::start('Model->User->getRecentEntries()'); @@ -280,6 +290,16 @@ public function getThreadId($id) { return $entry['Entry']['tid']; } + /** + * Shorthand for reading an entry with full data + */ + public function get($id, $unsanitized = false) { + return $this->find( + ($unsanitized) ? 'unsanitized' : 'entry', + ['conditions' => [$this->alias.'.id' => $id]] + ); + } + public function getParentId($id) { $entry = $this->find( 'first', @@ -337,14 +357,16 @@ public function createPosting($data, $CurrentUser = null) { if ($new_posting === false) { return false; } + $new_posting_id = $this->id; - if ($new_posting === true) { - $new_posting = $this->read(); - } + + // make sure we pass the complete ['Entry'] dataset to events + $this->contain(); + $new_posting = $this->read(); if ($this->isRoot($data)) { // thread-id of new thread is its own id - if ($this->save(['tid' => $new_posting_id]) === false) { + if ($this->save(['tid' => $new_posting_id], false, ['tid']) === false) { // @td raise error and/or roll back new entry return false; } else { @@ -353,6 +375,7 @@ public function createPosting($data, $CurrentUser = null) { } } else { // update last answer time of root entry + $this->clear(); $this->id = $new_posting[$this->alias]['tid']; $this->set('last_answer', $new_posting[$this->alias]['last_answer']); if ($this->save() === false) { @@ -360,25 +383,19 @@ public function createPosting($data, $CurrentUser = null) { return false; } - $this->getEventManager()->dispatch( - new CakeEvent( - 'Model.Entry.replyToEntry', - $this, - array( - 'subject' => $new_posting[$this->alias]['pid'], - 'data' => $new_posting, - ) - ) + $this->_dispatchEvent( + 'Model.Entry.replyToEntry', + [ + 'subject' => $new_posting[$this->alias]['pid'], + 'data' => $new_posting + ] ); - $this->getEventManager()->dispatch( - new CakeEvent( - 'Model.Entry.replyToThread', - $this, - array( - 'subject' => $new_posting[$this->alias]['tid'], - 'data' => $new_posting, - ) - ) + $this->_dispatchEvent( + 'Model.Entry.replyToThread', + [ + 'subject' => $new_posting[$this->alias]['tid'], + 'data' => $new_posting + ] ); } $this->id = $new_posting_id; @@ -390,7 +407,15 @@ public function update($data, $CurrentUser = null) { $this->_CurrentUser = $CurrentUser; } - $data[$this->alias]['id'] = $this->id; + if (empty($data[$this->alias]['id'])) { + throw new InvalidArgumentException('Missing entry id in arguments.'); + } + + $id = $data[$this->alias]['id']; + if (!$this->exists($id)) { + throw new NotFoundException(sprintf('Entry with id `%s` not found.', $id)); + } + $this->prepare($data, ['preFilterFields' => 'update']); // prevents normal user of changing category of complete thread when answering @@ -401,7 +426,30 @@ public function update($data, $CurrentUser = null) { $data[$this->alias]['edited'] = date('Y-m-d H:i:s'); $data[$this->alias]['edited_by'] = $this->_CurrentUser['username']; - return $this->save($data); + + $this->validator()->add( + 'edited_by', + 'isEditingAllowed', + [ + 'rule' => 'validateEditingAllowed' + ] + ); + + $result = $this->save($data); + + if ($result) { + $this->contain(); + $result = $this->read() + $data; + $this->_dispatchEvent( + 'Model.Entry.update', + [ + 'subject' => $result[$this->alias]['id'], + 'data' => $result + ] + ); + } + + return $result; } /* @mb `views` into extra related table if performance becomes a problem */ @@ -533,6 +581,18 @@ public function toggle($key) { if ($key === 'locked') { $this->_threadLock($result); } + + $this->contain(); + $entry = $this->read(); + + $this->_dispatchEvent( + 'Model.Entry.update', + [ + 'subject' => $entry[$this->alias]['id'], + 'data' => $entry + ] + ); + return $result; } @@ -554,10 +614,6 @@ public function beforeFind($queryData) { public function beforeValidate($options = array()) { parent::beforeValidate($options); - $this->validate['subject']['maxLength']['rule'][1] = Configure::read( - 'Saito.Settings.subject_maxlength' - ); - //* in n/t posting delete unnecessary body text if (isset($this->data['Entry']['text'])) { $this->data['Entry']['text'] = rtrim($this->data['Entry']['text']); @@ -583,7 +639,7 @@ public function deleteNode($id = null) { $ids_to_delete = $this->getIdsForNode($id); $success = $this->deleteAll( - ['Entry.id' => $ids_to_delete], + [$this->alias . '.id' => $ids_to_delete], true, true ); @@ -597,6 +653,11 @@ public function deleteNode($id = null) { foreach ($ids_to_delete as $entry_id) { $this->Esevent->deleteSubject($entry_id, 'entry'); } + + $this->_dispatchEvent( + 'Model.Thread.change', + ['subject' => $entry[$this->alias]['tid']] + ); endif; return $success; @@ -628,7 +689,7 @@ public function getIdsForNode($id) { public function anonymizeEntriesFromUser($user_id) { // remove username from all entries and reassign to anonyme user - return $this->updateAll( + $success = $this->updateAll( [ 'Entry.name' => "NULL", 'Entry.edited_by' => "NULL", @@ -637,6 +698,12 @@ public function anonymizeEntriesFromUser($user_id) { ], ['Entry.user_id' => $user_id] ); + + if ($success) { + $this->_dispatchEvent('Model.Thread.reset'); + } + + return $success; } /** @@ -647,7 +714,7 @@ public function anonymizeEntriesFromUser($user_id) { * @param misc $context Arbitrary data for the function. Useful for providing $this context. * @param array $tree The whole tree. */ - public static function mapTreeElements(&$leafs, $func, $context = null, &$tree = null) { + public static function mapTreeElements(&$leafs, callable $func, $context = null, &$tree = null) { if ($tree === null) { $tree = & $leafs; } @@ -718,9 +785,13 @@ public function threadMerge($targetId) { $this->Esevent->transferSubjectForEventType( $threadIdSource, - $targetEntry['Entry']['tid'], + $targetEntry[$this->alias]['tid'], 'thread' ); + $this->_dispatchEvent( + 'Model.Thread.change', + ['subject' => $targetEntry[$this->alias]['tid']] + ); return true; } return false; @@ -756,39 +827,43 @@ protected function _addAdditionalFields(&$entries) { /** * Check if someone is allowed to edit an entry - * - * @param array $entry - * @param SaitoUser $user - * @return boolean */ - public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) { - if ($CurrentUser === null) { - $CurrentUser = $this->_CurrentUser; + public function isEditingForbidden($entry, SaitoUser $User = null) { + + if ($User === null) { + $User = $this->_CurrentUser; } // Anon - if ($CurrentUser->isLoggedIn() === false) { + if ($User->isLoggedIn() !== true) { return true; } // Admins - if ($CurrentUser->isAdmin()) { + if ($User->isAdmin()) { return false; } $verboten = true; - $editPeriod = Configure::read('Saito.Settings.edit_period') * 60; - $expired = strtotime($entry['Entry']['time']) + $editPeriod; + if (!isset($entry['Entry'])) { + $entry = $this->get($entry); + } + + if (empty($entry)) { + throw new Exception(sprintf('Entry %s not found.', $entry)); + } + + $expired = strtotime($entry['Entry']['time']) + $this->_editPeriod; $isOverEditLimit = time() > $expired; - $isCurrentUsersPosting = (int)$CurrentUser->getId() + $isUsersPosting = (int)$User->getId() === (int)$entry['Entry']['user_id']; - if ($CurrentUser->isMod()) { + if ($User->isMod()) { // Mods // @todo mods don't edit admin posts - if ($isCurrentUsersPosting && $isOverEditLimit && + if ($isUsersPosting && $isOverEditLimit && /* Mods should be able to edit their own posts if they are pinned * * @todo this opens a 'mod can pin and then edit root entries'-loophole, @@ -805,7 +880,7 @@ public function isEditingForbidden(array $entry, SaitoUser $CurrentUser = null) } else { // Users - if ($isCurrentUsersPosting === false) { + if ($isUsersPosting === false) { $verboten = 'user'; } elseif ($isOverEditLimit) { $verboten = 'time'; @@ -896,7 +971,7 @@ public function prepare(&$data, array $options = []) { } else { $pid = $data[$this->alias]['pid']; } - $parent = $this->getUnsanitized($pid); + $parent = $this->get($pid, true); if ($parent === false) { throw new InvalidArgumentException; } @@ -937,20 +1012,22 @@ protected function _preFilterFields(&$data, $fields) { } } - public function getUnsanitized($id) { - $this->sanitize(false); - return $this->find( - 'entry', - [ - 'contain' => ['User', 'Category'], - 'conditions' => ['Entry.id' => $id] - ] - ); + protected function _findUnsanitized($state, $query, $results = []) { + if ($state === 'before') { + $query['sanitize'] = false; + } + return $this->_findEntry($state, $query, $results); } - protected function _findEntry($state, $query, $results = array()) { + protected function _findEntry($state, $query, $results = []) { if ($state === 'before') { - $query['contain'] = array('User', 'Category'); + if (isset($query['sanitize'])) { + if ($query['sanitize'] === false) { + $this->sanitize(false); + } + unset($query['sanitize']); + } + $query['contain'] = ['User', 'Category']; $query['fields'] = $this->threadLineFieldList . ',' . $this->showEntryFieldListAdditional; return $query; } @@ -1075,6 +1152,30 @@ public function validateCategoryIsAllowed($check) { return true; } + public function validateEditingAllowed($check) { + $forbidden = $this->isEditingForbidden($this->data['Entry']['id']); + if (is_bool($forbidden)) { + return !$forbidden; + } else { + return $forbidden; + } + } + + /** + * + * + * Don't use Cake's build in maxLength. Dynamically setting the length + * afterwards in $this->validates it is a bag of hurt with race + * conditions in ModelValidator::_parseRules() when checking + * if ($this->_validate === $this->_model->validate) is true. + * + * @param $check + * @return bool + */ + public function validateSubjectMaxLength($check) { + return mb_strlen($check['subject']) <= $this->_subjectMaxLenght; + } + /** * Changes the category of a thread. * @@ -1099,4 +1200,18 @@ protected function _threadChangeCategory($tid = null, $new_category_id = null) { ); return $out; } + + protected function _initialize() { + if ($this->_isInitialized) { + return; + } + $appSettings = Configure::read('Saito.Settings'); + if(isset($appSettings['edit_period'])) { + $this->_editPeriod = $appSettings['edit_period'] * 60; + } + if(isset($appSettings['subject_maxlength'])) { + $this->_subjectMaxLenght = (int)$appSettings['subject_maxlength']; + } + $this->_isInitialized = true; + } } \ No newline at end of file diff --git a/app/Model/Setting.php b/app/Model/Setting.php index 5df5c09de..4458b32cc 100755 --- a/app/Model/Setting.php +++ b/app/Model/Setting.php @@ -27,7 +27,9 @@ public function getSettings() { $settings = $this->find('all'); $settings = $this->_compactKeyValue($settings); - $settings['userranks_ranks'] = $this->_pipeSplitter($settings['userranks_ranks']); + $ranks = $this->_pipeSplitter($settings['userranks_ranks']); + ksort($ranks); + $settings['userranks_ranks'] = $ranks; return $settings; } diff --git a/app/Test/Case/Controller/EntriesControllerTest.php b/app/Test/Case/Controller/EntriesControllerTest.php index 0684a7ee8..59074a04e 100755 --- a/app/Test/Case/Controller/EntriesControllerTest.php +++ b/app/Test/Case/Controller/EntriesControllerTest.php @@ -110,7 +110,7 @@ public function testCategoryChooserNotLoggedIn() { $User = new CurrentUserComponent(new ComponentCollection()); $User->set(array()); $Entries->getInitialThreads($User); - $this->assertFalse($Entries->viewVars['categoryChooserIsUsed']); + $this->assertFalse(isset($Entries->viewVars['categoryChooser'])); } /** @@ -154,7 +154,7 @@ public function testCategoryChooserDeactivated() { 'user_category_override' => 1, )); $Entries->getInitialThreads($User); - $this->assertFalse($Entries->viewVars['categoryChooserIsUsed']); + $this->assertFalse(isset($Entries->viewVars['categoryChooser'])); } public function testCategoryChooserEmptyCustomSet() { @@ -196,7 +196,7 @@ public function testCategoryChooserEmptyCustomSet() { 'user_category_override' => 1, )); $Entries->getInitialThreads($User); - $this->assertTrue($Entries->viewVars['categoryChooserIsUsed']); + $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserTitleId'], 'All Categories'); } @@ -248,9 +248,9 @@ public function testCategoryChooserCustomSet() { 'user_category_custom' => array(1 => 1, 2 => 1, 7 => 0), )); $Entries->getInitialThreads($User); - $this->assertTrue($Entries->viewVars['categoryChooserIsUsed']); + $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserChecked'], array( - '2' => 1, + '2' => '2', '8' => '8', )); $this->assertEqual($Entries->viewVars['categoryChooser'], array( @@ -298,29 +298,28 @@ public function testCategoryChooserSingleCategory() { 'user_category_custom' => array(1 => 1, 2 => 1, 7 => 0), )); $Entries->getInitialThreads($User); - $this->assertTrue($Entries->viewVars['categoryChooserIsUsed']); + $this->assertTrue(isset($Entries->viewVars['categoryChooser'])); $this->assertEqual($Entries->viewVars['categoryChooserTitleId'], 7); $this->assertEqual($Entries->viewVars['categoryChooserChecked'], array( - '1' => 1, - '2' => 1, + '1' => '1', + '2' => '2', )); } public function testIndex() { - - $Entries = $this->generate('Entries'); + $this->generate('Entries'); $this->_logoutUser(); //* not logged in user - $result = $this->testAction('/entries/index', array('return' => 'vars')); + $result = $this->testAction('/entries/index', array('return' => 'vars')); $entries = $result['entries']; - $this->assertEqual(count($entries), 1); + $this->assertEqual(count($entries), 2); //* logged in user $this->_loginUser(3); - $result = $this->testAction('/entries/index', array('return' => 'vars')); + $result = $this->testAction('/entries/index', array('return' => 'vars')); $entries = $result['entries']; - $this->assertEqual(count($entries), 2); + $this->assertEqual(count($entries), 3); } public function testMergeNoSourceId() { @@ -459,7 +458,7 @@ public function testEditNoEntry() { public function testEditNoEntryId() { $Entries = $this->generate('Entries'); $this->_loginUser(2); - $this->expectException('NotFoundException'); + $this->expectException('BadRequestException'); $this->testAction('entries/edit/'); } @@ -495,53 +494,6 @@ public function testEditShowForm() { $this->assertPattern('/data\[Event\]\[2\]\[event_type_id\]"\s+?checked="checked"/', $result); } - public function testEmptyCache() { - - $Entries = $this->generate('Entries', array( - 'components' => array( - 'CacheTree' => array('delete'), - ) - )); - - $this->_loginUser(1); - - $data['Entry'] = array( - 'pid' => 5, - 'subject' => 'test', - 'category' => 4, - ); - - /* - * test entries/add - */ - $Entries->CacheTree - ->expects($this->once()) - ->method('delete') - ->with($this->equalTo('4')); - - $this->testAction( - '/entries/add/5', - array('data' => $data, 'method' => 'post') - ); - - /* - * Test entries/edit - */ - $Entries = $this->generate('Entries', array( - 'components' => array( - 'CacheTree' => array('delete'), - ) - )); - - $Entries->CacheTree - ->expects($this->once()) - ->method('delete') - ->with($this->equalTo('4')); - $result = $this->testAction('/entries/edit/5', array( - 'data' => $data, - 'method' => 'post')); - } - public function testPreviewLoggedIn() { $this->setExpectedException('ForbiddenException'); $this->testAction('/entries/preview'); @@ -678,8 +630,8 @@ public function testAppStats() { $this->assertEqual($headerCounter['user_online'], 1); $this->assertEqual($headerCounter['user'], 6); - $this->assertEqual($headerCounter['entries'], 9); - $this->assertEqual($headerCounter['threads'], 3); + $this->assertEqual($headerCounter['entries'], 10); + $this->assertEqual($headerCounter['threads'], 4); $this->assertEqual($headerCounter['user_registered'], 0); $this->assertEqual($headerCounter['user_anonymous'], 1); diff --git a/app/Test/Case/Controller/ToolsControllerTest.php b/app/Test/Case/Controller/ToolsControllerTest.php index 8791b6e31..b33b32361 100644 --- a/app/Test/Case/Controller/ToolsControllerTest.php +++ b/app/Test/Case/Controller/ToolsControllerTest.php @@ -45,29 +45,14 @@ public function testAdminEmptyCachesUser() { } public function testAdminEmptyCaches() { - $Tools = $this->generate('Tools', - array( - 'components' => array( - 'CacheSupport' => array( - 'clearAll' - ), - ) - ) + $Tools = $this->generate( + 'Tools', + ['components' => ['CacheSupport' => ['clear']]] ); $this->_loginUser(1); $Tools->CacheSupport->expects($this->once()) - ->method('clearAll'); + ->method('clear'); $this->testAction('admin/tools/emptyCaches'); } - - /** - * testClearCache method - * - * @return void - */ - public function testClearCache() { - - } - } diff --git a/app/Test/Case/Controller/Component/CacheTreeComponentTest.php b/app/Test/Case/Lib/CacheTreeTest.php similarity index 86% rename from app/Test/Case/Controller/Component/CacheTreeComponentTest.php rename to app/Test/Case/Lib/CacheTreeTest.php index 4fad92ddd..ea9860cd7 100644 --- a/app/Test/Case/Controller/Component/CacheTreeComponentTest.php +++ b/app/Test/Case/Lib/CacheTreeTest.php @@ -1,9 +1,12 @@ _cachedEntries = $data; @@ -30,7 +33,7 @@ public function setAllowUpdate($state) { * CacheTreeComponent Test Case * */ - class CacheTreeComponentTest extends CakeTestCase { + class CacheTreeTest extends CakeTestCase { /** * setUp method @@ -39,8 +42,7 @@ class CacheTreeComponentTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $Collection = new ComponentCollection(); - $this->CacheTree = new CacheTreeComponentMock($Collection); + $this->CacheTree = new CacheTreeMock(); $cacheData = array( '1' => array( @@ -215,51 +217,5 @@ public function testReset() { $result = $this->CacheTree->read(); $this->assertEqual($result, []); } - - /** - * testDelete method - * - * @return void - */ - public function testDelete() { - - } - - /** - * testRead method - * - * @return void - */ - public function testRead() { - - } - - /** - * testUpdate method - * - * @return void - */ - public function testUpdate() { - - } - - /** - * testReadCache method - * - * @return void - */ - public function testReadCache() { - - } - - /** - * testSaveCache method - * - * @return void - */ - public function testSaveCache() { - - } - } diff --git a/app/Test/Case/Model/CategoryTest.php b/app/Test/Case/Model/CategoryTest.php index d8bf5d447..eadc4d8f6 100755 --- a/app/Test/Case/Model/CategoryTest.php +++ b/app/Test/Case/Model/CategoryTest.php @@ -54,6 +54,45 @@ public function testGetCategoriesForAccession() { $this->assertEqual($result, $expected); } + public function testUpdateEvent() { + $Category = $this->getMockForModel('Category', ['_dispatchEvent']); + $Category->expects($this->once()) + ->method('_dispatchEvent') + ->with('Model.Category.update'); + + $data = [ + 'Category' => [ + 'category' => 'foo' + ] + ]; + $Category->id = 1; + $Category->save($data); + } + + public function testNoUpdateEvent() { + $Category = $this->getMockForModel('Category', ['_dispatchEvent']); + $Category->expects($this->never()) + ->method('_dispatchEvent'); + + $data = [ + 'Category' => [ + 'thread_count' => '300' + ] + ]; + $Category->id = 1; + $Category->save($data); + } + + public function testDeleteEvent() { + $Category = $this->getMockForModel('Category', ['_dispatchEvent']); + $Category->expects($this->once()) + ->method('_dispatchEvent') + ->with('Model.Category.delete'); + + $Category->id = 1; + $Category->delete(); + } + public function setUp() { parent::setup(); $this->Category = ClassRegistry::init('Category'); @@ -65,5 +104,3 @@ public function tearDown(){ } } - -?> \ No newline at end of file diff --git a/app/Test/Case/Model/EntryTest.php b/app/Test/Case/Model/EntryTest.php index dc6a319d4..371d3c6d9 100755 --- a/app/Test/Case/Model/EntryTest.php +++ b/app/Test/Case/Model/EntryTest.php @@ -6,6 +6,15 @@ class EntryMock extends Entry { public $_CurrentUser; + public $_editPeriod; + + public function prepareBbcode($string) { + return $string; + } + + public function getSubjectMaxLength() { + return $this->_subjectMaxLenght; + } } class EntryTest extends CakeTestCase { @@ -19,6 +28,8 @@ class EntryTest extends CakeTestCase { 'app.esnotification', ); + + public function testBeforeValidate() { //* save entry with text @@ -30,6 +41,48 @@ public function testBeforeValidate() { ); } + public function testCreateSuccess() { + App::uses('Category', 'Model'); + + $SaitoUser = $this->getMock( + 'SaitoUser', + ['getMaxAccession', 'getId', 'getBookmarks'], + [new ComponentCollection] + ); + $SaitoUser->expects($this->any()) + ->method('getMaxAccession') + ->will($this->returnValue(2)); + $SaitoUser->expects($this->any()) + ->method('getId') + ->will($this->returnValue(1)); + $this->Entry->_CurrentUser = $SaitoUser; + + $data[$this->Entry->alias] = [ + 'pid' => 0, + // +1 because str_pad calculates non ascii chars to a string length of 2 + 'subject' => str_pad('Sübject', $this->Entry->getSubjectMaxLength() + 1, '.'), + 'text' => 'Täxt', + 'category' => 1 + ]; + + $this->Entry->createPosting($data); + $result = $this->Entry->get($this->Entry->id, true); + + $this->assertEmpty($this->Entry->validationErrors); + + $expected = $data; + $result = array_intersect_key($result, $expected); + $result[$this->Entry->alias] = array_intersect_key( + $result[$this->Entry->alias], + $expected[$this->Entry->alias] + ); + + $this->assertEqual( + $result, + $expected + ); + } + public function testCreateCategoryThreadCounterUpdate() { App::uses('Category', 'Model'); @@ -38,10 +91,10 @@ public function testCreateCategoryThreadCounterUpdate() { ['getMaxAccession', 'getId'], [new ComponentCollection] ); - $SaitoUser->expects($this->once()) + $SaitoUser->expects($this->any()) ->method('getMaxAccession') ->will($this->returnValue(2)); - $SaitoUser->expects($this->once()) + $SaitoUser->expects($this->any()) ->method('getId') ->will($this->returnValue(1)); $this->Entry->_CurrentUser = $SaitoUser; @@ -289,7 +342,7 @@ public function testAnonymizeEntriesFromUser() { $this->assertEqual($result, $expected); // entries are now assigned to user_id 0 - $expected = 6; + $expected = 7; $result = $this->Entry->find('count', array( 'conditions' => array ('Entry.user_id' => 0) )); @@ -340,11 +393,12 @@ public function testIsAnsweringForbidden() { } public function testIsEditingForbiddenSuccess() { + $this->Entry->_editPeriod = 1200; $entry = array( 'Entry' => array( 'user_id' => 1, 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60 ) + 1), + time() - $this->Entry->_editPeriod + 1), 'locked' => 0, ) ); @@ -414,13 +468,17 @@ public function testIsEditingForbiddenWrongUser() { } public function testIsEditingForbiddenToLate() { - $entry = array( - 'Entry' => array( - 'user_id' => 1, - 'time' => strftime("%c", - time() - (Configure::read('Saito.Settings.edit_period') * 60 ) - 1), + $this->Entry->_editPeriod = 1200; + $entry = [ + 'Entry' => [ + 'user_id' => 1, + 'locked' => false, + 'time' => strftime( + "%c", + time() - $this->Entry->_editPeriod - 1 ) - ); + ] + ]; $user = array( 'id' => 1, 'user_type' => 'user', @@ -591,6 +649,21 @@ public function testGetThreadIdNotFound() { $result = $this->Entry->getThreadId(999); } + public function testUpdateNoId() { + $this->expectException('InvalidArgumentException'); + $this->Entry->update([]); + } + + /** + * Throw error if entry to update does not exist. + * + * Don't accidentally `create`. + */ + public function testUpdateEntryDoesNotExist() { + $this->expectException('NotFoundException'); + $this->Entry->update(['Entry' => ['id' => 999]]); + } + /** * setUp method * diff --git a/app/Test/Case/Model/SettingTest.php b/app/Test/Case/Model/SettingTest.php index 46158a441..a290488bb 100755 --- a/app/Test/Case/Model/SettingTest.php +++ b/app/Test/Case/Model/SettingTest.php @@ -24,7 +24,8 @@ class SettingTest extends CakeTestCase { 'subject_maxlength' => 100, 'tos_enabled' => 1, 'tos_url' => 'http://example.com/tos-url.html/', - 'thread_depth_indent' => '25' + 'thread_depth_indent' => '25', + 'edit_period' => '20' ); public function testAfterSave() { diff --git a/app/Test/Case/Model/UserOnlineTest.php b/app/Test/Case/Model/UserOnlineTest.php index 3238945ed..003f1822c 100644 --- a/app/Test/Case/Model/UserOnlineTest.php +++ b/app/Test/Case/Model/UserOnlineTest.php @@ -96,13 +96,21 @@ public function testSetOffline() { //* insert new user $user_id = 5; - $this->_startUsersOnline[0]['UserOnline'] = array('user_id' => '5', 'time' => time(), 'logged_in' => 1 ); + $this->_startUsersOnline[0]['UserOnline'] = [ + 'user_id' => '5', + 'logged_in' => 1 + ]; $this->UserOnline->setOnline($user_id, TRUE); //* test if user is inserted $this->UserOnline->contain(); $result = $this->UserOnline->find('all', $this->_fields); $expected = $this->_startUsersOnline; + + $time = $result[0]['UserOnline']['time']; + $this->assertGreaterThan(time() - 5, $time); + unset($result[0]['UserOnline']['time'], $time); + $this->assertEqual($result, $expected); //* try to delte new user diff --git a/app/Test/Case/Model/UserTest.php b/app/Test/Case/Model/UserTest.php index 9305f355b..a20546057 100755 --- a/app/Test/Case/Model/UserTest.php +++ b/app/Test/Case/Model/UserTest.php @@ -151,9 +151,9 @@ public function testNumberOfEntry() { $result = $this->User->numberOfEntries(); $this->assertEqual($expected, $result); - //* two entries + //* multiple entries $this->User->id = 3; - $expected = 6; + $expected = 7; $result = $this->User->numberOfEntries(); $this->assertEqual($expected, $result); } diff --git a/app/Test/Fixture/EntryFixture.php b/app/Test/Fixture/EntryFixture.php index 8622781c8..937543941 100755 --- a/app/Test/Fixture/EntryFixture.php +++ b/app/Test/Fixture/EntryFixture.php @@ -45,17 +45,18 @@ class EntryFixture extends CakeTestFixture { */ public $records = array( //* thread 1 - array( - 'id' => 1, - 'subject' => 'First_Subject', - 'text' => 'First_Text', - 'pid' => 0, - 'tid' => 1, - 'time' => '2000-01-01 20:00:00', - 'last_answer' => '2000-01-04 20:02:00', - 'category' => 2, // accession = 0 - 'user_id' => 3, - ), + [ + 'id' => 1, + 'subject' => 'First_Subject', + 'text' => 'First_Text', + 'pid' => 0, + 'tid' => 1, + 'time' => '2000-01-01 20:00:00', + 'last_answer' => '2000-01-04 20:02:00', + 'category' => 2, // accession = 0 + 'user_id' => 3, + 'locked' => 0 + ], array( 'id' => 2, 'subject' => 'Second_Subject', @@ -66,6 +67,7 @@ class EntryFixture extends CakeTestFixture { 'last_answer' => '2000-01-01 20:01:00', 'category' => 2, 'user_id' => 2, + 'locked' => 0 ), array( 'id' => 3, @@ -81,6 +83,7 @@ class EntryFixture extends CakeTestFixture { 'edited' => '2000-01-01 20:04:00', 'edited_by' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), array( 'id' => 7, @@ -94,6 +97,7 @@ class EntryFixture extends CakeTestFixture { 'user_id' => 3, 'name' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), array( 'id' => 8, @@ -107,6 +111,7 @@ class EntryFixture extends CakeTestFixture { 'user_id' => 3, 'name' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), array( 'id' => 9, @@ -120,6 +125,7 @@ class EntryFixture extends CakeTestFixture { 'user_id' => 3, 'name' => 'Ulysses', 'ip' => '1.1.1.1', + 'locked' => 0 ), //* thread 2 array( @@ -133,6 +139,7 @@ class EntryFixture extends CakeTestFixture { // accession = 1 'category' => 4, 'user_id' => 1, + 'locked' => 1 ), array( 'id' => 5, @@ -148,6 +155,7 @@ class EntryFixture extends CakeTestFixture { 'edited' => '0000-00-00 00:00:00', 'edited_by' => NULL, 'ip' => '1.1.1.1', + 'locked' => 1 ), // thread 3 array( @@ -158,14 +166,30 @@ class EntryFixture extends CakeTestFixture { 'tid' => 6, 'time' => '2000-01-01 11:00:00', 'last_answer' => '2000-01-01 11:00:00', + // accession = 2, 'category' => 1, 'user_id' => 1, 'name' => 'Alice', 'edited' => '0000-00-00 00:00:00', 'edited_by' => NULL, 'ip' => '1.1.1.3', + 'locked' => 0 ), - + // thread 4 public locked + [ + 'id' => 10, + 'subject' => 'First_Subject', + 'text' => 'First_Text', + 'pid' => 0, + 'tid' => 10, + 'time' => '2000-01-01 10:59:00', + 'last_answer' => '2000-01-01 10:59:00', + 'edited' => '0000-00-00 00:00:00', + 'edited_by' => null, + 'category' => 2, // accession = 0 + 'user_id' => 3, + 'locked' => 1 + ] ); } diff --git a/app/Test/Fixture/SettingFixture.php b/app/Test/Fixture/SettingFixture.php index 42061a95c..c46f462da 100755 --- a/app/Test/Fixture/SettingFixture.php +++ b/app/Test/Fixture/SettingFixture.php @@ -61,7 +61,11 @@ class SettingFixture extends CakeTestFixture { array( 'name' => 'thread_depth_indent', 'value' => 25, - ) + ), + [ + 'name' => 'edit_period', + 'value' => 20, + ] ); } diff --git a/app/View/Admins/admin_index.ctp b/app/View/Admins/admin_index.ctp index 797d7b652..d905d7082 100755 --- a/app/View/Admins/admin_index.ctp +++ b/app/View/Admins/admin_index.ctp @@ -6,7 +6,7 @@ You are running Saito version: .

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

Saito believes its base-URL is: request->webroot ?>. diff --git a/app/View/Elements/entry/view_content.ctp b/app/View/Elements/entry/view_content.ctp index fd72b7d4d..8c6b3bd6a 100644 --- a/app/View/Elements/entry/view_content.ctp +++ b/app/View/Elements/entry/view_content.ctp @@ -24,7 +24,7 @@ echo $subject; } ?> - – + – isLoggedIn()) : ?> Html->link( @@ -48,7 +48,7 @@ TimeH->formatTime($entry['Entry']['time']); ?>">TimeH->formatTime($entry['Entry']['time'], 'glasen'); ?>, */ ?> - TimeH->formatTime($entry['Entry']['time']); if (isset($entry['Entry']['edited_by']) && !is_null($entry['Entry']['edited_by']) && strtotime($entry['Entry']['edited']) > strtotime($entry['Entry']['time']) + ( Configure::read('Saito.Settings.edit_delay') * 60 ) @@ -72,7 +72,7 @@ } if ( Configure::read('Saito.Settings.store_ip') && $CurrentUser->isMod() ): ?>, IP: - diff --git a/app/View/Entries/add.ctp b/app/View/Entries/add.ctp index 5c9f1062d..5d6aaa3ef 100644 --- a/app/View/Entries/add.ctp +++ b/app/View/Entries/add.ctp @@ -84,7 +84,10 @@ ), 'label' => false, 'tabindex' => 2, - 'error' => ['notEmpty' => __('error_subject_empty')], + 'error' => [ + 'notEmpty' => __('error_subject_empty'), + 'maxLength' => __('error_subject_max_length') + ], 'div' => ['class' => 'required'], 'placeholder' => (!empty($citeSubject)) ? $citeSubject : __('Subject'), 'required' => ($posting_type === 'reply') ? false : "required" diff --git a/app/View/Entries/index.ctp b/app/View/Entries/index.ctp index fdc59f0f0..53bee6f7f 100644 --- a/app/View/Entries/index.ctp +++ b/app/View/Entries/index.ctp @@ -26,7 +26,7 @@ $this->end(); $this->start('headerSubnavRightTop'); - if (isset($categoryChooserIsUsed) && $categoryChooserIsUsed): + if (isset($categoryChooser)): // category-chooser link echo $this->Html->link( ' ' diff --git a/app/View/Helper/AppHelper.php b/app/View/Helper/AppHelper.php index b8b02d0cf..d1734ad75 100755 --- a/app/View/Helper/AppHelper.php +++ b/app/View/Helper/AppHelper.php @@ -33,13 +33,11 @@ class AppHelper extends Helper { public function getAssetTimestamp($path) { - $filepath = preg_replace( - '/^' . preg_quote($this->request->webroot, '/') . '/', - '', - $path - ); - $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath); - return @filemtime($webrootPath); + $pathWithTimestamp = $this->assetTimestamp($path); + // extracts integer unixtimestamp from `path/asset.ext? + if ($pathWithTimestamp) { + preg_match('/(?<=\?)[\d]+(?=$|\?|\&)/', $pathWithTimestamp, $matches); + return (int)$matches[0]; + } } - } \ No newline at end of file diff --git a/app/View/Helper/BbcodeHelper.php b/app/View/Helper/BbcodeHelper.php index 8cffde80e..dfd00c442 100644 --- a/app/View/Helper/BbcodeHelper.php +++ b/app/View/Helper/BbcodeHelper.php @@ -114,14 +114,16 @@ public function beforeRender($viewFile) { * @param array $options * @return string */ - public function parse($string, array $options = array( )) { - Stopwatch::start('Bbcode::parse'); - $this->_initParser($options); - $this->_tagElCounter = 0; - $this->_tagEls = array(); - $string = $this->_Parser->parse($string); - $string = $this->_detaginize($string); - Stopwatch::stop('Bbcode::parse'); + public function parse($string, array $options = []) { + if (empty($string) === false && $string !== 'n/t') { + Stopwatch::start('Bbcode::parse'); + $this->_initParser($options); + $this->_tagElCounter = 0; + $this->_tagEls = array(); + $string = $this->_Parser->parse($string); + $string = $this->_detaginize($string); + Stopwatch::stop('Bbcode::parse'); + } return $string; } diff --git a/app/View/Helper/SettingHelper.php b/app/View/Helper/SettingHelper.php new file mode 100644 index 000000000..bf8b4ea63 --- /dev/null +++ b/app/View/Helper/SettingHelper.php @@ -0,0 +1,71 @@ + $table_name + ]; + $options += $defaults; + + $out = $this->tableHeaders(); + $anchors = ''; + foreach ($setting_names as $name) { + $out .= $this->tableRow($name, $Settings); + $anchors .= ''; + } + $key = $this->addHeader($options['nav-title']); + $out = '' + . $out + . '
'; + $out = '

' + . $anchors + . '

' . $table_name . '

' + . $out; + return $out; + } + + public function addHeader($header) { + $id = count($this->_headers) + 1; + $this->_headers[$id] = $header; + return $id; + } + + public function getHeaders() { + return $this->_headers; + } + + public function tableRow($name, $Settings) { + return $this->Html->tableCells( + [ + __d('nondynamic', $name), + $Settings[$name], + "

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

", + $this->Html->link( + __('edit'), + ['controller' => 'settings', 'action' => 'edit', $name], + ['class' => 'btn'] + ) + ] + ); + } + + public function tableHeaders() { + return $this->Html->tableHeaders( + [ + __('Key'), + __('Value'), + __('Explanation'), + __('Actions') + ] + ); + } + } diff --git a/app/View/Settings/admin_edit.ctp b/app/View/Settings/admin_edit.ctp index bf1bc4cd1..a2ccc938e 100755 --- a/app/View/Settings/admin_edit.ctp +++ b/app/View/Settings/admin_edit.ctp @@ -1,8 +1,8 @@ Html->addCrumb(__('Settings'), '/admin/settings'); - $this->Html->addCrumb(__($this->request->data['Setting']['name']), '#'); + $this->Html->addCrumb(__d('nondynamic', $this->request->data['Setting']['name']), '#'); ?> -

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

+

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

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

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

+

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

\ No newline at end of file diff --git a/app/View/Settings/admin_index.ctp b/app/View/Settings/admin_index.ctp index e5bd4c2fd..d14afde11 100644 --- a/app/View/Settings/admin_index.ctp +++ b/app/View/Settings/admin_index.ctp @@ -1,724 +1,123 @@ Html->addCrumb(__('Settings'), '/admin/settings'); - $tableHeadersHtml = $this->Html->tableHeaders(array( - __('Key'), - __('Value'), - __('Explanation'), - __('Actions') - )); -?> -
-

-

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

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

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

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

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

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

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

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

- -

+ echo $this->Setting->table( + __('Email'), + ['forum_email'], + $Settings + ); -

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Shoutbox

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

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

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

Uploads

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

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

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

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

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

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

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

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

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

- - - Html->tableCells(array( - array( - __('embedly_enabled'), - $Settings['embedly_enabled'], - __('embedly_enabled_exp'), - $this->Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'embedly_enabled' ), - array( 'class' => 'btn' ) - ) - ), - array( - __('embedly_key'), - $Settings['embedly_key'], - __('embedly_key_exp'), - $this->Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'embedly_key' ), - array( 'class' => 'btn' ) - ) - ) - )); - ?> -
+ echo $this->Setting->table( + $this->Html->link('Flattr', 'http://flattr.com/'), + ['flattr_enabled', 'flattr_language', 'flattr_category'], + $Settings, + ['nav-title' => 'Flattr'] + ); -

- - - Html->tableCells(array( - array( - __('stopwatch_get'), - $Settings['stopwatch_get'], - __('stopwatch_get_exp'), - $this->Html->link( - __('edit'), - array( 'controller' => 'settings', 'action' => 'edit', 'stopwatch_get' ), - array( 'class' => 'btn' ) - ) - ), - )); - ?> -
+ echo $this->Setting->table( + $this->Html->link('Embed.ly', 'http://embed.ly/'), + ['embedly_enabled', 'embedly_key'], + $Settings, + ['nav-title' => 'Embedly'] + ); + + echo $this->Setting->table( + __('Debug'), + ['stopwatch_get'], + $Settings + ); + $this->end('settings'); +?> +
+
+ +
+

+ fetch('settings') ?> +
+
+ diff --git a/app/webroot/js/main-prod.js b/app/webroot/js/main-prod.js index cec341ece..c117205df 100644 --- a/app/webroot/js/main-prod.js +++ b/app/webroot/js/main-prod.js @@ -97,4 +97,4 @@ * @license MIT License (see LICENSE.txt) */ -function FastClick(e){var t,n=this;this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.layer=e;if(!e||!e.nodeType)throw new TypeError("Layer must be a document node");this.onClick=function(){return FastClick.prototype.onClick.apply(n,arguments)},this.onMouse=function(){return FastClick.prototype.onMouse.apply(n,arguments)},this.onTouchStart=function(){return FastClick.prototype.onTouchStart.apply(n,arguments)},this.onTouchEnd=function(){return FastClick.prototype.onTouchEnd.apply(n,arguments)},this.onTouchCancel=function(){return FastClick.prototype.onTouchCancel.apply(n,arguments)};if(FastClick.notNeeded())return;this.deviceIsAndroid&&(e.addEventListener("mouseover",this.onMouse,!0),e.addEventListener("mousedown",this.onMouse,!0),e.addEventListener("mouseup",this.onMouse,!0)),e.addEventListener("click",this.onClick,!0),e.addEventListener("touchstart",this.onTouchStart,!1),e.addEventListener("touchend",this.onTouchEnd,!1),e.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(e.removeEventListener=function(t,n,r){var i=Node.prototype.removeEventListener;t==="click"?i.call(e,t,n.hijacked||n,r):i.call(e,t,n,r)},e.addEventListener=function(t,n,r){var i=Node.prototype.addEventListener;t==="click"?i.call(e,t,n.hijacked||(n.hijacked=function(e){e.propagationStopped||n(e)}),r):i.call(e,t,n,r)}),typeof e.onclick=="function"&&(t=e.onclick,e.addEventListener("click",function(e){t(e)},!1),e.onclick=null)}define("domReady",[],function(){function u(e){var t;for(t=0;t2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?null:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or||n===void 0)return 1;if(n>>1;n.call(r,e[u])=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&(t[t.length]=n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]==null&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var O=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=O(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&O(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return O(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i":">",'"':""","'":"'","/":"/"}};M.unescape=x.invert(M.escape);var _={escape:new RegExp("["+x.keys(M.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(M.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(_[e],function(t){return M[e][t]})}}),x.result=function(e,t){if(e==null)return null;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),j.call(this,n.apply(x,e))}})};var D=0;x.uniqueId=function(e){var t=++D+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var P=/(.)^/,H={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||P).source,(n.interpolate||P).source,(n.evaluate||P).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(B,function(e){return"\\"+H[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var j=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],j.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return j.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this),define("underscore",function(e){return function(){return e._}}(this)),function(){var e=this,t=e.Backbone,n=[],r=n.push,i=n.slice,s=n.splice,o;typeof exports!="undefined"?o=exports:o=e.Backbone={},o.VERSION="1.0.0";var u=e._;!u&&typeof require!="undefined"&&(u=require("underscore")),o.$=e.jQuery||e.Zepto||e.ender||e.$,o.noConflict=function(){return e.Backbone=t,this},o.emulateHTTP=!1,o.emulateJSON=!1;var a=o.Events={on:function(e,t,n){if(!l(this,"on",e,[t,n])||!t)return this;this._events||(this._events={});var r=this._events[e]||(this._events[e]=[]);return r.push({callback:t,context:n,ctx:n||this}),this},once:function(e,t,n){if(!l(this,"once",e,[t,n])||!t)return this;var r=this,i=u.once(function(){r.off(e,i),t.apply(this,arguments)});return i._callback=t,this.on(e,i,n)},off:function(e,t,n){var r,i,s,o,a,f,c,h;if(!this._events||!l(this,"off",e,[t,n]))return this;if(!e&&!t&&!n)return this._events={},this;o=e?[e]:u.keys(this._events);for(a=0,f=o.length;a").attr(e);this.setElement(t,!1)}else this.setElement(u.result(this,"el"),!1)}}),o.sync=function(e,t,n){var r=T[e];u.defaults(n||(n={}),{emulateHTTP:o.emulateHTTP,emulateJSON:o.emulateJSON});var i={type:r,dataType:"json"};n.url||(i.url=u.result(t,"url")||B()),n.data==null&&t&&(e==="create"||e==="update"||e==="patch")&&(i.contentType="application/json",i.data=JSON.stringify(n.attrs||t.toJSON(n))),n.emulateJSON&&(i.contentType="application/x-www-form-urlencoded",i.data=i.data?{model:i.data}:{});if(n.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){i.type="POST",n.emulateJSON&&(i.data._method=r);var s=n.beforeSend;n.beforeSend=function(e){e.setRequestHeader("X-HTTP-Method-Override",r);if(s)return s.apply(this,arguments)}}i.type!=="GET"&&!n.emulateJSON&&(i.processData=!1),i.type==="PATCH"&&window.ActiveXObject&&(!window.external||!window.external.msActiveXFilteringEnabled)&&(i.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")});var a=n.xhr=o.ajax(u.extend(i,n));return t.trigger("request",t,a,n),a};var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};o.ajax=function(){return o.$.ajax.apply(o.$,arguments)};var N=o.Router=function(e){e||(e={}),e.routes&&(this.routes=e.routes),this._bindRoutes(),this.initialize.apply(this,arguments)},C=/\((.*?)\)/g,k=/(\(\?)?:\w+/g,L=/\*\w+/g,A=/[\-{}\[\]+?.,\\\^$|#\s]/g;u.extend(N.prototype,a,{initialize:function(){},route:function(e,t,n){u.isRegExp(e)||(e=this._routeToRegExp(e)),u.isFunction(t)&&(n=t,t=""),n||(n=this[t]);var r=this;return o.history.route(e,function(i){var s=r._extractParameters(e,i);n&&n.apply(r,s),r.trigger.apply(r,["route:"+t].concat(s)),r.trigger("route",t,s),o.history.trigger("route",r,t,s)}),this},navigate:function(e,t){return o.history.navigate(e,t),this},_bindRoutes:function(){if(!this.routes)return;this.routes=u.result(this,"routes");var e,t=u.keys(this.routes);while((e=t.pop())!=null)this.route(e,this.routes[e])},_routeToRegExp:function(e){return e=e.replace(A,"\\$&").replace(C,"(?:$1)?").replace(k,function(e,t){return t?e:"([^/]+)"}).replace(L,"(.*?)"),new RegExp("^"+e+"$")},_extractParameters:function(e,t){var n=e.exec(t).slice(1);return u.map(n,function(e){return e?decodeURIComponent(e):null})}});var O=o.History=function(){this.handlers=[],u.bindAll(this,"checkUrl"),typeof window!="undefined"&&(this.location=window.location,this.history=window.history)},M=/^[#\/]|\s+$/g,_=/^\/+|\/+$/g,D=/msie [\w.]+/,P=/\/$/;O.started=!1,u.extend(O.prototype,a,{interval:50,getHash:function(e){var t=(e||this).location.href.match(/#(.*)$/);return t?t[1]:""},getFragment:function(e,t){if(e==null)if(this._hasPushState||!this._wantsHashChange||t){e=this.location.pathname;var n=this.root.replace(P,"");e.indexOf(n)||(e=e.substr(n.length))}else e=this.getHash();return e.replace(M,"")},start:function(e){if(O.started)throw new Error("Backbone.history has already been started");O.started=!0,this.options=u.extend({},{root:"/"},this.options,e),this.root=this.options.root,this._wantsHashChange=this.options.hashChange!==!1,this._wantsPushState=!!this.options.pushState,this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var t=this.getFragment(),n=document.documentMode,r=D.exec(navigator.userAgent.toLowerCase())&&(!n||n<=7);this.root=("/"+this.root+"/").replace(_,"/"),r&&this._wantsHashChange&&(this.iframe=o.$(''}};return r}),define("text!templates/mediaInsert.html",[],function(){return'
\n \n \n
\n
\n
\n \n
\n
\n
\n \n
\n'}),define("views/mediaInsert",["jquery","underscore","backbone","models/app","lib/saito/markItUp.media","text!templates/mediaInsert.html"],function(e,t,n,r,i,s){return n.View.extend({template:t.template(s),events:{"click #markitup_media_btn":"_insert"},initialize:function(){this.model!==undefined&&this.model!==null&&this.listenTo(this.model,"change:isAnsweringFormShown",this.remove)},_insert:function(t){var n,s;t.preventDefault(),s=i,n=s.multimedia(this.$("#markitup_media_txta").val(),{embedlyEnabled:r.settings.get("embedly_enabled")===!0}),n===""?this._invalidInput():(e.markItUp({replaceWith:n}),this._closeDialog())},_hideErrorMessages:function(){this.$("#markitup_media_message").hide()},_invalidInput:function(){this.$("#markitup_media_message").show(),this.$el.dialog().parent().effect("shake",{times:2},250)},_closeDialog:function(){this.$el.dialog("close"),this._hideErrorMessages(),this.$("#markitup_media_txta").val("")},_showDialog:function(){this.$el.dialog({show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},title:e.i18n.__("Multimedia"),resizable:!1,open:function(){setTimeout(function(){e("#markitup_media_txta").focus()},210)},close:t.bind(function(){this._hideErrorMessages()},this)})},render:function(){return this.$el.html(this.template),this._showDialog(),this}})}),define("models/preview",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{rendered:"",data:"",fetchingData:0},initialize:function(){this.webroot=n.settings.get("webroot"),this.listenTo(this,"change:data",this._fetchRendered)},_fetchRendered:function(){this.set("fetchingData",1),$.post(this.webroot+"entries/preview/",this.get("data"),e.bind(function(e){this.set("fetchingData",0),this.set("rendered",e.html),n.eventBus.trigger("notificationUnset","all"),n.eventBus.trigger("notification",e)},this),"json")}});return r}),define("views/preview",["jquery","underscore","backbone","text!templates/spinner.html"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.render(),this.listenTo(this.model,"change:fetchingData",this._spinner),this.listenTo(this.model,"change:rendered",this.render)},_spinner:function(e){e.get("fetchingData")?this.$el.html(r):this.$el.html("")},render:function(){var e;return e=this.model.get("rendered"),e||(e=""),this.$el.html(e),this}});return i}),function(e){var t={_scrollToTop:function(t){e("body").animate({scrollTop:t.offset().top-10,easing:"swing"},300)},_scrollToBottom:function(n){e("body").animate({scrollTop:t._elementBottom(n)-e(window).height()+20,easing:"swing"},300,function(){t._isHeigherThanView(n)&&t._scrollToTop(n)})},_elementBottom:function(e){return e.offset().top+e.height()},_isScrolledIntoView:function(n){if(e(n).length===0)return!0;var r=e(window).scrollTop(),i=r+e(window).height(),s=e(n).offset().top,o=t._elementBottom(n);return o<=i&&s>=r},_isHeigherThanView:function(t){return e(window).height()<=t.height()}},n={top:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToTop(n),this},bottom:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToBottom(n),this},isInView:function(){var n;return n=e(this),t._isScrolledIntoView(n)}};e.fn.scrollIntoView=function(t){if(n[t])return n[t].apply(this,Array.prototype.slice.call(arguments,1));if(typeof t=="object"||!t)return n.init.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.scrollIntoView")}}(jQuery),define("lib/saito/jquery.scrollIntoView",function(){}),define("views/answering",["jquery","underscore","backbone","models/app","views/uploads","views/mediaInsert","models/preview","views/preview","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({rendered:!1,answeringForm:!1,preview:!1,mediaView:!1,model:null,events:{"click .btn-previewClose":"_closePreview","click .btn-preview":"_showPreview","click .btn-markItUp-Upload":"_upload","click .btn-markItUp-Media":"_media","click .btn-submit.js-inlined":"_sendInline"},initialize:function(e){this.parentThreadline=e.parentThreadline||null,this.listenTo(r.eventBus,"isAppVisible",this._focusSubject)},_upload:function(e){var t;e.preventDefault(),t=new i({el:"#markitup_upload",textarea:this.$("textarea#EntryText")[0]})},_media:function(e){e.preventDefault(),this.mediaView===!1&&(this.mediaView=new s({el:"#markitup_media",model:this.model})),this.mediaView.render()},_showPreview:function(e){var t;e.preventDefault(),this.$(".preview").slideDown("fast"),this.preview===!1&&(t=new o,this.preview=new u({el:this.$(".preview .content"),model:t})),this.preview.model.set("data",this.$("form").serialize())},_closePreview:function(e){e.preventDefault(),this.$(".preview").slideUp("fast")},_requestAnsweringForm:function(){e.ajax({url:r.settings.get("webroot")+"entries/add/"+this.model.get("id"),success:t.bind(function(e){this.answeringForm=e,this.render()},this)})},_postProcess:function(){this.$el.scrollIntoView("bottom"),this._focusSubject()},_focusSubject:function(){this.$(".postingform input[type=text]:first").focus()},_sendInline:function(n){n.preventDefault(),e.ajax({url:r.settings.get("webroot")+"entries/add",type:"POST",dataType:"json",data:this.$("#EntryAddForm").serialize(),beforeSend:t.bind(function(){this.$(".btn.btn-submit").attr("disabled","disabled")},this),success:t.bind(function(e){this.model.set({isAnsweringFormShown:!1}),this.parentThreadline!==null&&this.parentThreadline.set("isInlineOpened",!1),r.eventBus.trigger("newEntry",{tid:e.tid,pid:this.model.get("id"),id:e.id})},this)})},render:function(){return this.answeringForm===!1?this._requestAnsweringForm():this.rendered===!1&&(this.rendered=!0,this.$el.html(this.answeringForm),t.defer(function(e){e._postProcess()},this)),this}});return a}),define("views/postings",["jquery","underscore","backbone","models/app","collections/geshis","views/geshi","views/answering","text!templates/spinner.html"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({className:"js-entry-view-core",answeringForm:!1,events:{"click .js-btn-setAnsweringForm":"setAnsweringForm","click .btn-answeringClose":"setAnsweringForm"},initialize:function(e){this.collection=e.collection,this.parentThreadline=e.parentThreadline||null,this.listenTo(this.model,"change:isAnsweringFormShown",this.toggleAnsweringForm),this.listenTo(this.model,"change:html",this.render),this.initGeshi(".c_bbc_code-wrapper")},initGeshi:function(e){var t;t=this.$(e);if(t.length>0){var n=new i;t.each(function(e,t){new s({el:t,collection:n})})}},setAnsweringForm:function(e){e.preventDefault(),this.model.toggle("isAnsweringFormShown")},toggleAnsweringForm:function(){this.model.get("isAnsweringFormShown")?(this._hideAllAnsweringForms(),this._hideSignature(),this._showAnsweringForm(),this._hideBoxActions()):(this._showBoxActions(),this._hideAnsweringForm(),this._showSignature())},_showAnsweringForm:function(){r.eventBus.trigger("breakAutoreload"),this.answeringForm===!1&&this.$(".posting_formular_slider").html(u),this.$(".posting_formular_slider").slideDown("fast"),this.answeringForm===!1&&(this.answeringForm=new o({el:this.$(".posting_formular_slider"),model:this.model,parentThreadline:this.parentThreadline})),this.answeringForm.render()},_hideAnsweringForm:function(){var t;e(this.el).find(".posting_formular_slider").slideUp("fast"),t=e(this.el).find(".posting_formular_slider").parent(),this.answeringForm!==!1&&(this.answeringForm.remove(),this.answeringForm.undelegateEvents(),this.answeringForm=!1),t.append('
')},_hideAllAnsweringForms:function(){this.collection.forEach(function(e){e.get("id")!==this.model.get("id")&&e.set("isAnsweringFormShown",!1)},this)},_showSignature:function(){e(this.el).find(".signature").slideDown("fast")},_hideSignature:function(){e(this.el).find(".signature").slideUp("fast")},_showBoxActions:function(){e(this.el).find(".l-box-footer").slideDown("fast")},_hideBoxActions:function(){e(this.el).find(".l-box-footer").slideUp("fast")},render:function(){return this.$el.html(this.model.get("html")),this.initGeshi(".c_bbc_code-wrapper"),this}});return a}),define("models/posting",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isAnsweringFormShown:!1,html:""},fetchHtml:function(){$.ajax({success:e.bind(function(e){this.set("html",e)},this),type:"post",async:!1,dateType:"html",url:n.settings.get("webroot")+"entries/view/"+this.get("id")})}});return r}),define("views/threadlines",["jquery","underscore","backbone","models/app","models/threadline","views/threadline-spinner","text!templates/threadline-spinner.html","views/postings","models/posting","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u,a){var f=n.View.extend({className:"js-thread_line",tagName:"li",spinnerTpl:t.template(o),postings:null,events:{"click .btn_show_thread":"toggleInlineOpen","click .link_show_thread":"toggleInlineOpenFromLink"},initialize:function(e){this.postings=e.postings,this.model=new i({id:e.id}),e.el===undefined?this.model.fetch():this.model.set({html:this.el},{silent:!0}),this.collection.add(this.model,{silent:!0}),this.attributes={"data-id":e.id},this.listenTo(this.model,"change:isInlineOpened",this._toggleInlineOpened),this.listenTo(this.model,"change:html",this.render)},toggleInlineOpenFromLink:function(e){this.model.get("isAlwaysShownInline")&&this.toggleInlineOpen(e)},toggleInlineOpen:function(e){e.preventDefault(),this.model.get("isInlineOpened")?this.model.set({isInlineOpened:!1}):this.model.set({isInlineOpened:!0})},_toggleInlineOpened:function(e,n){if(n){var r=this.model.id;this.model.get("isContentLoaded")?this._showInlineView():(this.tlsV=new s({el:this.$el.find(".thread_line-pre i")}),this.tlsV.show(),this.$el.find(".js-thread_line-content").after(this.spinnerTpl({id:r})),this.$el.find(".js-btn-strip").on("click",t.bind(this.toggleInlineOpen,this)),this._insertContent())}else this._closeInlineView()},_insertContent:function(){var e,t;e=this.model.get("id"),this.postingModel=new a({id:e}),this.postings.add(this.postingModel),t=new u({el:this.$(".t_s"),model:this.postingModel,collection:this.postings,parentThreadline:this.model}),this.postingModel.fetchHtml(),this.model.set("isContentLoaded",!0),this._showInlineView()},_showInlineView:function(){var e=t.bind(function(){var e=this.model.get("shouldScrollOnInlineOpen");this.tlsV.hide(),e?this.$el.scrollIntoView("isInView")===!1&&this.$el.scrollIntoView("bottom"):this.model.set("shouldScrollOnInlineOpen",!0)},this);this.$el.find(".js-thread_line-content").fadeOut(100,t.bind(function(){this.$(".js-thread_inline").show(0,e)},this))},_closeInlineView:function(){this.$(".js-thread_inline").hide(0,t.bind(function(){this.$el.find(".js-thread_line-content").slideDown(),this._scrollLineIntoView()},this))},_scrollLineIntoView:function(){var e=this.$(".js-thread_line-content");e.scrollIntoView("isInView")||e.scrollIntoView("top").effect("highlight",{times:1},3e3)},render:function(){var t,n,r;return n=this.model.get("html"),n.length>0&&(t=this.$el,r=e(this.model.get("html")),this.setElement(r),t.replaceWith(r)),this}});return f}),function(e,t){typeof define=="function"&&define.amd?define("backboneLocalStorage",["underscore","backbone"],function(n,r){return t(n||e._,r||e.Backbone)}):t(_,Backbone)}(this,function(e,t){function n(){return((1+Math.random())*65536|0).toString(16).substring(1)}function r(){return n()+n()+"-"+n()+"-"+n()+"-"+n()+"-"+n()+n()+n()}return t.LocalStorage=window.Store=function(e){this.name=e;var t=this.localStorage().getItem(this.name);this.records=t&&t.split(",")||[]},e.extend(t.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(e){return e.id||(e.id=r(),e.set(e.idAttribute,e.id)),this.localStorage().setItem(this.name+"-"+e.id,JSON.stringify(e)),this.records.push(e.id.toString()),this.save(),this.find(e)},update:function(t){return this.localStorage().setItem(this.name+"-"+t.id,JSON.stringify(t)),e.include(this.records,t.id.toString())||this.records.push(t.id.toString()),this.save(),this.find(t)},find:function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e.id))},findAll:function(){return e(this.records).chain().map(function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e))},this).compact().value()},destroy:function(t){return t.isNew()?!1:(this.localStorage().removeItem(this.name+"-"+t.id),this.records=e.reject(this.records,function(e){return e===t.id.toString()}),this.save(),t)},localStorage:function(){return localStorage},jsonData:function(e){return e&&JSON.parse(e)}}),t.LocalStorage.sync=window.Store.sync=t.localSync=function(e,n,r){var i=n.localStorage||n.collection.localStorage,s,o,u=$.Deferred&&$.Deferred();try{switch(e){case"read":s=n.id!=undefined?i.find(n):i.findAll();break;case"create":s=i.create(n);break;case"update":s=i.update(n);break;case"delete":s=i.destroy(n)}}catch(a){a.code===DOMException.QUOTA_EXCEEDED_ERR&&window.localStorage.length===0?o="Private browsing is unsupported":o=a.message}return s?(n.trigger("sync",n,s,r),r&&r.success&&(t.VERSION==="0.9.10"?r.success(n,s,r):r.success(s)),u&&u.resolve(s)):(o=o?o:"Record Not Found",n.trigger("error",n,o,r),r&&r.error&&(t.VERSION==="0.9.10"?r.error(n,o,r):r.error(o)),u&&u.reject(o)),r&&r.complete&&r.complete(s),u&&u.promise()},t.ajaxSync=t.sync,t.getSyncMethod=function(e){return e.localStorage||e.collection&&e.collection.localStorage?t.localSync:t.ajaxSync},t.sync=function(e,n,r){return t.getSyncMethod(n).apply(this,[e,n,r])},t.LocalStorage}),define("models/thread",["underscore","backbone","collections/threadlines"],function(e,t,n){var r=t.Model.extend({defaults:{isThreadCollapsed:!1},initialize:function(){this.threadlines=new n}});return r}),define("collections/threads",["underscore","backbone","backboneLocalStorage","models/thread"],function(e,t,n,r){var i=t.Collection.extend({model:r,localStorage:new n("Threads")});return i}),define("views/thread",["jquery","underscore","backbone","models/app","collections/threadlines","views/threadlines"],function(e,t,n,r,i,s){var o=n.View.extend({className:"thread_box",events:{"click .btn-threadCollapse":"collapseThread","click .js-btn-openAllThreadlines":"openAllThreadlines","click .js-btn-closeAllThreadlines":"closeAllThreadlines","click .js-btn-showAllNewThreadlines":"showAllNewThreadlines"},initialize:function(t){this.postings=t.postings,this.$rootUl=this.$("ul.root"),this.$subThreadRootIl=e(this.$rootUl.find("li:not(:first-child)")[0]),this.model.get("isThreadCollapsed")?this.hide():this.show(),this.listenTo(r.eventBus,"newEntry",this._showNewThreadLine),this.listenTo(this.model,"change:isThreadCollapsed",this.toggleCollapseThread)},_showNewThreadLine:function(e){var t;if(e.tid!==this.model.get("id"))return;t=new s({id:e.id,collection:this.model.threadlines,postings:this.postings}),this._appendThreadlineToThread(e.pid,t.render().$el)},_appendThreadlineToThread:function(e,t){var n,r;n=this.$('.js-thread_line[data-id="'+e+'"]'),r=n.next().not(".js_threadline").find("ul:first"),r.length===0?t.wrap("
    ").parent().wrap("
  • ").parent().insertAfter(n):r.append(t)},openAllThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},closeAllThreadlines:function(e){e&&e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!0}),function(e){e.set({isInlineOpened:!1})},this)},showAllNewThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1,isNewToUser:!0}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},collapseThread:function(e){e.preventDefault(),this.closeAllThreadlines(),this.model.toggle("isThreadCollapsed"),this.model.save()},toggleCollapseThread:function(e,t){t?this.slideUp():this.slideDown()},slideUp:function(){this.$subThreadRootIl.slideUp(300),this.markHidden()},slideDown:function(){this.$subThreadRootIl.slideDown(300),this.markShown()},hide:function(){this.$subThreadRootIl.hide(),this.markHidden()},show:function(){this.$subThreadRootIl.show(),this.markShown()},markShown:function(){e(this.el).find(".icon-thread-closed").removeClass("icon-thread-closed").addClass("icon-thread-open")},markHidden:function(){e(this.el).find(".icon-thread-open").removeClass("icon-thread-open").addClass("icon-thread-closed")}});return o}),define("collections/postings",["underscore","backbone","models/posting"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/bookmark",["underscore","backbone","models/app","cakeRest"],function(e,t,n,r){var i=t.Model.extend({initialize:function(){this.webroot=n.settings.get("webroot")+"bookmarks/"}});return e.extend(i.prototype,r),i}),define("collections/bookmarks",["underscore","backbone","models/bookmark"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("views/bookmark",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({events:{"click .btn-bookmark-delete":"deleteBookmark"},initialize:function(){t.bindAll(this,"render"),this.model.on("destroy",this.removeBookmark,this)},deleteBookmark:function(e){e.preventDefault(),this.model.destroy()},removeBookmark:function(){this.$el.hide("slide",null,500,function(){e(this).remove()})}});return r}),define("views/bookmarks",["jquery","underscore","backbone","views/bookmark"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.initCollectionFromDom(".js-bookmark",this.collection,r)}});return i}),define("views/helps",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({isHelpShown:!1,events:function(){var e={};return e["click "+this.indicatorName]="toggle",e},initialize:function(e){this.indicatorName=e.indicatorName,this.elementName=e.elementName,this.activateHelpButton(),this.placeHelp()},activateHelpButton:function(){this.isHelpOnPage()&&e(this.indicatorName).removeClass("no-color")},placeHelp:function(){var t={trigger:"manual",html:!0},n=["bottom","right","left"];for(var r in n)e(this.elementName+"-"+n[r]).popover(e.extend(t,{placement:n[r]}));e(this.indicatorName).popover({placement:"left",trigger:"manual"})},isHelpOnPage:function(){return this.$el.find(this.elementName).length>0},toggle:function(){event.preventDefault(),this.isHelpShown?this.hide():this.show()},show:function(){this.isHelpShown=!0,this.isHelpOnPage()?e(this.elementName).popover("show"):e(this.indicatorName).popover("show")},hide:function(){this.isHelpShown=!1,e(this.elementName).popover("hide"),e(this.indicatorName).popover("hide")}});return r}),define("views/categoryChooser",["jquery","underscore","backbone"],function(e,t,n){return n.View.extend({initialize:function(){this.$el.dialog({autoOpen:!1,show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},width:400,position:[e("#btn-category-chooser").offset().left+e("#btn-category-chooser").width()-e(window).scrollLeft()-410,e("#btn-category-chooser").offset().top-e(window).scrollTop()+e("#btn-category-chooser").height()],title:e.i18n.__("Categories"),resizable:!1})},toggle:function(){this.$el.dialog("isOpen")?this.$el.dialog("close"):this.$el.dialog("open")}})}),define("models/slidetab",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isOpen:!1},initialize:function(){this.webroot=n.settings.get("webroot")},sync:function(){$.ajax({url:this.webroot+"users/ajax_toggle/show_"+this.get("id")})}});return r}),define("collections/slidetabs",["underscore","backbone","models/slidetab"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/shout",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{html:"",newShout:""},initialize:function(){this.webroot=n.settings.get("webroot")+"shouts/",this.listenTo(this,"change:newShout",this.send)},fetch:function(){$.ajax({url:this.webroot+"index",dataType:"html",success:e.bind(function(e){e.length>0&&this.set({html:e})},this)})},send:function(){$.ajax({url:this.webroot+"add",type:"post",data:{text:this.get("newShout")},success:e.bind(function(){this.fetch()},this)})}});return r}),function(e){var t={className:"autosizejs",append:"",callback:!1},n="hidden",r="border-box",i="lineHeight",s='\n
    \n
    \n
    \n \n
    \n
    \n
    \n \n\n'}),define("views/mediaInsert",["jquery","underscore","backbone","models/app","lib/saito/markItUp.media","text!templates/mediaInsert.html"],function(e,t,n,r,i,s){return n.View.extend({template:t.template(s),events:{"click #markitup_media_btn":"_insert"},initialize:function(){this.model!==undefined&&this.model!==null&&this.listenTo(this.model,"change:isAnsweringFormShown",this.remove)},_insert:function(t){var n,s;t.preventDefault(),s=i,n=s.multimedia(this.$("#markitup_media_txta").val(),{embedlyEnabled:r.settings.get("embedly_enabled")===!0}),n===""?this._invalidInput():(e.markItUp({replaceWith:n}),this._closeDialog())},_hideErrorMessages:function(){this.$("#markitup_media_message").hide()},_invalidInput:function(){this.$("#markitup_media_message").show(),this.$el.dialog().parent().effect("shake",{times:2},250)},_closeDialog:function(){this.$el.dialog("close"),this._hideErrorMessages(),this.$("#markitup_media_txta").val("")},_showDialog:function(){this.$el.dialog({show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},title:e.i18n.__("Multimedia"),resizable:!1,open:function(){setTimeout(function(){e("#markitup_media_txta").focus()},210)},close:t.bind(function(){this._hideErrorMessages()},this)})},render:function(){return this.$el.html(this.template),this._showDialog(),this}})}),define("models/preview",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{rendered:"",data:"",fetchingData:0},initialize:function(){this.webroot=n.settings.get("webroot"),this.listenTo(this,"change:data",this._fetchRendered)},_fetchRendered:function(){this.set("fetchingData",1),$.post(this.webroot+"entries/preview/",this.get("data"),e.bind(function(e){this.set("fetchingData",0),this.set("rendered",e.html),n.eventBus.trigger("notificationUnset","all"),n.eventBus.trigger("notification",e)},this),"json")}});return r}),define("views/preview",["jquery","underscore","backbone","text!templates/spinner.html"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.render(),this.listenTo(this.model,"change:fetchingData",this._spinner),this.listenTo(this.model,"change:rendered",this.render)},_spinner:function(e){e.get("fetchingData")?this.$el.html(r):this.$el.html("")},render:function(){var e;return e=this.model.get("rendered"),e||(e=""),this.$el.html(e),this}});return i}),function(e){var t={_scrollToTop:function(t){e("body").animate({scrollTop:t.offset().top-10,easing:"swing"},300)},_scrollToBottom:function(n){e("body").animate({scrollTop:t._elementBottom(n)-e(window).height()+20,easing:"swing"},300,function(){t._isHeigherThanView(n)&&t._scrollToTop(n)})},_elementBottom:function(e){return e.offset().top+e.height()},_isScrolledIntoView:function(n){if(e(n).length===0)return!0;var r=e(window).scrollTop(),i=r+e(window).height(),s=e(n).offset().top,o=t._elementBottom(n);return o<=i&&s>=r},_isHeigherThanView:function(t){return e(window).height()<=t.height()}},n={top:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToTop(n),this},bottom:function(){var n;return n=e(this),t._isScrolledIntoView(n)||t._scrollToBottom(n),this},isInView:function(){var n;return n=e(this),t._isScrolledIntoView(n)}};e.fn.scrollIntoView=function(t){if(n[t])return n[t].apply(this,Array.prototype.slice.call(arguments,1));if(typeof t=="object"||!t)return n.init.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.scrollIntoView")}}(jQuery),define("lib/saito/jquery.scrollIntoView",function(){}),define("views/answering",["jquery","underscore","backbone","models/app","views/uploads","views/mediaInsert","models/preview","views/preview","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({rendered:!1,answeringForm:!1,preview:!1,mediaView:!1,model:null,events:{"click .btn-previewClose":"_closePreview","click .btn-preview":"_showPreview","click .btn-markItUp-Upload":"_upload","click .btn-markItUp-Media":"_media","click .btn-submit.js-inlined":"_sendInline"},initialize:function(e){this.parentThreadline=e.parentThreadline||null,this.listenTo(r.eventBus,"isAppVisible",this._focusSubject)},_upload:function(e){var t;e.preventDefault(),t=new i({el:"#markitup_upload",textarea:this.$("textarea#EntryText")[0]})},_media:function(e){e.preventDefault(),this.mediaView===!1&&(this.mediaView=new s({el:"#markitup_media",model:this.model})),this.mediaView.render()},_showPreview:function(e){var t;e.preventDefault(),this.$(".preview").slideDown("fast"),this.preview===!1&&(t=new o,this.preview=new u({el:this.$(".preview .content"),model:t})),this.preview.model.set("data",this.$("form").serialize())},_closePreview:function(e){e.preventDefault(),this.$(".preview").slideUp("fast")},_requestAnsweringForm:function(){e.ajax({url:r.settings.get("webroot")+"entries/add/"+this.model.get("id"),success:t.bind(function(e){this.answeringForm=e,this.render()},this)})},_postProcess:function(){this.$el.scrollIntoView("bottom"),this._focusSubject()},_focusSubject:function(){this.$(".postingform input[type=text]:first").focus()},_sendInline:function(n){n.preventDefault(),e.ajax({url:r.settings.get("webroot")+"entries/add",type:"POST",dataType:"json",data:this.$("#EntryAddForm").serialize(),beforeSend:t.bind(function(){this.$(".btn.btn-submit").attr("disabled","disabled")},this),success:t.bind(function(e){this.model.set({isAnsweringFormShown:!1}),this.parentThreadline!==null&&this.parentThreadline.set("isInlineOpened",!1),r.eventBus.trigger("newEntry",{tid:e.tid,pid:this.model.get("id"),id:e.id})},this)})},render:function(){return this.answeringForm===!1?this._requestAnsweringForm():this.rendered===!1&&(this.rendered=!0,this.$el.html(this.answeringForm),t.defer(function(e){e._postProcess()},this)),this}});return a}),define("views/postings",["jquery","underscore","backbone","models/app","collections/geshis","views/geshi","views/answering","text!templates/spinner.html"],function(e,t,n,r,i,s,o,u){var a=n.View.extend({className:"js-entry-view-core",answeringForm:!1,events:{"click .js-btn-setAnsweringForm":"setAnsweringForm","click .btn-answeringClose":"setAnsweringForm"},initialize:function(e){this.collection=e.collection,this.parentThreadline=e.parentThreadline||null,this.listenTo(this.model,"change:isAnsweringFormShown",this.toggleAnsweringForm),this.listenTo(this.model,"change:html",this.render),this.initGeshi(".c_bbc_code-wrapper")},initGeshi:function(e){var t;t=this.$(e);if(t.length>0){var n=new i;t.each(function(e,t){new s({el:t,collection:n})})}},setAnsweringForm:function(e){e.preventDefault(),this.model.toggle("isAnsweringFormShown")},toggleAnsweringForm:function(){this.model.get("isAnsweringFormShown")?(this._hideAllAnsweringForms(),this._hideSignature(),this._showAnsweringForm(),this._hideBoxActions()):(this._showBoxActions(),this._hideAnsweringForm(),this._showSignature())},_showAnsweringForm:function(){r.eventBus.trigger("breakAutoreload"),this.answeringForm===!1&&this.$(".posting_formular_slider").html(u),this.$(".posting_formular_slider").slideDown("fast"),this.answeringForm===!1&&(this.answeringForm=new o({el:this.$(".posting_formular_slider"),model:this.model,parentThreadline:this.parentThreadline})),this.answeringForm.render()},_hideAnsweringForm:function(){var t;e(this.el).find(".posting_formular_slider").slideUp("fast"),t=e(this.el).find(".posting_formular_slider").parent(),this.answeringForm!==!1&&(this.answeringForm.remove(),this.answeringForm.undelegateEvents(),this.answeringForm=!1),t.append('
    ')},_hideAllAnsweringForms:function(){this.collection.forEach(function(e){e.get("id")!==this.model.get("id")&&e.set("isAnsweringFormShown",!1)},this)},_showSignature:function(){e(this.el).find(".signature").slideDown("fast")},_hideSignature:function(){e(this.el).find(".signature").slideUp("fast")},_showBoxActions:function(){e(this.el).find(".l-box-footer").slideDown("fast")},_hideBoxActions:function(){e(this.el).find(".l-box-footer").slideUp("fast")},render:function(){return this.$el.html(this.model.get("html")),this.initGeshi(".c_bbc_code-wrapper"),this}});return a}),define("models/posting",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isAnsweringFormShown:!1,html:""},fetchHtml:function(){$.ajax({success:e.bind(function(e){this.set("html",e)},this),type:"post",async:!1,dateType:"html",url:n.settings.get("webroot")+"entries/view/"+this.get("id")})}});return r}),define("views/threadlines",["jquery","underscore","backbone","models/app","models/threadline","views/threadline-spinner","text!templates/threadline-spinner.html","views/postings","models/posting","lib/saito/jquery.scrollIntoView"],function(e,t,n,r,i,s,o,u,a){var f=n.View.extend({className:"js-thread_line",tagName:"li",spinnerTpl:t.template(o),postings:null,events:{"click .btn_show_thread":"toggleInlineOpen","click .link_show_thread":"toggleInlineOpenFromLink"},initialize:function(e){this.postings=e.postings,this.model=new i({id:e.id}),e.el===undefined?this.model.fetch():this.model.set({html:this.el},{silent:!0}),this.collection.add(this.model,{silent:!0}),this.attributes={"data-id":e.id},this.listenTo(this.model,"change:isInlineOpened",this._toggleInlineOpened),this.listenTo(this.model,"change:html",this.render)},toggleInlineOpenFromLink:function(e){this.model.get("isAlwaysShownInline")&&this.toggleInlineOpen(e)},toggleInlineOpen:function(e){e.preventDefault(),this.model.get("isInlineOpened")?this.model.set({isInlineOpened:!1}):this.model.set({isInlineOpened:!0})},_toggleInlineOpened:function(e,n){if(n){var r=this.model.id;this.model.get("isContentLoaded")?this._showInlineView():(this.tlsV=new s({el:this.$el.find(".thread_line-pre i")}),this.tlsV.show(),this.$el.find(".js-thread_line-content").after(this.spinnerTpl({id:r})),this.$el.find(".js-btn-strip").on("click",t.bind(this.toggleInlineOpen,this)),this._insertContent())}else this._closeInlineView()},_insertContent:function(){var e,t;e=this.model.get("id"),this.postingModel=new a({id:e}),this.postings.add(this.postingModel),t=new u({el:this.$(".t_s"),model:this.postingModel,collection:this.postings,parentThreadline:this.model}),this.postingModel.fetchHtml(),this.model.set("isContentLoaded",!0),this._showInlineView()},_showInlineView:function(){var e=t.bind(function(){var e=this.model.get("shouldScrollOnInlineOpen");this.tlsV.hide(),e?this.$el.scrollIntoView("isInView")===!1&&this.$el.scrollIntoView("bottom"):this.model.set("shouldScrollOnInlineOpen",!0)},this);this.$el.find(".js-thread_line-content").fadeOut(100,t.bind(function(){this.$(".js-thread_inline").show(0,e)},this))},_closeInlineView:function(){this.$(".js-thread_inline").hide(0,t.bind(function(){this.$el.find(".js-thread_line-content").slideDown(),this._scrollLineIntoView()},this))},_scrollLineIntoView:function(){var e=this.$(".js-thread_line-content");e.scrollIntoView("isInView")||e.scrollIntoView("top").effect("highlight",{times:1},3e3)},render:function(){var t,n,r;return n=this.model.get("html"),n.length>0&&(t=this.$el,r=e(this.model.get("html")),this.setElement(r),t.replaceWith(r)),this}});return f}),function(e,t){typeof define=="function"&&define.amd?define("backboneLocalStorage",["underscore","backbone"],function(n,r){return t(n||e._,r||e.Backbone)}):t(_,Backbone)}(this,function(e,t){function n(){return((1+Math.random())*65536|0).toString(16).substring(1)}function r(){return n()+n()+"-"+n()+"-"+n()+"-"+n()+"-"+n()+n()+n()}return t.LocalStorage=window.Store=function(e){this.name=e;var t=this.localStorage().getItem(this.name);this.records=t&&t.split(",")||[]},e.extend(t.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(e){return e.id||(e.id=r(),e.set(e.idAttribute,e.id)),this.localStorage().setItem(this.name+"-"+e.id,JSON.stringify(e)),this.records.push(e.id.toString()),this.save(),this.find(e)},update:function(t){return this.localStorage().setItem(this.name+"-"+t.id,JSON.stringify(t)),e.include(this.records,t.id.toString())||this.records.push(t.id.toString()),this.save(),this.find(t)},find:function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e.id))},findAll:function(){return e(this.records).chain().map(function(e){return this.jsonData(this.localStorage().getItem(this.name+"-"+e))},this).compact().value()},destroy:function(t){return t.isNew()?!1:(this.localStorage().removeItem(this.name+"-"+t.id),this.records=e.reject(this.records,function(e){return e===t.id.toString()}),this.save(),t)},localStorage:function(){return localStorage},jsonData:function(e){return e&&JSON.parse(e)}}),t.LocalStorage.sync=window.Store.sync=t.localSync=function(e,n,r){var i=n.localStorage||n.collection.localStorage,s,o,u=$.Deferred&&$.Deferred();try{switch(e){case"read":s=n.id!=undefined?i.find(n):i.findAll();break;case"create":s=i.create(n);break;case"update":s=i.update(n);break;case"delete":s=i.destroy(n)}}catch(a){a.code===DOMException.QUOTA_EXCEEDED_ERR&&window.localStorage.length===0?o="Private browsing is unsupported":o=a.message}return s?(n.trigger("sync",n,s,r),r&&r.success&&(t.VERSION==="0.9.10"?r.success(n,s,r):r.success(s)),u&&u.resolve(s)):(o=o?o:"Record Not Found",n.trigger("error",n,o,r),r&&r.error&&(t.VERSION==="0.9.10"?r.error(n,o,r):r.error(o)),u&&u.reject(o)),r&&r.complete&&r.complete(s),u&&u.promise()},t.ajaxSync=t.sync,t.getSyncMethod=function(e){return e.localStorage||e.collection&&e.collection.localStorage?t.localSync:t.ajaxSync},t.sync=function(e,n,r){return t.getSyncMethod(n).apply(this,[e,n,r])},t.LocalStorage}),define("models/thread",["underscore","backbone","collections/threadlines"],function(e,t,n){var r=t.Model.extend({defaults:{isThreadCollapsed:!1},initialize:function(){this.threadlines=new n}});return r}),define("collections/threads",["underscore","backbone","backboneLocalStorage","models/thread"],function(e,t,n,r){var i=t.Collection.extend({model:r,localStorage:new n("Threads")});return i}),define("views/thread",["jquery","underscore","backbone","models/app","collections/threadlines","views/threadlines"],function(e,t,n,r,i,s){var o=n.View.extend({className:"thread_box",events:{"click .btn-threadCollapse":"collapseThread","click .js-btn-openAllThreadlines":"openAllThreadlines","click .js-btn-closeAllThreadlines":"closeAllThreadlines","click .js-btn-showAllNewThreadlines":"showAllNewThreadlines"},initialize:function(t){this.postings=t.postings,this.$rootUl=this.$("ul.root"),this.$subThreadRootIl=e(this.$rootUl.find("li:not(:first-child)")[0]),this.model.get("isThreadCollapsed")?this.hide():this.show(),this.listenTo(r.eventBus,"newEntry",this._showNewThreadLine),this.listenTo(this.model,"change:isThreadCollapsed",this.toggleCollapseThread)},_showNewThreadLine:function(e){var t;if(e.tid!==this.model.get("id"))return;t=new s({id:e.id,collection:this.model.threadlines,postings:this.postings}),this._appendThreadlineToThread(e.pid,t.render().$el)},_appendThreadlineToThread:function(e,t){var n,r;n=this.$('.js-thread_line[data-id="'+e+'"]'),r=n.next().not(".js_threadline").find("ul:first"),r.length===0?t.wrap("
      ").parent().wrap("
    • ").parent().insertAfter(n):r.append(t)},openAllThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},closeAllThreadlines:function(e){e&&e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!0}),function(e){e.set({isInlineOpened:!1})},this)},showAllNewThreadlines:function(e){e.preventDefault(),t.each(this.model.threadlines.where({isInlineOpened:!1,isNewToUser:!0}),function(e){e.set({isInlineOpened:!0,shouldScrollOnInlineOpen:!1})},this)},collapseThread:function(e){e.preventDefault(),this.closeAllThreadlines(),this.model.toggle("isThreadCollapsed"),this.model.save()},toggleCollapseThread:function(e,t){t?this.slideUp():this.slideDown()},slideUp:function(){this.$subThreadRootIl.slideUp(300),this.markHidden()},slideDown:function(){this.$subThreadRootIl.slideDown(300),this.markShown()},hide:function(){this.$subThreadRootIl.hide(),this.markHidden()},show:function(){this.$subThreadRootIl.show(),this.markShown()},markShown:function(){e(this.el).find(".icon-thread-closed").removeClass("icon-thread-closed").addClass("icon-thread-open")},markHidden:function(){e(this.el).find(".icon-thread-open").removeClass("icon-thread-open").addClass("icon-thread-closed")}});return o}),define("collections/postings",["underscore","backbone","models/posting"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/bookmark",["underscore","backbone","models/app","cakeRest"],function(e,t,n,r){var i=t.Model.extend({initialize:function(){this.webroot=n.settings.get("webroot")+"bookmarks/"}});return e.extend(i.prototype,r),i}),define("collections/bookmarks",["underscore","backbone","models/bookmark"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("views/bookmark",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({events:{"click .btn-bookmark-delete":"deleteBookmark"},initialize:function(){t.bindAll(this,"render"),this.model.on("destroy",this.removeBookmark,this)},deleteBookmark:function(e){e.preventDefault(),this.model.destroy()},removeBookmark:function(){this.$el.hide("slide",null,500,function(){e(this).remove()})}});return r}),define("views/bookmarks",["jquery","underscore","backbone","views/bookmark"],function(e,t,n,r){var i=n.View.extend({initialize:function(){this.initCollectionFromDom(".js-bookmark",this.collection,r)}});return i}),define("views/helps",["jquery","underscore","backbone"],function(e,t,n){var r=n.View.extend({isHelpShown:!1,events:function(){var e={};return e["click "+this.indicatorName]="toggle",e},initialize:function(e){this.indicatorName=e.indicatorName,this.elementName=e.elementName,this.activateHelpButton(),this.placeHelp()},activateHelpButton:function(){this.isHelpOnPage()&&e(this.indicatorName).removeClass("no-color")},placeHelp:function(){var t={trigger:"manual",html:!0},n=["bottom","right","left"];for(var r in n)e(this.elementName+"-"+n[r]).popover(e.extend(t,{placement:n[r]}));e(this.indicatorName).popover({placement:"left",trigger:"manual"})},isHelpOnPage:function(){return this.$el.find(this.elementName).length>0},toggle:function(){event.preventDefault(),this.isHelpShown?this.hide():this.show()},show:function(){this.isHelpShown=!0,this.isHelpOnPage()?e(this.elementName).popover("show"):e(this.indicatorName).popover("show")},hide:function(){this.isHelpShown=!1,e(this.elementName).popover("hide"),e(this.indicatorName).popover("hide")}});return r}),define("views/categoryChooser",["jquery","underscore","backbone"],function(e,t,n){return n.View.extend({initialize:function(){this.$el.dialog({autoOpen:!1,show:{effect:"scale",duration:200},hide:{effect:"fade",duration:200},width:400,position:[e("#btn-category-chooser").offset().left+e("#btn-category-chooser").width()-e(window).scrollLeft()-410,e("#btn-category-chooser").offset().top-e(window).scrollTop()+e("#btn-category-chooser").height()],title:e.i18n.__("Categories"),resizable:!1})},toggle:function(){this.$el.dialog("isOpen")?this.$el.dialog("close"):this.$el.dialog("open")}})}),define("models/slidetab",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{isOpen:!1},initialize:function(){this.webroot=n.settings.get("webroot")},sync:function(){$.ajax({url:this.webroot+"users/ajax_toggle/show_"+this.get("id")})}});return r}),define("collections/slidetabs",["underscore","backbone","models/slidetab"],function(e,t,n){var r=t.Collection.extend({model:n});return r}),define("models/shout",["underscore","backbone","models/app"],function(e,t,n){var r=t.Model.extend({defaults:{html:"",newShout:""},initialize:function(){this.webroot=n.settings.get("webroot")+"shouts/",this.listenTo(this,"change:newShout",this.send)},fetch:function(){$.ajax({url:this.webroot+"index",dataType:"html",success:e.bind(function(e){e.length>0&&this.set({html:e})},this)})},send:function(){$.ajax({url:this.webroot+"add",type:"post",data:{text:this.get("newShout")},success:e.bind(function(){this.fetch()},this)})}});return r}),function(e){var t={className:"autosizejs",append:"",callback:!1},n="hidden",r="border-box",i="lineHeight",s='