Auto-generate breadcrumbs using page location
authorAlexander Ebert <ebert@woltlab.com>
Tue, 26 Apr 2016 14:12:05 +0000 (16:12 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 26 Apr 2016 14:12:11 +0000 (16:12 +0200)
17 files changed:
wcfsetup/install/files/lib/data/ITitledLinkObject.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/IUserContent.class.php
wcfsetup/install/files/lib/data/page/Page.class.php
wcfsetup/install/files/lib/data/page/PageCache.class.php
wcfsetup/install/files/lib/data/user/UserProfile.class.php
wcfsetup/install/files/lib/form/AbstractModerationForm.class.php
wcfsetup/install/files/lib/form/MailForm.class.php
wcfsetup/install/files/lib/form/UserSearchForm.class.php
wcfsetup/install/files/lib/page/RecentActivityListPage.class.php
wcfsetup/install/files/lib/page/SearchResultPage.class.php
wcfsetup/install/files/lib/page/TeamPage.class.php
wcfsetup/install/files/lib/page/UserPage.class.php
wcfsetup/install/files/lib/page/UsersOnlineListPage.class.php
wcfsetup/install/files/lib/system/breadcrumb/Breadcrumbs.class.php
wcfsetup/install/files/lib/system/cache/builder/PageCacheBuilder.class.php
wcfsetup/install/files/lib/system/page/PageLocationManager.class.php
wcfsetup/install/files/lib/system/request/ControllerMap.class.php

diff --git a/wcfsetup/install/files/lib/data/ITitledLinkObject.class.php b/wcfsetup/install/files/lib/data/ITitledLinkObject.class.php
new file mode 100644 (file)
index 0000000..f2f069b
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+namespace wcf\data;
+
+/**
+ * Common interface to require both title and link.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data
+ * @category   Community Framework
+ * @since       2.2
+ */
+interface ITitledLinkObject extends ILinkableObject, ITitledObject {}
index 963da50d97ecbd636e650d6c4312e7d436fa4cc5..e33efa78e47a7c5962422b4feb8450c7e9d6642b 100644 (file)
@@ -11,7 +11,7 @@ namespace wcf\data;
  * @subpackage data
  * @category   Community Framework
  */
-interface IUserContent extends ILinkableObject, ITitledObject {
+interface IUserContent extends ITitledLinkObject {
        /**
         * Returns message creation timestamp.
         * 
index d41980b2334811752f9adf055c0e4e827d3e2040..42d7373003b979a3dc5ee81cc1a66d2e0e0cc76a 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\data\page;
 use wcf\data\DatabaseObject;
+use wcf\data\ILinkableObject;
+use wcf\data\ITitledObject;
 use wcf\data\TDatabaseObjectOptions;
 use wcf\data\TDatabaseObjectPermissions;
 use wcf\system\application\ApplicationHandler;
@@ -13,7 +15,7 @@ use wcf\system\WCF;
  * Represents a page.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage data.page
@@ -39,7 +41,7 @@ use wcf\system\WCF;
  * @property-read      string          $permissions
  * @property-read      string          $options
  */
-class Page extends DatabaseObject {
+class Page extends DatabaseObject implements ILinkableObject, ITitledObject {
        use TDatabaseObjectOptions;
        use TDatabaseObjectPermissions;
        
@@ -146,9 +148,7 @@ class Page extends DatabaseObject {
        }
        
        /**
-        * Returns the page URL.
-        * 
-        * @return      string
+        * @inheritDoc
         */
        public function getLink() {
                if ($this->controller) {
@@ -167,6 +167,18 @@ class Page extends DatabaseObject {
                }
        }
        
+       /**
+        * @inheritDoc
+        */
+       public function getTitle() {
+               $title = PageCache::getInstance()->getPageTitle($this->pageID);
+               if (empty($title)) {
+                       $title = $this->getGenericTitle();
+               }
+               
+               return $title;
+       }
+       
        /**
         * Returns shortened link for acp page list.
         *
@@ -290,6 +302,15 @@ class Page extends DatabaseObject {
                return '';
        }
        
+       /**
+        * Returns the value of a generic phrase based upon a page's identifier.
+        * 
+        * @return      string  generic title
+        */
+       protected function getGenericTitle() {
+               return WCF::getLanguage()->get('wcf.page.' . $this->identifier);
+       }
+       
        /**
         * Returns the page with the given identifier.
         * 
index b7fe00a97daf8d3693bc1dba11a7e17e56f390ed..76f6ab734f7e03cddf6a618e5d09694d0a14a816 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\data\page;
 use wcf\system\cache\builder\PageCacheBuilder;
 use wcf\system\SingletonFactory;
+use wcf\system\WCF;
 
 /**
  * Provides access to the page cache.
@@ -12,6 +13,7 @@ use wcf\system\SingletonFactory;
  * @package    com.woltlab.wcf
  * @subpackage data.page
  * @category   Community Framework
+ * @since       2.2
  */
 class PageCache extends SingletonFactory {
        /**
@@ -68,4 +70,39 @@ class PageCache extends SingletonFactory {
                
                return null;
        }
+       
+       /**
+        * Returns the localized page title by page id, optionally retrieving the title
+        * for given language id if it is a multilingual page.
+        * 
+        * @param       integer         $pageID         page id
+        * @param       integer         $languageID     specific value by language id
+        * @return      string          localized page title
+        */
+       public function getPageTitle($pageID, $languageID = null) {
+               if (isset($this->cache['pageTitles'][$pageID])) {
+                       $page = $this->getPage($pageID);
+                       if ($page->isMultilingual) {
+                               if ($languageID !== null && isset($this->cache['pageTitles'][$pageID][$languageID])) {
+                                       return $this->cache['pageTitles'][$pageID][$languageID];
+                               }
+                               
+                               return $this->cache['pageTitles'][$pageID][WCF::getUser()->getLanguage()->languageID];
+                       }
+                       else {
+                               return $this->cache['pageTitles'][$pageID][0];
+                       }
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the global landing page.
+        * 
+        * @return      Page
+        */
+       public function getLandingPage() {
+               return $this->cache['landingPage'];
+       }
 }
index b1f6b158da80183bad9b888c39f9ab4a1f58b998..9e18a7283e0de3244e0d6d3e1e79d552bf8efba2 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\data\user;
+use wcf\data\ITitledLinkObject;
 use wcf\data\user\avatar\DefaultAvatar;
 use wcf\data\user\avatar\Gravatar;
 use wcf\data\user\avatar\IUserAvatar;
@@ -9,8 +10,6 @@ use wcf\data\user\online\UserOnline;
 use wcf\data\user\option\ViewableUserOption;
 use wcf\data\user\rank\UserRank;
 use wcf\data\DatabaseObjectDecorator;
-use wcf\system\breadcrumb\Breadcrumb;
-use wcf\system\breadcrumb\IBreadcrumbProvider;
 use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
 use wcf\system\cache\runtime\UserProfileRuntimeCache;
 use wcf\system\request\LinkHandler;
@@ -74,7 +73,7 @@ use wcf\util\StringUtil;
  * @property-read      integer         $likesReceived
  * @property-read      string          $socialNetworkPrivacySettings
  */
-class UserProfile extends DatabaseObjectDecorator implements IBreadcrumbProvider {
+class UserProfile extends DatabaseObjectDecorator implements ITitledLinkObject {
        /**
         * @inheritDoc
         */
@@ -688,15 +687,6 @@ class UserProfile extends DatabaseObjectDecorator implements IBreadcrumbProvider
                return ($this->activationCode ? false : true);
        }
        
-       /**
-        * @inheritDoc
-        */
-       public function getBreadcrumb() {
-               return new Breadcrumb($this->username, LinkHandler::getInstance()->getLink('User', [
-                       'object' => $this
-               ]));
-       }
-       
        /**
         * Returns the encoded email address.
         * 
@@ -834,6 +824,20 @@ class UserProfile extends DatabaseObjectDecorator implements IBreadcrumbProvider
                return '<a href="'.$link.'" class="userLink" data-user-id="'.$this->userID.'">'.StringUtil::encodeHTML($this->username).'</a>';
        }
        
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return $this->getDecoratedObject()->getLink();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTitle() {
+               return $this->getDecoratedObject()->getTitle();
+       }
+       
        /**
         * Returns an "empty" user profile object for a guest with the given username.
         * 
index 0ee986751ddd59248bcd33911617398cb5e2a185..104900393eb5a8800a76030cffe18ff53aa50c16 100644 (file)
@@ -3,13 +3,12 @@ namespace wcf\form;
 use wcf\data\comment\StructuredCommentList;
 use wcf\data\moderation\queue\ModerationQueueAction;
 use wcf\data\moderation\queue\ViewableModerationQueue;
-use wcf\system\breadcrumb\Breadcrumb;
 use wcf\system\comment\manager\ICommentManager;
 use wcf\system\comment\CommentHandler;
 use wcf\system\event\EventHandler;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\PermissionDeniedException;
-use wcf\system\request\LinkHandler;
+use wcf\system\page\PageLocationManager;
 use wcf\system\WCF;
 
 /**
@@ -102,10 +101,7 @@ abstract class AbstractModerationForm extends AbstractForm {
                        $this->assignedUserID = $this->queue->assignedUserID;
                }
                
-               WCF::getBreadcrumbs()->add(new Breadcrumb(
-                       WCF::getLanguage()->get('wcf.moderation.moderation'),
-                       LinkHandler::getInstance()->getLink('ModerationList')
-               ));
+               PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.ModerationList');
                
                $this->commentObjectTypeID = CommentHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.moderation.queue');
                $this->commentManager = CommentHandler::getInstance()->getObjectType($this->commentObjectTypeID)->getProcessor();
index a9a31baa4421cff603e09834cb91e57cb6c2ca90..16b619ad6426c47813d0bd6e90cc8a315b002766 100644 (file)
@@ -6,6 +6,7 @@ use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\mail\Mail;
+use wcf\system\page\PageLocationManager;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
 use wcf\util\HeaderUtil;
@@ -16,7 +17,7 @@ use wcf\util\UserUtil;
  * Shows the user mail form.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage form
@@ -24,7 +25,7 @@ use wcf\util\UserUtil;
  */
 class MailForm extends AbstractCaptchaForm {
        /**
-        * @see \wcf\form\AbstractCaptchaForm::$useCaptcha
+        * @inheritDoc
         */
        public $useCaptcha = PROFILE_MAIL_USE_CAPTCHA;
        
@@ -65,7 +66,12 @@ class MailForm extends AbstractCaptchaForm {
        public $email = '';
        
        /**
-        * @see \wcf\page\IPage::readParameters()
+        * @inheritDoc
+        */
+       public $neededPermissions = ['user.profile.canMail'];
+       
+       /**
+        * @inheritDoc
         */
        public function readParameters() {
                parent::readParameters();
@@ -80,11 +86,11 @@ class MailForm extends AbstractCaptchaForm {
                        throw new PermissionDeniedException();
                }
                
-               $this->canonicalURL = LinkHandler::getInstance()->getLink('Mail', array('object' => $this->user->getDecoratedObject()));
+               $this->canonicalURL = LinkHandler::getInstance()->getLink('Mail', ['object' => $this->user->getDecoratedObject()]);
        }
        
        /**
-        * @see \wcf\form\IForm::readFormParameters()
+        * @inheritDoc
         */
        public function readFormParameters() {
                parent::readFormParameters();
@@ -97,7 +103,7 @@ class MailForm extends AbstractCaptchaForm {
        }
        
        /**
-        * @see \wcf\form\IForm::validate()
+        * @inheritDoc
         */
        public function validate() {
                if (!WCF::getUser()->userID) {
@@ -122,7 +128,7 @@ class MailForm extends AbstractCaptchaForm {
        }
        
        /**
-        * @see \wcf\form\IForm::save()
+        * @inheritDoc
         */
        public function save() {
                parent::save();
@@ -131,18 +137,18 @@ class MailForm extends AbstractCaptchaForm {
                $userLanguage = $this->user->getLanguage();
                
                // build message data
-               $subjectData = array(
+               $subjectData = [
                        'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->email,
                        'subject' => $this->subject
-               );
-               $messageData = array(
+               ];
+               $messageData = [
                        'message' => $this->message,
                        'recipient' => $this->user,
                        'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->email,
-               );
+               ];
                
                // build mail
-               $mail = new Mail(array($this->user->username => $this->user->email), $userLanguage->getDynamicVariable('wcf.user.mail.mail.subject', $subjectData), $userLanguage->getDynamicVariable('wcf.user.mail.mail', $messageData));
+               $mail = new Mail([$this->user->username => $this->user->email], $userLanguage->getDynamicVariable('wcf.user.mail.mail.subject', $subjectData), $userLanguage->getDynamicVariable('wcf.user.mail.mail', $messageData));
                $mail->setLanguage($userLanguage);
                
                // add reply-to tag
@@ -158,40 +164,39 @@ class MailForm extends AbstractCaptchaForm {
                $this->saved();
                
                // forward to profile page
-               HeaderUtil::delayedRedirect(LinkHandler::getInstance()->getLink('User', array('object' => $this->user)), WCF::getLanguage()->getDynamicVariable('wcf.user.mail.sent', array('user' => $this->user)));
+               HeaderUtil::delayedRedirect(LinkHandler::getInstance()->getLink('User', ['object' => $this->user]), WCF::getLanguage()->getDynamicVariable('wcf.user.mail.sent', ['user' => $this->user]));
                exit;
        }
        
        /**
-        * @see \wcf\page\IPage::readData()
+        * @inheritDoc
         */
        public function readData() {
                parent::readData();
                
-               WCF::getBreadcrumbs()->add($this->user->getBreadcrumb());
+               PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.User', $this->user->userID, $this->user);
+               if (MODULE_MEMBERS_LIST) PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.MembersList');
        }
        
        /**
-        * @see \wcf\page\IPage::assignVariables()
+        * @inheritDoc
         */
        public function assignVariables() {
                parent::assignVariables();
                
-               WCF::getTPL()->assign(array(
+               WCF::getTPL()->assign([
                        'user' => $this->user,
                        'showAddress' => $this->showAddress,
                        'message' => $this->message,
                        'subject' => $this->subject,
                        'email' => $this->email
-               ));
+               ]);
        }
        
        /**
-        * @see \wcf\page\IPage::show()
+        * @inheritDoc
         */
        public function show() {
-               WCF::getSession()->checkPermissions(array('user.profile.canMail'));
-               
                if (!$this->user->isAccessible('canMail')) {
                        throw new PermissionDeniedException();
                }
index 63b96114c92e4401fd831e078a1da177ba577342..0eecfc025b63930c7c7fe6a5f064b77f92474214 100644 (file)
@@ -2,9 +2,9 @@
 namespace wcf\form;
 use wcf\acp\form\UserOptionListForm;
 use wcf\data\search\SearchEditor;
-use wcf\system\breadcrumb\Breadcrumb;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
+use wcf\system\page\PageLocationManager;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
 use wcf\util\HeaderUtil;
@@ -88,7 +88,7 @@ class UserSearchForm extends UserOptionListForm {
                $this->readOptionTree();
                
                // add breadcrumbs
-               WCF::getBreadcrumbs()->add(new Breadcrumb(WCF::getLanguage()->get('wcf.user.members'), LinkHandler::getInstance()->getLink('MembersList')));
+               if (MODULE_MEMBERS_LIST) PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.MembersList');
        }
        
        /**
index e43f301be1301675041149fe904f415cad3dc717..9f05750800b805346566e80d392fb4e6c8accb74 100644 (file)
@@ -1,8 +1,7 @@
 <?php
 namespace wcf\page;
 use wcf\data\user\activity\event\ViewableUserActivityEventList;
-use wcf\system\breadcrumb\Breadcrumb;
-use wcf\system\request\LinkHandler;
+use wcf\system\page\PageLocationManager;
 use wcf\system\user\activity\event\UserActivityEventHandler;
 use wcf\system\WCF;
 
@@ -10,7 +9,7 @@ use wcf\system\WCF;
  * Shows the global recent activity list page.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage page
@@ -19,12 +18,12 @@ use wcf\system\WCF;
 class RecentActivityListPage extends AbstractPage {
        /**
         * viewable user activity event list
-        * @var \wcf\data\user\activity\event\ViewableUserActivityEventList
+        * @var ViewableUserActivityEventList
         */
        public $eventList = null;
        
        /**
-        * @see \wcf\page\IPage::readData()
+        * @inheritDoc
         */
        public function readData() {
                parent::readData();
@@ -33,11 +32,11 @@ class RecentActivityListPage extends AbstractPage {
                $this->eventList->readObjects();
                
                // add breadcrumbs
-               if (MODULE_MEMBERS_LIST) WCF::getBreadcrumbs()->add(new Breadcrumb(WCF::getLanguage()->get('wcf.user.members'), LinkHandler::getInstance()->getLink('MembersList')));
+               if (MODULE_MEMBERS_LIST) PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.MembersList');
        }
        
        /**
-        * @see \wcf\page\IPage::assignVariables()
+        * @inheritDoc
         */
        public function assignVariables() {
                parent::assignVariables();
@@ -47,10 +46,10 @@ class RecentActivityListPage extends AbstractPage {
                // removes orphaned and non-accessable events
                UserActivityEventHandler::validateEvents($this->eventList);
                
-               WCF::getTPL()->assign(array(
+               WCF::getTPL()->assign([
                        'eventList' => $this->eventList,
                        'lastEventTime' => $lastEventTime,
                        'allowSpidersToIndexThisPage' => true
-               ));
+               ]);
        }
 }
index a011dd56e3de4a0488de6c6975fe3f37b347a76e..4d2143640f4f2bfb30676dd9b60c678c66ad6901 100644 (file)
@@ -3,11 +3,10 @@ namespace wcf\page;
 use wcf\data\search\ISearchResultObject;
 use wcf\data\search\Search;
 use wcf\system\application\ApplicationHandler;
-use wcf\system\breadcrumb\Breadcrumb;
 use wcf\system\event\EventHandler;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\SystemException;
-use wcf\system\request\LinkHandler;
+use wcf\system\page\PageLocationManager;
 use wcf\system\search\SearchEngine;
 use wcf\system\WCF;
 
@@ -108,7 +107,7 @@ class SearchResultPage extends MultipleLinkPage {
                }
                
                // add breadcrumbs
-               WCF::getBreadcrumbs()->add(new Breadcrumb(WCF::getLanguage()->get('wcf.search.title'), LinkHandler::getInstance()->getLink('Search')));
+               PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.Search');
        }
        
        /**
index 84d357f37d5e8850ef874b42308defed05fde424..3005e9ecbd1aa1a1754c6947ab1229f7c1b1a988 100644 (file)
@@ -1,14 +1,14 @@
 <?php
 namespace wcf\page;
-use wcf\system\breadcrumb\Breadcrumb;
-use wcf\system\request\LinkHandler;
+use wcf\data\user\TeamList;
+use wcf\system\page\PageLocationManager;
 use wcf\system\WCF;
 
 /**
  * Shows the team members list.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage page
@@ -16,58 +16,58 @@ use wcf\system\WCF;
  */
 class TeamPage extends MultipleLinkPage {
        /**
-        * @see \wcf\page\AbstractPage::$neededPermissions
+        * @inheritDoc
         */
-       public $neededPermissions = array('user.profile.canViewMembersList');
+       public $neededPermissions = ['user.profile.canViewMembersList'];
        
        /**
-        * @see \wcf\page\AbstractPage::$neededModules
+        * @inheritDoc
         */
-       public $neededModules = array('MODULE_TEAM_PAGE');
+       public $neededModules = ['MODULE_TEAM_PAGE'];
        
        /**
-        * @see \wcf\page\AbstractPage::$enableTracking
+        * @inheritDoc
         */
        public $enableTracking = true;
        
        /**
-        * @see \wcf\page\MultipleLinkPage::$itemsPerPage
+        * @inheritDoc
         */
        public $itemsPerPage = 1000;
        
        /**
-        * @see \wcf\page\MultipleLinkPage::$sortField
+        * @inheritDoc
         */
        public $sortField = MEMBERS_LIST_DEFAULT_SORT_FIELD;
        
        /**
-        * @see \wcf\page\MultipleLinkPage::$sortOrder
+        * @inheritDoc
         */
        public $sortOrder = MEMBERS_LIST_DEFAULT_SORT_ORDER;
        
        /**
-        * @see \wcf\page\MultipleLinkPage::$objectListClassName
+        * @inheritDoc
         */
-       public $objectListClassName = 'wcf\data\user\TeamList';
+       public $objectListClassName = TeamList::class;
        
        /**
-        * @see \wcf\page\IPage::readData()
+        * @inheritDoc
         */
        public function readData() {
                parent::readData();
                
                // add breadcrumbs
-               if (MODULE_MEMBERS_LIST) WCF::getBreadcrumbs()->add(new Breadcrumb(WCF::getLanguage()->get('wcf.user.members'), LinkHandler::getInstance()->getLink('MembersList')));
+               if (MODULE_MEMBERS_LIST) PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.MembersList');
        }
        
        /**
-        * @see \wcf\page\IPage::assignVariables()
+        * @inheritDoc
         */
        public function assignVariables() {
                parent::assignVariables();
                
-               WCF::getTPL()->assign(array(
+               WCF::getTPL()->assign([
                        'allowSpidersToIndexThisPage' => true
-               ));
+               ]);
        }
 }
index b2c870f5ca0667d1a815eb96dcb00e02715cafe6..64854dc23075e4e943cce1a9810222892d2c51d5 100644 (file)
@@ -9,11 +9,11 @@ use wcf\data\user\profile\visitor\UserProfileVisitorEditor;
 use wcf\data\user\profile\visitor\UserProfileVisitorList;
 use wcf\data\user\UserEditor;
 use wcf\data\user\UserProfile;
-use wcf\system\breadcrumb\Breadcrumb;
 use wcf\system\cache\runtime\UserProfileRuntimeCache;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\menu\user\profile\UserProfileMenu;
+use wcf\system\page\PageLocationManager;
 use wcf\system\request\LinkHandler;
 use wcf\system\MetaTagHandler;
 use wcf\system\WCF;
@@ -110,7 +110,7 @@ class UserPage extends AbstractPage {
                parent::readData();
                
                // add breadcrumbs
-               if (MODULE_MEMBERS_LIST) WCF::getBreadcrumbs()->add(new Breadcrumb(WCF::getLanguage()->get('wcf.user.members'), LinkHandler::getInstance()->getLink('MembersList')));
+               if (MODULE_MEMBERS_LIST) PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.MembersList');
                
                // get profile content
                if ($this->editOnInit) {
index 528ed7e7e4d82e459822d8577a7d109f06cfa27f..c906292cc4b335b315e3541ee894795781fd61d8 100644 (file)
@@ -3,8 +3,8 @@ namespace wcf\page;
 use wcf\data\page\PageCache;
 use wcf\data\user\online\UserOnline;
 use wcf\data\user\online\UsersOnlineList;
-use wcf\system\breadcrumb\Breadcrumb;
 use wcf\system\page\handler\IOnlineLocationPageHandler;
+use wcf\system\page\PageLocationManager;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
 use wcf\util\HeaderUtil;
@@ -101,9 +101,7 @@ class UsersOnlineListPage extends SortablePage {
                parent::readData();
                
                // add breadcrumbs
-               if (MODULE_MEMBERS_LIST) {
-                       WCF::getBreadcrumbs()->add(new Breadcrumb(WCF::getLanguage()->get('wcf.user.members'), LinkHandler::getInstance()->getLink('MembersList')));
-               }
+               if (MODULE_MEMBERS_LIST) PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.MembersList');
                
                // cache all necessary data for showing locations
                foreach ($this->objectList as $userOnline) {
index 39e49809d1ef22f010b7343ae151e956d4bb9598..0994a605b9986d73915994ac3f7c2496bfdfc98c 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 namespace wcf\system\breadcrumb;
+use wcf\data\page\PageCache;
+use wcf\system\page\PageLocationManager;
 use wcf\system\SingletonFactory;
 
 /**
@@ -17,7 +19,7 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
         * list of breadcrumbs
         * @var Breadcrumb[]
         */
-       protected $items = [];
+       protected $items = null;
        
        /**
         * Current iterator-index
@@ -37,9 +39,10 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
         * Adds a breadcrumb (insertion order is crucial!).
         * 
         * @param       Breadcrumb      $item
+        * @deprecated  2.2
         */
        public function add(Breadcrumb $item) {
-               $this->items[] = $item;
+               throw new \BadMethodCallException("Breadcrumbs::add() is no longer supported, please use " . PageLocationManager::class . " instead.");
        }
        
        /**
@@ -48,6 +51,10 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
         * @return      Breadcrumb[]
         */
        public function get() {
+               if ($this->items === null) {
+                       $this->loadBreadcrumbs();
+               }
+               
                return $this->items;
        }
        
@@ -57,15 +64,10 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
         * @param       Breadcrumb      $item
         * @param       integer         $index
         * @return      boolean
+        * @deprecated  2.2
         */
        public function replace(Breadcrumb $item, $index) {
-               if (isset($this->items[$index])) {
-                       $this->items[$index] = $item;
-                       
-                       return true;
-               }
-               
-               return false;
+               throw new \BadMethodCallException("Breadcrumbs::replace() is no longer supported, please use " . PageLocationManager::class . " instead.");
        }
        
        /**
@@ -73,21 +75,53 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
         * 
         * @param       integer         $index
         * @return      boolean
+        * @deprecated  2.2
         */
        public function remove($index) {
-               if (isset($this->items[$index])) {
-                       unset($this->items[$index]);
-                       
-                       return true;
+               throw new \BadMethodCallException("Breadcrumbs::remove() is no longer supported, please use " . PageLocationManager::class . " instead.");
+       }
+       
+       protected function loadBreadcrumbs() {
+               $this->items = [];
+               $locations = PageLocationManager::getInstance()->getLocations();
+               
+               // locations are provided starting with the current location followed
+               // by all relevant ancestors, but breadcrumbs appear in the reverse order
+               $locations = array_reverse($locations);
+               
+               // add the landing page as first location, unless it is already included
+               $landingPage = PageCache::getInstance()->getLandingPage();
+               $addLandingPage = true;
+               for ($i = 0, $length = count($locations); $i < $length; $i++) {
+                       if ($locations[$i]['pageID'] == $landingPage->pageID) {
+                               $addLandingPage = false;
+                               break;
+                       }
+               }
+               
+               if ($addLandingPage) {
+                       array_unshift($locations, [
+                               'link' => $landingPage->getLink(),
+                               'title' => $landingPage->getTitle()
+                       ]);
                }
                
-               return false;
+               // ignore the last location as it represents the current page
+               array_pop($locations);
+               
+               for ($i = 0, $length = count($locations); $i < $length; $i++) {
+                       $this->items[] = new Breadcrumb($locations[$i]['title'], $locations[$i]['link']);
+               }
        }
        
        /**
         * @inheritDoc
         */
        public function count() {
+               if ($this->items === null) {
+                       $this->loadBreadcrumbs();
+               }
+               
                return count($this->items);
        }
        
index 6447f21216dd4b649b1ec38c5e41fc206787cbb3..6eaedf88e4bd6dab3fe3fb33fd6d4dc46d7456fb 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\system\cache\builder;
 use wcf\data\page\Page;
 use wcf\data\page\PageList;
+use wcf\system\WCF;
 
 /**
  * Caches the page data.
@@ -12,6 +13,7 @@ use wcf\data\page\PageList;
  * @package    com.woltlab.wcf
  * @subpackage system.cache.builder
  * @category   Community Framework
+ * @since       2.2
  */
 class PageCacheBuilder extends AbstractCacheBuilder {
        /**
@@ -22,6 +24,7 @@ class PageCacheBuilder extends AbstractCacheBuilder {
                        'identifier' => [],
                        'controller' => [],
                        'pages' => [],
+                       'pageTitles' => [],
                        'landingPage' => null
                ];
                
@@ -29,6 +32,21 @@ class PageCacheBuilder extends AbstractCacheBuilder {
                $pageList->readObjects();
                $data['pages'] = $pageList->getObjects();
                
+               // get page titles
+               $sql = "SELECT  pageID, languageID, title
+                       FROM    wcf".WCF_N."_page_content";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute();
+               while ($row = $statement->fetchArray()) {
+                       $pageID = $row['pageID'];
+                       
+                       if (!isset($data['pageTitles'])) {
+                               $data['pageTitles'][$pageID] = [];
+                       }
+                       
+                       $data['pageTitles'][$pageID][$row['languageID'] ?: 0] = $row['title'];
+               }
+               
                // build lookup table
                /** @var Page $page */
                foreach ($pageList as $page) {
index 98a99b36a0546849b831667e0c672b0354404605..31e2e80eb3ad653a3d17db3233551d38727d1685 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\system\page;
+use wcf\data\ITitledLinkObject;
 use wcf\data\page\PageCache;
 use wcf\system\exception\SystemException;
 use wcf\system\request\RequestHandler;
@@ -14,6 +15,7 @@ use wcf\system\SingletonFactory;
  * @package    com.woltlab.wcf
  * @subpackage system.page
  * @category   Community Framework
+ * @since       2.2
  */
 class PageLocationManager extends SingletonFactory {
        /**
@@ -34,8 +36,12 @@ class PageLocationManager extends SingletonFactory {
                }
                
                $metaData = $activeRequest->getMetaData();
+               $link = $title = '';
+               $page = null;
                if (isset($metaData['cms'])) {
                        $pageID = $metaData['cms']['pageID'];
+                       
+                       $page = PageCache::getInstance()->getPage($pageID);
                }
                else {
                        $page = PageCache::getInstance()->getPageByController($activeRequest->getClassName());
@@ -48,8 +54,11 @@ class PageLocationManager extends SingletonFactory {
                
                if ($pageID !== null) {
                        $this->stack[] = [
+                               'identifier' => $page->identifier,
+                               'link' => $page->getLink(),
                                'pageID' => $pageID,
-                               'pageObjectID' => $pageObjectID
+                               'pageObjectID' => $pageObjectID,
+                               'title' => $page->getTitle()
                        ];
                }
        }
@@ -58,19 +67,39 @@ class PageLocationManager extends SingletonFactory {
         * Appends a parent location to the stack, the later it is added the lower
         * is its assumed priority when matching suitable menu items.
         * 
-        * @param       string          $identifier     internal page identifier
-        * @param       integer         $pageObjectID   page object id
+        * @param       string                  $identifier             internal page identifier
+        * @param       integer                 $pageObjectID           page object id
+        * @param       ITitledLinkObject       $locationObject         optional label for breadcrumbs usage
         * @throws      SystemException
         */
-       public function addParentLocation($identifier, $pageObjectID = 0) {
+       public function addParentLocation($identifier, $pageObjectID = 0, ITitledLinkObject $locationObject = null) {
                $page = PageCache::getInstance()->getPageByIdentifier($identifier);
                if ($page === null) {
                        throw new SystemException("Unknown page identifier '".$identifier."'.");
                }
                
+               // check if the provided location is already part of the stack
+               for ($i = 0, $length = count($this->stack); $i < $length; $i++) {
+                       if ($this->stack[$i]['identifier'] == $identifier && $this->stack[$i]['pageObjectID'] == $pageObjectID) {
+                               return;
+                       }
+               }
+               
+               if ($locationObject !== null) {
+                       $link = $locationObject->getLink();
+                       $title = $locationObject->getTitle();
+               }
+               else {
+                       $link = $page->getLink();
+                       $title = $page->getTitle();
+               }
+               
                $this->stack[] = [
+                       'identifier' => $identifier,
+                       'link' => $link,
                        'pageID' => $page->pageID,
-                       'pageObjectID' => $pageObjectID
+                       'pageObjectID' => $pageObjectID,
+                       'title' => $title
                ];
        }
        
index dca618a7f38284d59f5074efb7521ba749549a8f..9aaf799a4d4fb25a93e4ae575eec02deb0610009 100644 (file)
@@ -10,7 +10,7 @@ use wcf\system\WCF;
  * Resolves incoming requests and performs lookups for controller to url mappings.
  * 
  * @author     Alexander Ebert
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage system.request
@@ -57,7 +57,7 @@ class ControllerMap extends SingletonFactory {
         * @param       string          $application    application identifier
         * @param       string          $controller     url controller
         * @param       boolean         $isAcpRequest   true if this is an ACP request
-        * @return      mixed   array containing className, controller and pageType or a string containing the controller name for aliased controllers
+        * @return      mixed           array containing className, controller and pageType or a string containing the controller name for aliased controllers
         * @throws      SystemException
         */
        public function resolve($application, $controller, $isAcpRequest) {