Implemented landing page support
authorAlexander Ebert <ebert@woltlab.com>
Wed, 26 Dec 2012 02:56:03 +0000 (03:56 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 26 Dec 2012 02:56:03 +0000 (03:56 +0100)
See #998
Closes #1000

wcfsetup/install/files/acp/js/WCF.ACP.js
wcfsetup/install/files/acp/templates/pageMenuItemAdd.tpl
wcfsetup/install/files/acp/templates/pageMenuItemList.tpl
wcfsetup/install/files/lib/data/page/menu/item/PageMenuItem.class.php
wcfsetup/install/files/lib/data/page/menu/item/PageMenuItemAction.class.php
wcfsetup/install/files/lib/data/page/menu/item/PageMenuItemEditor.class.php
wcfsetup/install/files/lib/system/menu/page/PageMenu.class.php
wcfsetup/install/files/lib/system/request/RequestHandler.class.php
wcfsetup/install/files/lib/system/request/Route.class.php
wcfsetup/install/files/lib/system/request/RouteHandler.class.php
wcfsetup/install/lang/de.xml

index f3c33ba0c02262393597edacbf0d2779b3e014ec..dfdb1cff2082c4fccb343e0416d3815ab39852b7 100644 (file)
@@ -708,6 +708,67 @@ WCF.ACP.Package.Uninstallation = WCF.ACP.Package.Installation.extend({
        }
 });
 
+/**
+ * Namespace for page menu.
+ */
+WCF.ACP.PageMenu = { };
+
+/**
+ * Allows menu items to be set as landing page.
+ * 
+ * @param      integer         menuItemID
+ */
+WCF.ACP.PageMenu.SetAsLandingPage = Class.extend({
+       /**
+        * menu item id
+        * @var integer
+        */
+       _menuItemID: 0,
+       
+       /**
+        * Initializes the WCF.ACP.PageMenu.SetAsLandingPage class.
+        * 
+        * @param       integer         menuItemID
+        */
+       init: function(menuItemID) {
+               this._menuItemID = menuItemID;
+               
+               $('#setAsLandingPage').click($.proxy(this._click, this));
+       },
+       
+       /**
+        * Handles button clicks.
+        */
+       _click: function() {
+               var self = this;
+               WCF.System.Confirmation.show(WCF.Language.get('wcf.acp.pageMenu.isLandingPage.confirmMessage'), function(action) {
+                       if (action === 'confirm') {
+                               new WCF.Action.Proxy({
+                                       autoSend: true,
+                                       data: {
+                                               actionName: 'setAsLandingPage',
+                                               className: 'wcf\\data\\page\\menu\\item\\PageMenuItemAction',
+                                               objectIDs: [ self._menuItemID ]
+                                       },
+                                       success: $.proxy(self._success, self)
+                               });
+                       }
+               });
+       },
+       
+       /**
+        * Handles successful AJAX requests.
+        * 
+        * @param       object          data
+        * @param       string          textStatus
+        * @param       jQuery          jqXHR
+        */
+       _success: function(data, textStatus, jqXHR) {
+               var $notification = new WCF.System.Notification(WCF.Language.get('wcf.acp.pageMenu.isLandingPage.success'));
+               $notification.show(function() { window.location.reload(); });
+       }
+});
+
 /**
  * Handles option selection.
  */
index 86f6cbf9d203d7857f8a30186242153b09658501..6a4b1a56dc9bb6057d04753104774b62766fb2db 100644 (file)
                
                handleMenuPosition();
                handleIsInternalLink();
+               
+               {if $action == 'edit' && $menuItem->isValidLandingPage()}
+                       WCF.Language.addObject({
+                               'wcf.acp.pageMenu.isLandingPage.confirmMessage': '{lang}wcf.acp.pageMenu.isLandingPage.confirmMessage{/lang}',
+                               'wcf.acp.pageMenu.isLandingPage.success': '{lang}wcf.acp.pageMenu.isLandingPage.success{/lang}'
+                       });
+                       
+                       new WCF.ACP.PageMenu.SetAsLandingPage({@$menuItem->menuItemID});
+               {/if}
        });
        //]]>
 </script>
@@ -52,6 +61,9 @@
 <div class="contentNavigation">
        <nav>
                <ul>
+                       {if $action == 'edit' && $menuItem->isValidLandingPage()}
+                               <li id="setAsLandingPage"><a class="button"><img src="{@$__wcf->getPath()}icon/default.svg" /> <span>{lang}wcf.acp.pageMenu.setAsLandingPage{/lang}</span></a></li>
+                       {/if}
                        <li><a href="{link controller='PageMenuItemList'}{/link}" class="button"><img src="{@$__wcf->getPath()}icon/list.svg" alt="" /> <span>{lang}wcf.acp.pageMenu.list{/lang}</span></a></li>
                </ul>
        </nav>
index 23dca21a79ac94317757324af3911b84a01ce4f8..ceefec23be1bbe49e67843580a73bdd7c37777ca 100644 (file)
                                                        <span class="sortableNodeLabel">
                                                                <a href="{link controller='PageMenuItemEdit' id=$menuItem->menuItemID}{/link}">{lang}{$menuItem->menuItem}{/lang}</a>
                                                                <span class="statusDisplay sortableButtonContainer">
-                                                                       <img src="{@$__wcf->getPath()}icon/{if $menuItem->isDisabled}disabled{else}enabled{/if}.svg" alt="" title="{lang}wcf.global.button.{if $menuItem->isDisabled}enable{else}disable{/if}{/lang}" class="icon16 jsToggleButton jsTooltip pointer" data-object-id="{@$menuItem->menuItemID}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" />
+                                                                       {if $menuItem->canDisable()}
+                                                                               <img src="{@$__wcf->getPath()}icon/{if $menuItem->isDisabled}disabled{else}enabled{/if}.svg" alt="" title="{lang}wcf.global.button.{if $menuItem->isDisabled}enable{else}disable{/if}{/lang}" class="icon16 jsToggleButton jsTooltip pointer" data-object-id="{@$menuItem->menuItemID}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" />
+                                                                       {else}
+                                                                               <img src="{@$__wcf->getPath()}icon/enabled.svg" alt="" class="icon16 disabled" />
+                                                                       {/if}
                                                                        <a href="{link controller='PageMenuItemEdit' id=$menuItem->menuItemID}{/link}" class="jsTooltip" title="{lang}wcf.global.button.edit{/lang}"><img src="{@$__wcf->getPath()}icon/edit.svg" alt="" class="icon16" /></a>
-                                                                       <img src="{@$__wcf->getPath()}icon/delete.svg" alt="" title="{lang}wcf.global.button.delete{/lang}" class="icon16 jsDeleteButton jsTooltip pointer" data-object-id="{@$menuItem->menuItemID}" data-confirm-message="{lang __menuItem=$menuItem}wcf.acp.pageMenu.delete.sure{/lang}" />
+                                                                       {if $menuItem->canDelete()}
+                                                                               <img src="{@$__wcf->getPath()}icon/delete.svg" alt="" title="{lang}wcf.global.button.delete{/lang}" class="icon16 jsDeleteButton jsTooltip pointer" data-object-id="{@$menuItem->menuItemID}" data-confirm-message="{lang __menuItem=$menuItem}wcf.acp.pageMenu.delete.sure{/lang}" />
+                                                                       {else}
+                                                                               <img src="{@$__wcf->getPath()}icon/delete.svg" alt="" class="icon16 disabled" />
+                                                                       {/if}
                                                                </span>
                                                        </span>
                                                        {if $menuItem|count}
index 1a6eb697aaa0e5f4943c09dabd93eed9611a5d44..93a84cce09e1ffe9cf656cd8401ddeff5f253553 100644 (file)
@@ -33,6 +33,18 @@ class PageMenuItem extends ProcessibleDatabaseObject implements ITreeMenuItem {
         */
        protected static $processorInterface = 'wcf\system\menu\page\IPageMenuItemProvider';
        
+       /**
+        * application abbreviation
+        * @var string
+        */
+       protected $application = '';
+       
+       /**
+        * menu item controller
+        * @var string
+        */
+       protected $controller = null;
+       
        /**
         * @see wcf\data\ProcessibleDatabaseObject::getProcessor()
         */
@@ -53,15 +65,85 @@ class PageMenuItem extends ProcessibleDatabaseObject implements ITreeMenuItem {
                        return WCF::getLanguage()->get($this->menuItemLink);
                }
                
-               // resolve application and controller
-               $parts = explode('\\', $this->menuItemController);
-               $abbreviation = array_shift($parts);
-               $controller = array_pop($parts);
+               $this->parseController();
+               return LinkHandler::getInstance()->getLink($this->controller, array('application' => $this->application), WCF::getLanguage()->get($this->menuItemLink));
+       }
+       
+       /**
+        * Returns true, if current menu item may be set as landing page.
+        * 
+        * @return      boolean
+        */
+       public function isValidLandingPage() {
+               // item must be a top header menu item without parents
+               if ($this->menuPosition != 'header' || $this->parentMenuItem) {
+                       return false;
+               }
+               
+               // external links are not valid
+               if (!$this->menuItemController) {
+                       return false;
+               }
+               
+               // already is landing page
+               if ($this->isLandingPage) {
+                       return false;
+               }
                
-               // drop controller suffix
-               $controller = Regex::compile('(Action|Form|Page)$')->replace($controller, '');
+               // disabled items cannot be a landing page
+               if ($this->isDisabled) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       /**
+        * Returns true, if this item can be deleted.
+        * 
+        * @return      boolean
+        */
+       public function canDelete() {
+               return ($this->isLandingPage ? false : true);
+       }
+       
+       /**
+        * Returns true, if this item can be disabled.
+        * 
+        * @return      boolean
+        */
+       public function canDisable() {
+               return ($this->isLandingPage ? false : true);
+       }
+       
+       /**
+        * Returns controller name.
+        * 
+        * @return      string
+        */
+       public function getController() {
+               $this->parseController();
                
-               return LinkHandler::getInstance()->getLink($controller, array('application' => $abbreviation), WCF::getLanguage()->get($this->menuItemLink));
+               return $this->controller;
+       }
+       
+       /**
+        * Parses controller name.
+        */
+       protected function parseController() {
+               if ($this->controller === null) {
+                       $this->controller = '';
+                               
+                       // resolve application and controller
+                       if ($this->menuItemController) {
+                               $parts = explode('\\', $this->menuItemController);
+                               $this->application = array_shift($parts);
+                               $menuItemController = array_pop($parts);
+                               
+                               // drop controller suffix
+                               $this->controller = Regex::compile('(Action|Form|Page)$')->replace($menuItemController, '');
+                       }
+               }
        }
        
        /**
index 62b767f97ae939899310899ec7fe208b93a117f1..20cf883a9ba1303326ab17f58ec6de3abb2fcd94 100644 (file)
@@ -3,6 +3,7 @@ namespace wcf\data\page\menu\item;
 use wcf\data\AbstractDatabaseObjectAction;
 use wcf\data\ISortableAction;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
 
@@ -22,6 +23,12 @@ class PageMenuItemAction extends AbstractDatabaseObjectAction implements ISortab
         */
        protected $className = 'wcf\data\page\menu\item\PageMenuItemEditor';
        
+       /**
+        * page menu item editor
+        * @var wcf\data\page\menu\item\PageMenuItemEditor
+        */
+       public $menuItemEditor = null;
+       
        /**
         * list of menu items
         * @var array<wcf\data\page\menu\item\PageMenuItem>
@@ -46,6 +53,25 @@ class PageMenuItemAction extends AbstractDatabaseObjectAction implements ISortab
                return $menuItem;
        }
        
+       /**
+        * Validates parameters to set a menu item as landing page.
+        */
+       public function validateSetAsLandingPage() {
+               WCF::getSession()->checkPermissions(array('admin.display.canManagePageMenu'));
+               
+               $this->menuItemEditor = $this->getSingleObject();
+               if (!$this->menuItemEditor->isValidLandingPage()) {
+                       throw new PermissionDeniedException();
+               }
+       }
+       
+       /**
+        * Sets a menu item as landing page.
+        */
+       public function setAsLandingPage() {
+               $this->menuItemEditor->setAsLandingPage();
+       }
+       
        /**
         * @see wcf\data\ISortableAction::validateUpdatePosition()
         */
index 39c1938e4286f64d7206d04cb9950ce7dd236821..22aa0616d0a3151061335ce56c2f528579887478 100644 (file)
@@ -74,6 +74,8 @@ class PageMenuItemEditor extends DatabaseObjectEditor implements IEditableCached
                $statement->execute();
                
                $this->update(array('isLandingPage' => 1));
+               
+               self::resetCache();
        }
        
        /**
index bf1469ac3c331f17e27cb7dbf8e597f268dabc62..50c32e229927dd1015dadfe0276a53846ad46f97 100644 (file)
@@ -16,6 +16,12 @@ use wcf\system\menu\TreeMenu;
  * @category   Community Framework
  */
 class PageMenu extends TreeMenu {
+       /**
+        * landing page menu item
+        * @var wcf\data\page\menu\item\PageMenuItem
+        */
+       protected $landingPage = null;
+       
        /**
         * @see wcf\system\SingletonFactory::init()
         */
@@ -33,6 +39,24 @@ class PageMenu extends TreeMenu {
                
                // call init event
                EventHandler::getInstance()->fireAction($this, 'init');
+               
+               foreach ($this->menuItems as $menuItems) {
+                       foreach ($menuItems as $menuItem) {
+                               if ($menuItem->isLandingPage) {
+                                       $this->landingPage = $menuItem;
+                                       break 2;
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * Returns landing page menu item.
+        * 
+        * @return      wcf\data\page\menu\item\PageMenuItem
+        */
+       public function getLandingPage() {
+               return $this->landingPage;
        }
        
        /**
index df1e52af52b36a4b53f325996fad080ce9a9e115..6c265f166ce67fb0227a348a6a1616b5f931d892 100644 (file)
@@ -1,11 +1,13 @@
 <?php
 namespace wcf\system\request;
-use wcf\util\HeaderUtil;
+use wcf\util\StringUtil;
 
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\SystemException;
+use wcf\system\menu\page\PageMenu;
 use wcf\system\SingletonFactory;
 use wcf\system\WCF;
+use wcf\util\HeaderUtil;
 
 /**
  * Handles http requests.
@@ -67,18 +69,27 @@ class RequestHandler extends SingletonFactory {
         * @param       string          $application
         */
        protected function buildRequest($application) {
-               try {
+               //try {
                        $routeData = RouteHandler::getInstance()->getRouteData();
-                       $controller = $routeData['controller'];
                        
-                       /*
-                        * @todo redirect to landing page once page menu items support controller based URLs (see https://github.com/WoltLab/WCF/issues/998)
-                        * 
-                       if (RouteHandler::getInstance()->isDefaultController()) {
-                               HeaderUtil::redirect(..., true);
-                               exit;
+                       // handle landing page for frontend requests
+                       if (!$this->isACPRequest()) {
+                               $landingPage = PageMenu::getInstance()->getLandingPage();
+                               if ($landingPage !== null && RouteHandler::getInstance()->isDefaultController()) {
+                                       // check if redirect URL matches current URL
+                                       $redirectURL = $landingPage->getLink();
+                                       if (StringUtil::replace(RouteHandler::getHost(), '', $redirectURL) == $_SERVER['REQUEST_URI']) {
+                                               $routeData['controller'] = $landingPage->getController();
+                                       }
+                                       else {
+                                               // redirect to landing page
+                                               HeaderUtil::redirect($landingPage->getLink(), true);
+                                               exit;
+                                       }
+                               }
                        }
-                       */
+                       
+                       $controller = $routeData['controller'];
                        
                        // validate class name
                        if (!preg_match('~^[a-z0-9_]+$~i', $controller)) {
@@ -98,10 +109,10 @@ class RequestHandler extends SingletonFactory {
                        }
                        
                        $this->activeRequest = new Request($classData['className'], $classData['controller'], $classData['pageType']);
-               }
-               catch (SystemException $e) {
-                       throw new IllegalLinkException();
-               }
+               //}
+               //catch (SystemException $e) {
+               //      throw new IllegalLinkException();
+               //}
        }
        
        /**
index 5e1b9316bd540834d75aa86a6f16ea1218f3b077..04bb8462b4c209e9e4e6f3d5e66c2105fd1ba486 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 namespace wcf\system\request;
+use wcf\system\menu\page\PageMenu;
+
 use wcf\system\exception\SystemException;
 
 /**
@@ -111,10 +113,6 @@ class Route {
         * @param       boolean         $isOptional
         */
        public function setParameterOption($key, $default = null, $regexPattern = null, $isOptional = false) {
-               if ($key == 'controller' && (empty($default) && $isOptional)) {
-                       throw new SystemException('Routes require a controller, it is not possible to regard them as optional without a default value.');
-               }
-               
                $this->parameterOptions[$key] = array(
                        'default' => $default,
                        'isOptional' => $isOptional,
@@ -171,7 +169,7 @@ class Route {
                }
                
                if (!isset($data['isDefaultController'])) {
-                       $data['isDefaultController'] = true;
+                       $data['isDefaultController'] = false;
                }
                
                $this->routeData = $data;
@@ -181,6 +179,10 @@ class Route {
                        $this->routeData['controller'] = $this->controller;
                }
                
+               if (!isset($this->routeData['controller'])) {
+                       $this->routeData['isDefaultController'] = true;
+               }
+               
                return true;
        }
        
@@ -257,9 +259,21 @@ class Route {
                
                // handle default values for controller
                $buildRoute = true;
-               if (count($components) == 1) {
+               if (count($components) == 1 && isset($components['controller'])) {
+                       $ignoreController = false;
                        if (isset($this->parameterOptions['controller']) && strcasecmp($this->parameterOptions['controller']['default'], $components['controller']) == 0) {
                                // only the controller was given and matches default, omit routing
+                               $ignoreController = true;
+                       }
+                       else {
+                               $landingPage = PageMenu::getInstance()->getLandingPage();
+                               if ($landingPage !== null && ($landingPage->getController() == $components['controller'])) {
+                                       $ignoreController = true;
+                               }
+                       }
+                       
+                       // drops controller from route
+                       if ($ignoreController) {
                                $buildRoute = false;
                                
                                // unset the controller, since it would otherwise added with http_build_query()
@@ -285,7 +299,9 @@ class Route {
                        }
                }
                
-               $link = 'index.php' . (!empty($link) ? '/' : '') . $link;
+               if (!empty($link)) {
+                       $link = 'index.php/' . $link;
+               }
                
                if (!empty($components)) {
                        $link .= '?' . http_build_query($components, '', '&');
index 0e408ff2b0741ce61c686afcbeacfaf76c360994..1064bb1c0fbeb922cc62ae8cbff17eb973c1f72d 100644 (file)
@@ -94,7 +94,7 @@ class RouteHandler extends SingletonFactory {
                
                $defaultRoute = new Route('default');
                $defaultRoute->setSchema('/{controller}/{id}');
-               $defaultRoute->setParameterOption('controller', 'Index', null, true);
+               $defaultRoute->setParameterOption('controller', null, null, true);
                $defaultRoute->setParameterOption('id', null, '\d+', true);
                $this->addRoute($defaultRoute);
        }
index cc5ef45a129990b2d9eab377af89ab5ffa85565a..ea0e54f33ea31bb5b49dcd1511793aee3b445ec9 100644 (file)
                <item name="wcf.acp.pageMenu.footer"><![CDATA[Fußzeile]]></item>
                <item name="wcf.acp.pageMenu.header"><![CDATA[Hauptmenü]]></item>
                <item name="wcf.acp.pageMenu.isDisabled"><![CDATA[Menüpunkt deaktivieren]]></item>
-               <item name="wcf.acp.pageMenu.isLandingPage"><![CDATA[Startseite Ihrer Website]]></item>
-               <item name="wcf.acp.pageMenu.isLandingPage.description"><![CDATA[Beim direkten Aufruf Ihrer Website wird diese Menüpunkt aufgerufen.]]></item>
+               <item name="wcf.acp.pageMenu.isLandingPage.confirmMessage"><![CDATA[Möchten Sie den Menüpunkt „{$menuItem}“ als Startseite Ihrer Website festlegen?]]></item>
+               <item name="wcf.acp.pageMenu.isLandingPage.success"><![CDATA[„{$menuItem}“ ist nun als Startseite festgelegt]]></item>
                <item name="wcf.acp.pageMenu.link"><![CDATA[Link]]></item>
                <item name="wcf.acp.pageMenu.link.external"><![CDATA[Externer Link]]></item>
                <item name="wcf.acp.pageMenu.link.internal"><![CDATA[Interner Link]]></item>
                <item name="wcf.acp.pageMenu.menuPosition.header"><![CDATA[Hauptmenü]]></item>
                <item name="wcf.acp.pageMenu.pageMenuItem"><![CDATA[Name]]></item>
                <item name="wcf.acp.pageMenu.parentMenuItem"><![CDATA[Übergeordneter Menüpunkt]]></item>
+               <item name="wcf.acp.pageMenu.setAsLandingPage"><![CDATA[Als Startseite festlegen]]></item>
                <item name="wcf.acp.pageMenu.showOrder"><![CDATA[Reihenfolge]]></item>
        </category>