Overhauled page / menu system, wip
authorAlexander Ebert <ebert@woltlab.com>
Wed, 16 Dec 2015 12:55:43 +0000 (13:55 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 16 Dec 2015 12:55:43 +0000 (13:55 +0100)
47 files changed:
com.woltlab.wcf/acpMenu.xml
com.woltlab.wcf/menu.xml [new file with mode: 0644]
com.woltlab.wcf/menuItem.xml [new file with mode: 0644]
com.woltlab.wcf/package.xml
com.woltlab.wcf/packageInstallationPlugin.xml
com.woltlab.wcf/page.xml
com.woltlab.wcf/templates/pageHeaderMenu.tpl
wcfsetup/install/files/acp/js/WCF.ACP.js
wcfsetup/install/files/acp/templates/applicationEdit.tpl
wcfsetup/install/files/acp/templates/applicationManagement.tpl
wcfsetup/install/files/acp/templates/pageAdd.tpl
wcfsetup/install/files/acp/templates/pageLanding.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/pageList.tpl
wcfsetup/install/files/lib/acp/form/LoginForm.class.php
wcfsetup/install/files/lib/acp/form/PageAddForm.class.php
wcfsetup/install/files/lib/acp/form/PageLandingForm.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/application/Application.class.php
wcfsetup/install/files/lib/data/application/ApplicationAction.class.php
wcfsetup/install/files/lib/data/application/ApplicationEditor.class.php
wcfsetup/install/files/lib/data/box/Box.class.php
wcfsetup/install/files/lib/data/menu/MenuCache.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/menu/ViewableMenu.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/menu/item/MenuItemNodeTree.class.php
wcfsetup/install/files/lib/data/page/Page.class.php
wcfsetup/install/files/lib/page/AbstractPage.class.php
wcfsetup/install/files/lib/system/WCF.class.php
wcfsetup/install/files/lib/system/application/ApplicationHandler.class.php
wcfsetup/install/files/lib/system/box/BoxHandler.class.php
wcfsetup/install/files/lib/system/breadcrumb/Breadcrumbs.class.php
wcfsetup/install/files/lib/system/cache/builder/ApplicationCacheBuilder.class.php
wcfsetup/install/files/lib/system/cache/builder/MenuCacheBuilder.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/cache/builder/RoutingCacheBuilder.class.php
wcfsetup/install/files/lib/system/menu/page/PageMenu.class.php
wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php
wcfsetup/install/files/lib/system/package/plugin/BoxPackageInstallationPlugin.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/package/plugin/MenuPackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/package/plugin/PagePackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/request/ControllerMap.class.php
wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php
wcfsetup/install/files/lib/system/request/LinkHandler.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/route/DynamicRequestRoute.class.php
wcfsetup/install/files/lib/util/exception/CryptoException.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index 65f790266e517f075272624eb5914f647e579102..1364dd815030bef51eee5299d342a9ac644e126e 100644 (file)
                        <permissions>admin.content.cms.canManagePage</permissions>
                </acpmenuitem>
                
+               <acpmenuitem name="wcf.acp.menu.link.cms.page.landing">
+                       <controller><![CDATA[wcf\acp\form\PageLandingForm]]></controller>
+                       <parent>wcf.acp.menu.link.cms.page.list</parent>
+                       <permissions>admin.content.cms.canManagePage</permissions>
+                       <icon>fa-home</icon>
+               </acpmenuitem>
+               
                <acpmenuitem name="wcf.acp.menu.link.cms.page.add">
                        <controller><![CDATA[wcf\acp\form\PageAddForm]]></controller>
                        <parent>wcf.acp.menu.link.cms.page.list</parent>
diff --git a/com.woltlab.wcf/menu.xml b/com.woltlab.wcf/menu.xml
new file mode 100644 (file)
index 0000000..80da9c4
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/vortex/menu.xsd">
+       <import>
+               <menu identifier="com.woltlab.wcf.MainMenu">
+                       <title language="de">Hauptmenü</title>
+                       <title language="en">Main Menu</title>
+                       
+                       <box>
+                               <position>mainMenu</position>
+                               <showheader>0</showheader>
+                               <visibleeveryhwere>1</visibleeveryhwere>
+                       </box>
+               </menu>
+       </import>
+</data>
diff --git a/com.woltlab.wcf/menuItem.xml b/com.woltlab.wcf/menuItem.xml
new file mode 100644 (file)
index 0000000..cb2f054
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/vortex/menuItem.xsd">
+       <import>
+               <item identifier="com.woltlab.wcf.Dashboard">
+                       <menu>com.woltlab.wcf.MainMenu</menu>
+                       <title language="de"><![CDATA[Dashboard]]></title>
+                       <title language="en"><![CDATA[Dashboard]]></title>
+                       <page>com.woltlab.wcf.Dashboard</page>
+               </item>
+               
+               <item identifier="com.woltlab.wcf.MembersList">
+                       <menu>com.woltlab.wcf.MainMenu</menu>
+                       <title language="de"><![CDATA[Mitglieder]]></title>
+                       <title language="en"><![CDATA[Members]]></title>
+                       <page>com.woltlab.wcf.MembersList</page>
+               </item>
+               <item identifier="com.woltlab.wcf.RecentActivityList">
+                       <parent>com.woltlab.wcf.MembersList</parent>
+                       <title language="de"><![CDATA[Letzte Aktivitäten]]></title>
+                       <title language="en"><![CDATA[Recent Activities]]></title>
+                       <page>com.woltlab.wcf.RecentActivityList</page>
+               </item>
+               <item identifier="com.woltlab.wcf.UsersOnlineList">
+                       <parent>com.woltlab.wcf.MembersList</parent>
+                       <title language="de"><![CDATA[Benutzer Online]]></title>
+                       <title language="en"><![CDATA[Users Online]]></title>
+                       <page>com.woltlab.wcf.UsersOnlineList</page>
+               </item>
+               <item identifier="com.woltlab.wcf.Team">
+                       <parent>com.woltlab.wcf.MembersList</parent>
+                       <title language="de"><![CDATA[Team]]></title>
+                       <title language="en"><![CDATA[Team]]></title>
+                       <page>com.woltlab.wcf.Team</page>
+               </item>
+               <item identifier="com.woltlab.wcf.UserSearch">
+                       <parent>com.woltlab.wcf.MembersList</parent>
+                       <title language="de"><![CDATA[Benutzer suchen]]></title>
+                       <title language="en"><![CDATA[Search Users]]></title>
+                       <page>com.woltlab.wcf.UserSearch</page>
+               </item>
+               
+               <!-- TODO: privacy policy / footer menu? -->
+       </import>
+</data>
index 37dd5fbfbd493957464e61b71f59f920c5e2cff9..84ce6d35fcce4503ec8439786983a6c2db5a73a3 100644 (file)
@@ -5,7 +5,7 @@
                <packagedescription><![CDATA[Free web-framework, designed and developed for complex community applications.]]></packagedescription>
                <packagedescription language="de"><![CDATA[Freies Web-Framework, das für komplexe Community-Anwendungen entworfen und entwickelt wurde.]]></packagedescription>
                <isapplication>1</isapplication>
-               <version>2.2.0 Alpha 1</version> <!-- codename: vortex -->
+               <version>2.2.0 Alpha 2</version> <!-- codename: vortex -->
                <date>2015-10-22</date>
        </packageinformation>
        
                <instruction type="userNotificationEvent" />
                <instruction type="aclOption" />
                <instruction type="page" />
+               <instruction type="menu" />
+               <instruction type="menuItem" />
                <instruction type="script">acp/post_install.php</instruction>
        </instructions>
+       
+       <instructions type="update" fromversion="2.2.0 Alpha 1">
+               <instruction type="page" />
+               <instruction type="menuItem" />
+       </instructions>
 </package>
index 2ced21adcffe67bffdf8e739f1a38a48f72d2b46..33d2ecb8c1950154c2dd8d95a88fa76311c5277a 100644 (file)
@@ -30,5 +30,8 @@
                <pip name="userMenu">wcf\system\package\plugin\UserMenuPackageInstallationPlugin</pip>
                <pip name="userNotificationEvent">wcf\system\package\plugin\UserNotificationEventPackageInstallationPlugin</pip>
                <pip name="dashboardBox">wcf\system\package\plugin\DashboardBoxPackageInstallationPlugin</pip>
+               <pip name="box">wcf\system\package\plugin\BoxPackageInstallationPlugin</pip>
+               <pip name="menu">wcf\system\package\plugin\MenuPackageInstallationPlugin</pip>
+               <pip name="menuItem">wcf\system\package\plugin\MenuItemPackageInstallationPlugin</pip>
        </import>
 </data>
index cf05e50ffbf5044485020b020242d785efdc0e90..f9e286562480b344a7444dbe43ee965532a8d9a1 100644 (file)
@@ -1,6 +1,44 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/page.xsd">
        <import>
+               <!-- dynamic -->
+               <page identifier="com.woltlab.wcf.Dashboard">
+                       <controller>wcf\page\DashboardPage</controller>
+                       <name language="de"><![CDATA[Dashboard]]></name>
+                       <name language="en"><![CDATA[Dashboard]]></name>
+               </page>
+               
+               <page identifier="com.woltlab.wcf.MembersList">
+                       <controller>wcf\page\MembersListPage</controller>
+                       <name language="de"><![CDATA[Mitglieder]]></name>
+                       <name language="en"><![CDATA[Members]]></name>
+               </page>
+               <page identifier="com.woltlab.wcf.RecentActivityList">
+                       <controller>wcf\page\RecentActivityListPage</controller>
+                       <name language="de"><![CDATA[Letzte Aktivitäten]]></name>
+                       <name language="en"><![CDATA[Recent Activities]]></name>
+                       <parent>com.woltlab.wcf.MembersList</parent>
+               </page>
+               <page identifier="com.woltlab.wcf.UsersOnlineList">
+                       <controller>wcf\page\UsersOnlineListPage</controller>
+                       <name language="de"><![CDATA[Benutzer Online]]></name>
+                       <name language="en"><![CDATA[Users Online]]></name>
+                       <parent>com.woltlab.wcf.MembersList</parent>
+               </page>
+               <page identifier="com.woltlab.wcf.Team">
+                       <controller>wcf\page\TeamPage</controller>
+                       <name language="de"><![CDATA[Team]]></name>
+                       <name language="en"><![CDATA[Team]]></name>
+                       <parent>com.woltlab.wcf.MembersList</parent>
+               </page>
+               <page identifier="com.woltlab.wcf.UserSearch">
+                       <controller>wcf\page\UserSearchForm</controller>
+                       <name language="de"><![CDATA[Benutzer suchen]]></name>
+                       <name language="en"><![CDATA[Search Users]]></name>
+                       <parent>com.woltlab.wcf.MembersList</parent>
+               </page>
+               
+               <!-- static -->
                <page identifier="com.woltlab.wcf.CookiePolicy">
                        <name language="en">Cookie Policy</name>
                        <name language="de">Cookie-Richtlinie</name>
index 15b97c03c9bd11339f06950e08a74296630ddda3..fab563ca1badf56d18006f5ebdd8ad8aaeb170fd 100644 (file)
@@ -1,6 +1,11 @@
-{if $__wcf->getPageMenu()->getMenuItems('header')|count > 0}
+{@$__wcf->getBoxHandler()->getBoxes('mainMenu')[0]}
+{*if $__wcf->getPageMenu()->getMenuItems('header')|count > 0}
        <nav id="mainMenu" class="mainMenu jsMobileNavigation" data-button-label="{lang}wcf.page.mainMenu{/lang}">
                <ul>
+                       {foreach from=$__wcf->getBoxHandler()->getBoxes('mainMenu') item=box}
+                               {@$box}
+                       {/foreach}
+                       
                        {foreach from=$__wcf->getPageMenu()->getMenuItems('header') item=menuItem}
                                <li class="{if $__wcf->getPageMenu()->getActiveMenuItem() == $menuItem->menuItem}active {/if}{if $__wcf->getPageMenu()->getMenuItems($menuItem->menuItem)|count > 0}subMenuItems{/if}" data-menu-item="{$menuItem->menuItem}">
                                        <a href="{$menuItem->getProcessor()->getLink()}">{lang}{$menuItem->menuItem}{/lang}{if $menuItem->getProcessor()->getNotifications()} <span class="badge badgeUpdate">{#$menuItem->getProcessor()->getNotifications()}</span>{/if}</a>
@@ -13,4 +18,4 @@
                        {/foreach}
                </ul>
        </nav>
-{/if}
+{/if*}
index a30a1b1ff312dd682ffe5625daa13730fbfde623..467e0b7c04d9700204073961cd5af69139e2dbfb 100644 (file)
@@ -16,69 +16,6 @@ WCF.ACP = { };
  */
 WCF.ACP.Application = { };
 
-/**
- * Provides the ability to set an application as primary.
- * 
- * @param      integer         packageID
- */
-WCF.ACP.Application.SetAsPrimary = Class.extend({
-       /**
-        * application package id
-        * @var integer
-        */
-       _packageID: 0,
-       
-       /**
-        * Initializes the WCF.ACP.Application.SetAsPrimary class.
-        * 
-        * @param       integer         packageID
-        */
-       init: function(packageID) {
-               this._packageID = packageID;
-               
-               $('#setAsPrimary').click($.proxy(this._click, this));
-       },
-       
-       /**
-        * Shows a confirmation dialog to set current application as primary.
-        */
-       _click: function() {
-               WCF.System.Confirmation.show(WCF.Language.get('wcf.acp.application.setAsPrimary.confirmMessage'), $.proxy(function(action) {
-                       if (action === 'confirm') {
-                               this._setAsPrimary();
-                       }
-               }, this));
-       },
-       
-       /**
-        * Sets an application as primary.
-        */
-       _setAsPrimary: function() {
-               new WCF.Action.Proxy({
-                       autoSend: true,
-                       data: {
-                               actionName: 'setAsPrimary',
-                               className: 'wcf\\data\\application\\ApplicationAction',
-                               objectIDs: [ this._packageID ]
-                       },
-                       success: $.proxy(function(data, textStatus, jqXHR) {
-                               var $notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'));
-                               $notification.show();
-                               
-                               // remove button
-                               $('#setAsPrimary').parent().remove();
-                               
-                               // insert icon
-                               $headline = $('.boxHeadline > h1');
-                               $headline.html($headline.html() + ' ');
-                               $('<span class="icon icon16 fa-home jsTooltip" title="' + WCF.Language.get('wcf.acp.application.primaryApplication') + '" />').appendTo($headline);
-                               
-                               WCF.DOMNodeInsertedHandler.execute();
-                       }, this)
-               });
-       }
-});
-
 /**
  * Namespace for ACP cronjob management.
  */
index d8b9baa203c0a1a0c0756298e975a3af4c8d8056..bceaf485bf6ca4b0c29dff4a236afc21da2a9f22 100644 (file)
@@ -1,22 +1,7 @@
 {include file='header' pageTitle='wcf.acp.application.edit'}
 
-{if $application->packageID != 1 && !$application->isPrimary}
-       <script data-relocate="true">
-               //<![CDATA[
-               $(function() {
-                       WCF.Language.addObject({
-                               'wcf.acp.application.primaryApplication': '{lang}wcf.acp.application.primaryApplication{/lang}',
-                               'wcf.acp.application.setAsPrimary.confirmMessage': '{lang}wcf.acp.application.setAsPrimary.confirmMessage{/lang}'
-                       });
-                       
-                       new WCF.ACP.Application.SetAsPrimary({@$application->packageID});
-               });
-               //]]>
-       </script>
-{/if}
-
 <header class="boxHeadline">
-       <h1>{lang}wcf.acp.application.edit.title{/lang}{if $application->isPrimary} <span class="icon icon16 icon-ok-sign jsTooltip" title="{lang}wcf.acp.application.primaryApplication{/lang}"></span>{/if}</h1>
+       <h1>{lang}wcf.acp.application.edit.title{/lang}</h1>
 </header>
 
 {include file='formError'}
@@ -28,9 +13,6 @@
 <div class="contentNavigation">
        <nav>
                <ul>
-                       {if $application->packageID != 1 && !$application->isPrimary}
-                               <li><a id="setAsPrimary" class="button"><span class="icon icon16 fa-home"></span> <span>{lang}wcf.acp.application.setAsPrimary{/lang}</span></a></li>
-                       {/if}
                        <li><a href="{link controller='ApplicationManagement'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.link.application.management{/lang}</span></a></li>
                        
                        {event name='contentNavigationButtons'}
index 8d39dd832b52d3e3914e0c80ca3a55e76b567902..2f56fc76cf2321da1a458ef0284fa15b4983e5b4 100644 (file)
                                <tr>
                                        <td class="columnIcon"><a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}" class="jsTooltip" title="{lang}wcf.global.button.edit{/lang}"><span class="icon icon16 fa-pencil"></span></a></td>
                                        <td class="columnID columnPackageID">{#$application->packageID}</td>
-                                       <td class="columnText columnPackageName">
-                                               <a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}">{$application->getPackage()}</a>
-                                               {if $application->isPrimary}
-                                                       <aside class="statusDisplay">
-                                                               <ul class="statusIcons">
-                                                                       <li><span class="icon icon16 fa-home jsTooltip" title="{lang}wcf.acp.application.primaryApplication{/lang}"></span></li>
-                                                               </ul>
-                                                       </aside>
-                                               {/if}
-                                       </td>
+                                       <td class="columnText columnPackageName"><a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}">{$application->getPackage()}</a></td>
                                        <td class="columnText columnDomainName">{$application->domainName}</td>
                                        <td class="columnText columnDomainPath">{$application->domainPath}</td>
                                        <td class="columnText columnCookieDomain">{$application->cookieDomain}</td>
index 91ecc357adae9230de7eb87113fcaa1d786c0d05..25ab702c9a44bfcf7f48f631b102d5f4ec53d6cc 100644 (file)
@@ -37,7 +37,7 @@
 <div class="contentNavigation">
        <nav>
                <ul>
-                       <li><a href="{link controller='PageList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.cms.page.list{/lang}</span></a></li>
+                       <li><a href="{link controller='PageList'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.link.cms.page.list{/lang}</span></a></li>
                                
                        {event name='contentNavigationButtons'}
                </ul>
        </div>
        
        <div class="formSubmit">
-               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
-               <input type="hidden" name="isMultilingual" value="{@$isMultilingual}" />
+               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+               <input type="hidden" name="isMultilingual" value="{@$isMultilingual}">
                {@SECURITY_TOKEN_INPUT_TAG}
        </div>
 </form>
diff --git a/wcfsetup/install/files/acp/templates/pageLanding.tpl b/wcfsetup/install/files/acp/templates/pageLanding.tpl
new file mode 100644 (file)
index 0000000..7d5729a
--- /dev/null
@@ -0,0 +1,59 @@
+{include file='header' pageTitle='wcf.acp.page.landing'}
+
+<header class="boxHeadline">
+       <h1>{lang}wcf.acp.page.landing{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+       <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+       <nav>
+               <ul>
+                       <li><a href="{link controller='PageList'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.link.cms.page.list{/lang}</span></a></li>
+                       
+                       {event name='contentNavigationButtons'}
+               </ul>
+       </nav>
+</div>
+
+<form method="post" action="{link controller='PageLanding'}{/link}">
+       <div class="tabularBox marginTop">
+               <table class="table">
+                       <thead>
+                               <tr>
+                                       <th class="columnText">{lang}wcf.acp.page.landing.application{/lang}</th>
+                                       <th class="columnURL">{lang}wcf.acp.page.landing.pageURL{/lang}</th>
+                                       <th class="columnText">{lang}wcf.acp.page.landing.landingPage{/lang}</th>
+                               </tr>
+                       </thead>
+                       
+                       <tbody>
+                               {foreach from=$applications item=application}
+                                       <tr>
+                                               <td class="columnText">{$application->getPackage()}</td>
+                                               <td class="columnURL">{$application->getPageURL()}</td>
+                                               <td class="columnText">
+                                                       <select name="landingPages[{@$application->packageID}]">
+                                                               <option value="-1">{lang}wcf.acp.page.landing.applicationDefault{/lang}</option>
+                                                               {foreach from=$pageNodeList item=pageNode}
+                                                                       <option value="{@$pageNode->getPage()->pageID}"{if $pageNode->getPage()->pageID == $application->landingPageID} selected{/if}>{if $pageNode->getDepth() > 1}{@"&nbsp;&nbsp;&nbsp;&nbsp;"|str_repeat:($pageNode->getDepth() - 1)}{/if}{$pageNode->getPage()}</option>
+                                                               {/foreach}
+                                                       </select>
+                                               </td>
+                                       </tr>
+                               {/foreach}      
+                       </tbody>
+               </table>
+       </div>
+       
+       <div class="formSubmit">
+               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+               {@SECURITY_TOKEN_INPUT_TAG}
+       </div>
+</form>
+
+{include file='footer'}
index 8e4afc77069f0040316927ca65fb9f869f53809e..9dda58e620792d753a7e82502ead7e5fb0b5375d 100644 (file)
@@ -18,6 +18,7 @@
        
        <nav>
                <ul>
+                       <li><a href="{link controller='PageLanding'}{/link}" class="button"><span class="icon icon16 fa-home"></span> {lang}wcf.acp.page.landing{/lang}</a></li>
                        <li><a href="{link controller='PageAdd'}{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.page.add{/lang}</span></a></li>
                        <li><a href="{link controller='PageAdd'}isMultilingual=1{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.page.addMultilingual{/lang}</span></a></li>
                
@@ -80,6 +81,7 @@
                
                <nav>
                        <ul>
+                               <li><a href="{link controller='PageLanding'}{/link}" class="button"><span class="icon icon16 fa-home"></span> {lang}wcf.acp.page.landing{/lang}</a></li>
                                <li><a href="{link controller='PageAdd'}{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.page.add{/lang}</span></a></li>
                                <li><a href="{link controller='PageAdd'}isMultilingual=1{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.page.addMultilingual{/lang}</span></a></li>
                
index d8f9319ecb36a19108cc0130a2cf4eb3101befb5..bae20a48f128579a1d564717ad81a36809b8e4ba 100755 (executable)
@@ -64,11 +64,6 @@ class LoginForm extends AbstractCaptchaForm {
                if (WCF::getUser()->userID) {
                        throw new PermissionDeniedException();
                }
-               else if (PACKAGE_ID == 1 && PACKAGE_ID != ApplicationHandler::getInstance()->getPrimaryApplication()->packageID) {
-                       $application = ApplicationHandler::getInstance()->getPrimaryApplication();
-                       HeaderUtil::redirect(RouteHandler::getProtocol() . $application->domainName . $application->domainPath . 'acp/?Login/');
-                       exit;
-               }
                
                parent::__run();
        }
index 58e666379dd7b59860f2f3d555835598e1f5a92e..7819b12ff0d8801cdd602acf5aef89cdd0cf2d5f 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\acp\form;
+use wcf\data\application\Application;
 use wcf\data\application\ApplicationList;
 use wcf\data\page\Page;
 use wcf\data\page\PageAction;
@@ -71,38 +72,38 @@ class PageAddForm extends AbstractForm {
        public $packageID = 1;
        
        /**
-        * list of available applications (standalone packages)
+        * list of available applications
         * @var Application[]
         */
        public $availableApplications = [];
        
        /**
         * page custom URL
-        * @var array<string>
+        * @var string[]
         */
        public $customURL = [];
        
        /**
         * page titles
-        * @var array<string>
+        * @var string[]
         */
        public $title = [];
        
        /**
         * page contents
-        * @var array<string>
+        * @var string[]
         */
        public $content = [];
        
        /**
         * page meta descriptions
-        * @var array<string>
+        * @var string[]
         */
        public $metaDescription = [];
        
        /**
         * page meta keywords
-        * @var array<string>
+        * @var string[]
         */
        public $metaKeywords = [];
        
@@ -262,8 +263,6 @@ class PageAddForm extends AbstractForm {
        public function assignVariables() {
                parent::assignVariables();
                
-               $pageNodeList = new PageNodeTree();
-               
                WCF::getTPL()->assign([
                        'action' => 'add',
                        'parentPageID' => $this->parentPageID,
@@ -279,7 +278,7 @@ class PageAddForm extends AbstractForm {
                        'metaKeywords' => $this->metaKeywords,
                        'availableApplications' => $this->availableApplications,
                        'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
-                       'pageNodeList' => $pageNodeList->getNodeList()
+                       'pageNodeList' => (new PageNodeTree())->getNodeList()
                ]);
        }
 }
diff --git a/wcfsetup/install/files/lib/acp/form/PageLandingForm.class.php b/wcfsetup/install/files/lib/acp/form/PageLandingForm.class.php
new file mode 100644 (file)
index 0000000..79e3ce3
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+namespace wcf\acp\form;
+use wcf\data\application\Application;
+use wcf\data\application\ApplicationAction;
+use wcf\data\application\ApplicationList;
+use wcf\data\page\Page;
+use wcf\data\page\PageList;
+use wcf\data\page\PageNodeTree;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+
+/**
+ * Shows the page add form.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage acp.form
+ * @category   Community Framework
+ */
+class PageLandingForm extends AbstractForm {
+       /**
+        * @inheritDoc
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.cms.page.landing';
+       
+       /**
+        * list of available applications
+        * @var Application[]
+        */
+       public $applications;
+       
+       /**
+        * landing page id per application package id
+        * @var integer[]
+        */
+       public $landingPages = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public $neededPermissions = ['admin.content.cms.canManagePage'];
+       
+       /**
+        * list of available pages
+        * @var Page[]
+        */
+       public $pages = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public function readParameters() {
+               parent::readParameters();
+       
+               // get available applications and pages
+               $applicationList = new ApplicationList();
+               $applicationList->readObjects();
+               $this->applications = $applicationList->getObjects();
+               
+               $pageList = new PageList();
+               $pageList->readObjects();
+               foreach ($pageList as $page) {
+                       if (!isset($this->pages[$page->packageID])) $this->pages[$page->packageID] = [];
+                       
+                       $this->pages[$page->packageID][$page->pageID] = $page;
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readFormParameters() {
+               parent::readFormParameters();
+               
+               if (isset($_POST['landingPages']) && is_array($_POST['landingPages'])) $this->landingPages = ArrayUtil::toIntegerArray($_POST['landingPages']);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function validate() {
+               parent::validate();
+               
+               foreach ($this->applications as $packageID => $application) {
+                       if (empty($this->landingPages[$packageID])) {
+                               throw new UserInputException('landingPage');
+                       }
+                       
+                       // handle application default
+                       if ($this->landingPages[$packageID] === -1) {
+                               $this->landingPages[$packageID] = null;
+                               
+                               continue;
+                       }
+                       
+                       $page = new Page($this->landingPages[$packageID]);
+                       if (!$page->pageID) {
+                               throw new UserInputException('landingPage', 'notValid');
+                       }
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function save() {
+               parent::save();
+               
+               $this->objectAction = new ApplicationAction($this->applications, 'setLandingPage', ['landingPages' => $this->landingPages]);
+               $this->objectAction->executeAction();
+               
+               // call saved event
+               $this->saved();
+               
+               // show success
+               WCF::getTPL()->assign('success', true);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign([
+                       'applications' => $this->applications,
+                       'pageNodeList' => (new PageNodeTree())->getNodeList()
+               ]);
+       }
+}
index 7c6b540e911e8d46b2e43d2ca68675ffe8557cba..a3635cd9faba90c4e737aa4c70ec922b02b03894 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\data\application;
 use wcf\data\package\Package;
+use wcf\data\package\PackageCache;
 use wcf\data\package\PackageList;
 use wcf\data\DatabaseObject;
 use wcf\system\application\ApplicationHandler;
@@ -19,6 +20,12 @@ use wcf\util\FileUtil;
  * @category   Community Framework
  */
 class Application extends DatabaseObject {
+       /**
+        * related package object
+        * @var Package
+        */
+       protected $package;
+       
        /**
         * absolute page URL
         * @var string
@@ -55,6 +62,19 @@ class Application extends DatabaseObject {
                return ApplicationHandler::getInstance()->getAbbreviation($this->packageID);
        }
        
+       /**
+        * Returns related package object.
+        * 
+        * @return      Package         related package object
+        */
+       public function getPackage() {
+               if ($this->package === null) {
+                       $this->package = PackageCache::getInstance()->getPackage($this->packageID);
+               }
+               
+               return $this->package;
+       }
+       
        /**
         * Returns absolute page URL.
         * 
index 7a98fd03980ccf05640e934b9df00ff70685bf0a..ab20557631e9e16047e94e87cb653ab911364441 100644 (file)
@@ -22,20 +22,15 @@ use wcf\util\StringUtil;
  */
 class ApplicationAction extends AbstractDatabaseObjectAction {
        /**
-        * @see \wcf\data\AbstractDatabaseObjectAction::$className
+        * @inheritDoc
         */
-       protected $className = 'wcf\data\application\ApplicationEditor';
+       protected $className = ApplicationEditor::class;
        
        /**
         * application editor object
-        * @var \wcf\data\application\ApplicationEditor
+        * @var ApplicationEditor
         */
-       public $applicationEditor = null;
-       
-       /**
-        * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
-        */
-       protected $requireACP = array('setAsPrimary');
+       public $applicationEditor;
        
        /**
         * Assigns a list of applications to a group and computes cookie domain and path.
@@ -52,7 +47,7 @@ class ApplicationAction extends AbstractDatabaseObjectAction {
                $statement = WCF::getDB()->prepareStatement($sql);
                
                // calculate cookie path
-               $domains = array();
+               $domains = [];
                $regex = new Regex(':[0-9]+');
                foreach ($this->objects as $application) {
                        $domainName = $application->domainName;
@@ -61,7 +56,7 @@ class ApplicationAction extends AbstractDatabaseObjectAction {
                        }
                        
                        if (!isset($domains[$domainName])) {
-                               $domains[$domainName] = array();
+                               $domains[$domainName] = [];
                        }
                        
                        $domains[$domainName][$application->packageID] = explode('/', FileUtil::removeLeadingSlash(FileUtil::removeTrailingSlash($application->domainPath)));
@@ -94,11 +89,11 @@ class ApplicationAction extends AbstractDatabaseObjectAction {
                        $path = FileUtil::addLeadingSlash(FileUtil::addTrailingSlash(implode('/', $path)));
                        
                        foreach (array_keys($data) as $packageID) {
-                               $statement->execute(array(
+                               $statement->execute([
                                        $domainName,
                                        $path,
                                        $packageID
-                               ));
+                               ]);
                        }
                }
                WCF::getDB()->commitTransaction();
@@ -111,24 +106,14 @@ class ApplicationAction extends AbstractDatabaseObjectAction {
        }
        
        /**
-        * Validates parameters to set an application as primary.
+        * Sets landing pages for applications.
         */
-       public function validateSetAsPrimary() {
-               WCF::getSession()->checkPermissions(array('admin.system.canManageApplication'));
-               
-               $this->applicationEditor = $this->getSingleObject();
-               if (!$this->applicationEditor->packageID || $this->applicationEditor->packageID == 1) {
-                       throw new UserInputException('objectIDs');
-               }
-               else if ($this->applicationEditor->isPrimary) {
-                       throw new PermissionDeniedException();
+       public function setLandingPage() {
+               /** @var ApplicationEditor $applicationEditor */
+               foreach ($this->objects as $applicationEditor) {
+                       $applicationEditor->update([
+                               'landingPageID' => $this->parameters['landingPages'][$applicationEditor->packageID]
+                       ]);
                }
        }
-       
-       /**
-        * Sets an application as primary.
-        */
-       public function setAsPrimary() {
-               $this->applicationEditor->setAsPrimary();
-       }
 }
index 8647caaac5ebc6eb087f9ba5bf851f470124b775..5247dcb8a1f05ac86fbad25415b90ac73e749bf7 100644 (file)
@@ -3,7 +3,6 @@ namespace wcf\data\application;
 use wcf\data\DatabaseObjectEditor;
 use wcf\data\IEditableCachedObject;
 use wcf\system\cache\builder\ApplicationCacheBuilder;
-use wcf\system\WCF;
 
 /**
  * Provides functions to edit applications.
@@ -17,72 +16,9 @@ use wcf\system\WCF;
  */
 class ApplicationEditor extends DatabaseObjectEditor implements IEditableCachedObject {
        /**
-        * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+        * @inheritDoc
         */
-       protected static $baseClass = 'wcf\data\application\Application';
-       
-       /**
-        * Sets current application as primary application.
-        */
-       public function setAsPrimary() {
-               $sql = "UPDATE  wcf".WCF_N."_application
-                       SET     isPrimary = ?";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute(array(0));
-               
-               $sql = "UPDATE  wcf".WCF_N."_application
-                       SET     isPrimary = ?
-                       WHERE   packageID = ?";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute(array(
-                       1,
-                       $this->packageID
-               ));
-               
-               self::resetCache();
-       }
-       
-       /**
-        * Sets the first installed application as primary unless an other application already is primary.
-        */
-       public static function setup() {
-               $sql = "SELECT  COUNT(*) AS count
-                       FROM    wcf".WCF_N."_application
-                       WHERE   isPrimary = ?";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute(array(1));
-               $row = $statement->fetchArray();
-               
-               if ($row['count']) {
-                       // there is already a primary application
-                       return;
-               }
-               else {
-                       // set first installed application as primary
-                       $sql = "SELECT          packageID
-                               FROM            wcf".WCF_N."_package
-                               WHERE           packageID <> ?
-                                               AND isApplication = ?
-                               ORDER BY        installDate ASC";
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array(
-                               1,
-                               1
-                       ));
-                       $row = $statement->fetchArray();
-                       
-                       if ($row !== false) {
-                               $sql = "UPDATE  wcf".WCF_N."_application
-                                       SET     isPrimary = ?
-                                       WHERE   packageID = ?";
-                               $statement = WCF::getDB()->prepareStatement($sql);
-                               $statement->execute(array(
-                                       1,
-                                       $row['packageID']
-                               ));
-                       }
-               }
-       }
+       protected static $baseClass = Application::class;
        
        /**
         * @see \wcf\data\IEditableCachedObject::resetCache()
index f1aed8a92cfb9c92bc040175354daa78fd13a095..6eea110546e7bcbeb058893667d785b05b5c3c1a 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\data\box;
 use wcf\data\DatabaseObject;
+use wcf\data\menu\Menu;
+use wcf\data\menu\MenuCache;
 use wcf\system\WCF;
 use wcf\util\StringUtil;
 
@@ -37,6 +39,12 @@ class Box extends DatabaseObject {
         */
        public static $availablePositions = ['hero', 'headerBoxes', 'top', 'sidebarLeft', 'contentTop', 'sidebarRight', 'contentBottom', 'bottom', 'footerBoxes', 'footer'];
        
+       /**
+        * menu object
+        * @var Menu
+        */
+       protected $menu;
+       
        /**
         * Returns true if the active user can delete this box.
         * 
@@ -109,25 +117,22 @@ class Box extends DatabaseObject {
                else if ($this->boxType == 'menu') {
                        return $this->getMenu()->getContent();
                }
+               
+               $boxContent = $this->getBoxContent();
+               $content = '';
+               if ($this->isMultilingual) {
+                       if (isset($boxContent[WCF::getLanguage()->languageID])) $content = $boxContent[WCF::getLanguage()->languageID]['content'];
+               }
                else {
-                       $boxContent = $this->getBoxContent();
-                       $content = '';
-                       if ($this->isMultilingual) {
-                               if (isset($boxContent[WCF::getLanguage()->languageID])) $content = $boxContent[WCF::getLanguage()->languageID]['content'];
-                       }
-                       else {
-                               if (isset($boxContent[0])) $content = $boxContent[0]['content'];
-                       }
-                       
-                       if ($this->boxType == 'text') {
-                               // @todo parse text
-                               $content = StringUtil::encodeHTML($content);
-                       }
-                       
-                       return $content;
+                       if (isset($boxContent[0])) $content = $boxContent[0]['content'];
                }
                
-               return '';
+               if ($this->boxType == 'text') {
+                       // @todo parse text
+                       $content = StringUtil::encodeHTML($content);
+               }
+               
+               return $content;
        }
        
        /**
@@ -166,7 +171,7 @@ class Box extends DatabaseObject {
                                if (isset($boxContent[0])) $content = $boxContent[0]['content'];
                        }
                        
-                       return ($content != '');
+                       return !empty($content);
                }
        }
        
@@ -175,7 +180,11 @@ class Box extends DatabaseObject {
        }
        
        public function getMenu() {
-               // @todo
+               if ($this->menu === null) {
+                       $this->menu = MenuCache::getInstance()->getMenuByID($this->menuID);
+               }
+               
+               return $this->menu;
        }
        
        /**
@@ -189,10 +198,8 @@ class Box extends DatabaseObject {
                        FROM    wcf".WCF_N."_box
                        WHERE   name = ?";
                $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute(array($name));
-               $row = $statement->fetchArray();
-               if ($row !== false) return new Box(null, $row);
-       
-               return null;
+               $statement->execute([$name]);
+               
+               return $statement->fetchObject(Box::class);
        }
 }
diff --git a/wcfsetup/install/files/lib/data/menu/MenuCache.class.php b/wcfsetup/install/files/lib/data/menu/MenuCache.class.php
new file mode 100644 (file)
index 0000000..e9ad238
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+namespace wcf\data\menu;
+use wcf\data\menu\item\MenuItemNodeTree;
+use wcf\system\cache\builder\MenuCacheBuilder;
+use wcf\system\SingletonFactory;
+
+/**
+ * Manages the menu cache.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.menu
+ * @category   Community Framework
+ */
+class MenuCache extends SingletonFactory {
+       /**
+        * @var Menu[]
+        */
+       protected $cachedMenus;
+       
+       /**
+        * @var MenuItemNodeTree[]
+        */
+       protected $cachedMenuItems;
+       
+       /**
+        * @inheritDoc
+        */
+       protected function init() {
+               $this->cachedMenus = MenuCacheBuilder::getInstance()->getData([], 'menus');
+               $this->cachedMenuItems = MenuCacheBuilder::getInstance()->getData([], 'menuItems');
+       }
+       
+       public function getMenuByID($menuID) {
+               if (isset($this->cachedMenus[$menuID])) {
+                       return $this->cachedMenus[$menuID];
+               }
+               
+               return null;
+       }
+       
+       public function getMenuItemsByMenuID($menuID) {
+               if (isset($this->cachedMenuItems[$menuID])) {
+                       return $this->cachedMenuItems[$menuID];
+               }
+               
+               return null;
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/menu/ViewableMenu.class.php b/wcfsetup/install/files/lib/data/menu/ViewableMenu.class.php
new file mode 100644 (file)
index 0000000..bcf65ff
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+namespace wcf\data\menu;
+use wcf\data\DatabaseObjectDecorator;
+use wcf\data\menu\item\MenuItemNodeTree;
+use wcf\system\WCF;
+
+class ViewableMenu extends DatabaseObjectDecorator {
+       /**
+        * @var MenuItemNodeTree
+        */
+       protected $menuItems;
+       
+       public function hasContent() {
+               // TODO
+               return true;
+       }
+       
+       public function getContent() {
+               return WCF::getTPL()->fetch('__menu', 'wcf', ['menuItems' => $this->menuItems->getNodeList()]);
+       }
+       
+       protected function getMenuItems() {
+               if ($this->menuItems === null) {
+                       $this->menuItems = MenuCache::getInstance()->getMenuItemsByMenuID($this->object->menuID);
+               }
+               
+               return $this->menuItems;
+       }
+}
index ab4aee74586e4028a5fa0418d7af7f3fa89239f5..dca05354f81f06759041689bfae4add6214ff2d1 100644 (file)
@@ -16,25 +16,25 @@ class MenuItemNodeTree {
         * menu id
         * @var integer
         */
-       public $menuID = null;
+       public $menuID;
        
        /**
         * list of menu items
         * @var MenuItem[]
         */
-       public $menuItems = array();
+       public $menuItems = [];
        
        /**
         * menu item structure
         * @var mixed[]
         */
-       public $menuItemStructure = array();
+       public $menuItemStructure = [];
        
        /**
         * root node
         * @var MenuItemNode
         */
-       public $node = null;
+       public $node;
        
        /**
         * Creates a new MenuItemNodeTree object.
@@ -67,10 +67,9 @@ class MenuItemNodeTree {
        /**
         * Generates the node tree recursively
         * 
-        * @param       integer                 $parentID
-        * @param       MenuItemNode            $parentNode
-        * @param       int[]                   $filter
-        * @return      MenuItemNode[]
+        * @param       integer                 $parentID       parent menu item id
+        * @param       MenuItemNode            $parentNode     parent menu item object
+        * @return      MenuItemNode[]          nested menu item tree
         */
        protected function generateNodeTree($parentID = null, MenuItemNode $parentNode = null) {
                $nodes = array();
index 4bdab514fa4c53a980b7a14dec54b01d13049220..f645d7732a8c9bbfe83a39d446121aefe7a06d95 100644 (file)
@@ -110,6 +110,15 @@ class Page extends DatabaseObject {
                // @todo
        }
        
+       /**
+        * Returns the page's internal name.
+        * 
+        * @return string
+        */
+       public function __toString() {
+               return $this->name;
+       }
+       
        /**
         * Returns the page with the given identifier.
         * 
index ff22acb50c934231384b744bacc020c35c51ce08..c9f0334091802d9139333a1c0905726d74c18c9b 100644 (file)
@@ -222,6 +222,7 @@ abstract class AbstractPage implements IPage, ITrackablePage {
                        }
                        
                        if ($redirect) {
+                               die("should redirect");
                                $redirectURL = $this->canonicalURL;
                                if (!empty($requestURL['query'])) {
                                        $queryString = $requestURL['query'];
index 1bc4788a237924bea7a9822970580320633212e2..eaff6c478ce45ac3d658dee42230227a1ad1564c 100644 (file)
@@ -424,23 +424,37 @@ class WCF {
                $loadedApplications = array();
                
                // register WCF as application
-               self::$applications['wcf'] = ApplicationHandler::getInstance()->getWCF();
+               self::$applications['wcf'] = ApplicationHandler::getInstance()->getApplicationByID(1);
                
+               // TODO: what exactly should the base href represent and how should it be calculated, also because
+               // defining it here eventually breaks the ACP due to tpl initialization occurs first
+               if (!class_exists(WCFACP::class, false)) {
+                       $this->getTPL()->assign('baseHref', self::$applications['wcf']->getPageURL());
+               }
+               
+               // TODO: this is required for the uninstallation of applications, find a different solution!
                if (PACKAGE_ID == 1) {
-                       return;
+                       //return;
                }
                
                // start main application
                $application = ApplicationHandler::getInstance()->getActiveApplication();
-               $loadedApplications[] = $this->loadApplication($application);
-               
-               // register primary application
-               $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID);
-               self::$applications[$abbreviation] = $application;
+               if ($application->packageID != 1) {
+                       $loadedApplications[] = $this->loadApplication($application);
+                       
+                       // register primary application
+                       $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID);
+                       self::$applications[$abbreviation] = $application;
+               }
                
                // start dependent applications
                $applications = ApplicationHandler::getInstance()->getDependentApplications();
                foreach ($applications as $application) {
+                       if ($application->packageID == 1) {
+                               // ignore WCF
+                               continue;
+                       }
+                       
                        $loadedApplications[] = $this->loadApplication($application, true);
                }
                
@@ -844,9 +858,9 @@ class WCF {
         */
        public function getFavicon() {
                $activeApplication = ApplicationHandler::getInstance()->getActiveApplication();
-               $primaryApplication = ApplicationHandler::getInstance()->getPrimaryApplication();
+               $wcf = ApplicationHandler::getInstance()->getWCF();
                
-               if ($activeApplication->domainName != $primaryApplication->domainName) {
+               if ($activeApplication->domainName !== $wcf->domainName) {
                        if (file_exists(WCF_DIR.'images/favicon.ico')) {
                                $favicon = file_get_contents(WCF_DIR.'images/favicon.ico');
                                
index 137a0186f46e2edb4c56e0c42326de54c6a4e944..2b3336e3fed9feca300e689ef37e38342af2fb79 100644 (file)
@@ -20,15 +20,15 @@ use wcf\system\SingletonFactory;
 class ApplicationHandler extends SingletonFactory {
        /**
         * application cache
-        * @var array<array>
+        * @var Application[]
         */
-       protected $cache = null;
+       protected $cache;
        
        /**
         * list of page URLs
-        * @var array<string>
+        * @var string[]
         */
-       protected $pageURLs = array();
+       protected $pageURLs = [];
        
        /**
         * Initializes cache.
@@ -37,21 +37,6 @@ class ApplicationHandler extends SingletonFactory {
                $this->cache = ApplicationCacheBuilder::getInstance()->getData();
        }
        
-       /**
-        * Returns the primary application.
-        * 
-        * @return      Application
-        */
-       public function getPrimaryApplication() {
-               $packageID = ($this->cache['primary']) ?: PACKAGE_ID;
-               
-               if (isset($this->cache['application'][$packageID])) {
-                       return $this->cache['application'][$packageID];
-               }
-               
-               return $this->cache['wcf'];
-       }
-       
        /**
         * Returns an application based upon it's abbreviation. Will return the
         * primary application if $abbreviation equals to 'wcf'
@@ -60,10 +45,6 @@ class ApplicationHandler extends SingletonFactory {
         * @return      Application
         */
        public function getApplication($abbreviation) {
-               if ($abbreviation == 'wcf') {
-                       return $this->getPrimaryApplication();
-               }
-               
                if (isset($this->cache['abbreviation'][$abbreviation])) {
                        $packageID = $this->cache['abbreviation'][$abbreviation];
                        
@@ -94,9 +75,10 @@ class ApplicationHandler extends SingletonFactory {
         * e.g. cross-domain files requestable through the webserver.
         * 
         * @return      Application
+        * @deprecated  2.2 please use `getApplication()` instead
         */
        public function getWCF() {
-               return $this->cache['wcf'];
+               return $this->getApplicationByID(1);
        }
        
        /**
index fd4a852c23af4e511a431a60813d4f38d76a48a4..d3d4f1880350d5f786379bd3b57a56446ae61413 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\system\box;
+use wcf\data\box\Box;
 use wcf\data\box\BoxList;
 use wcf\system\SingletonFactory;
 
@@ -16,7 +17,7 @@ use wcf\system\SingletonFactory;
 class BoxHandler extends SingletonFactory {
        /**
         * boxes grouped by position
-        * @var array
+        * @var Box[][]
         */
        protected $boxes = [];
        
@@ -38,7 +39,7 @@ class BoxHandler extends SingletonFactory {
         * Returns boxes for the given position.
         * 
         * @param       string          $position
-        * @return      \wcf\data\box\Box[]
+        * @return      Box[]
         */
        public function getBoxes($position) {
                if (isset($this->boxes[$position])) {
index 09da28216344185c1751e9571bc1fa77da9e10b1..f868223aea366de33d58cd55af48c557e1f10563 100644 (file)
@@ -1,8 +1,6 @@
 <?php
 namespace wcf\system\breadcrumb;
-use wcf\system\menu\page\PageMenu;
 use wcf\system\SingletonFactory;
-use wcf\system\WCF;
 
 /**
  * Manages breadcrumbs.
@@ -17,9 +15,9 @@ use wcf\system\WCF;
 class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
        /**
         * list of breadcrumbs
-        * @var array<\wcf\system\breadcrumb\Breadcrumb>
+        * @var Breadcrumb[]
         */
-       protected $items = array();
+       protected $items = [];
        
        /**
         * Current iterator-index
@@ -27,17 +25,18 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
        protected $index = 0;
        
        /**
-        * @see \wcf\system\SingletonFactory::init()
+        * @inheritDoc
         */
        protected function init() {
                // add main breadcrumbs entry
-               $this->add(new Breadcrumb(WCF::getLanguage()->get(PAGE_TITLE), PageMenu::getInstance()->getLandingPage()->getProcessor()->getLink()));
+               // TODO: there is no longer a global landing page, what should be displayed instead?
+               //$this->add(new Breadcrumb(WCF::getLanguage()->get(PAGE_TITLE), PageMenu::getInstance()->getLandingPage()->getProcessor()->getLink()));
        }
        
        /**
         * Adds a breadcrumb (insertion order is crucial!).
         * 
-        * @param       \wcf\system\breadcrumb\Breadcrumb       $item
+        * @param       Breadcrumb      $item
         */
        public function add(Breadcrumb $item) {
                $this->items[] = $item;
@@ -46,7 +45,7 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
        /**
         * Returns the list of breadcrumbs.
         * 
-        * @return      array<\wcf\system\breadcrumb\Breadcrumb>
+        * @return      Breadcrumb[]
         */
        public function get() {
                return $this->items;
@@ -55,8 +54,8 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
        /**
         * Replaces a breadcrumb, returns true if replacement was successful.
         * 
-        * @param       \wcf\system\breadcrumb\Breadcrumb       $item
-        * @param       integer                                 $index
+        * @param       Breadcrumb      $item
+        * @param       integer         $index
         * @return      boolean
         */
        public function replace(Breadcrumb $item, $index) {
@@ -86,42 +85,42 @@ class Breadcrumbs extends SingletonFactory implements \Countable, \Iterator {
        }
        
        /**
-        * @see \Countable::count()
+        * @inheritDoc
         */
        public function count() {
                return count($this->items);
        }
        
        /**
-        * @see \Iterator::current()
+        * @inheritDoc
         */
        public function current() {
                return $this->items[$this->index];
        }
        
        /**
-        * @see \Iterator::key()
+        * @inheritDoc
         */
        public function key() {
                return $this->index;
        }
        
        /**
-        * @see \Iterator::valid()
+        * @inheritDoc
         */
        public function valid() {
                return isset($this->items[$this->index]);
        }
        
        /**
-        * @see \Iterator::rewind()
+        * @inheritDoc
         */
        public function rewind() {
                $this->index = 0;
        }
        
        /**
-        * @see \Iterator::next()
+        * @inheritDoc
         */
        public function next() {
                $this->index++;
index bca202b9287297c53f38bfc1ee6394159ca11223..33397564c2e6ed835ded3110bf956d3b737ce4e4 100644 (file)
@@ -16,44 +16,30 @@ use wcf\data\package\PackageList;
  */
 class ApplicationCacheBuilder extends AbstractCacheBuilder {
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+        * @inheritDoc
         */
        public function rebuild(array $parameters) {
-               $data = array(
-                       'abbreviation' => array(),
-                       'application' => array(),
-                       'primary' => 0,
-                       'wcf' => null
-               );
+               $data = [
+                       'abbreviation' => [],
+                       'application' => []
+               ];
                
                // fetch applications
                $applicationList = new ApplicationList();
                $applicationList->readObjects();
-               $applications = $applicationList->getObjects();
                
-               foreach ($applications as $application) {
+               foreach ($applicationList as $application) {
                        $data['application'][$application->packageID] = $application;
-                       
-                       // save primary application's package id
-                       if ($application->isPrimary) {
-                               $data['primary'] = $application->packageID;
-                       }
                }
                
                // fetch abbreviations
                $packageList = new PackageList();
-               $packageList->getConditionBuilder()->add('package.isApplication = ?', array(1));
+               $packageList->getConditionBuilder()->add('package.isApplication = ?', [1]);
                $packageList->readObjects();
-               foreach ($packageList->getObjects() as $package) {
+               foreach ($packageList as $package) {
                        $data['abbreviation'][Package::getAbbreviation($package->package)] = $package->packageID;
                }
                
-               // assign wcf pseudo-application
-               if (PACKAGE_ID) {
-                       $data['wcf'] = $data['application'][1];
-                       unset($data['application'][1]);
-               }
-               
                return $data;
        }
 }
diff --git a/wcfsetup/install/files/lib/system/cache/builder/MenuCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/MenuCacheBuilder.class.php
new file mode 100644 (file)
index 0000000..2af4e50
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\menu\item\MenuItemNodeTree;
+use wcf\data\menu\MenuList;
+
+/**
+ * Caches menus and menu item node trees.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.cache.builder
+ * @category   Community Framework
+ */
+class MenuCacheBuilder extends AbstractCacheBuilder {
+       /**
+        * @inheritDoc
+        */
+       protected function rebuild(array $parameters) {
+               $data = [
+                       'menus' => [],
+                       'menuItems' => []
+               ];
+               
+               $menuList = new MenuList();
+               $menuList->readObjects();
+               foreach ($menuList as $menu) {
+                       $data['menus'][$menu->menuID] = $menu;
+                       $data['menuItems'][$menu->menuID] = new MenuItemNodeTree($menu->menuID);
+               }
+               
+               return $data;
+       }
+}
index 6dd4523bfbf95ab27a4ca6215101f34e6103ae40..3c63e6db6273993da76e7dccc5a5d14fd406fde1 100644 (file)
@@ -1,7 +1,9 @@
 <?php
 namespace wcf\system\cache\builder;
 use wcf\data\application\Application;
+use wcf\data\page\Page;
 use wcf\system\application\ApplicationHandler;
+use wcf\system\request\ControllerMap;
 use wcf\system\WCF;
 use wcf\util\FileUtil;
 
@@ -22,7 +24,8 @@ class RoutingCacheBuilder extends AbstractCacheBuilder {
        protected function rebuild(array $parameters) {
                return [
                        'ciControllers' => $this->getCaseInsensitiveControllers(),
-                       'customUrls' => $this->getCustomUrls()
+                       'customUrls' => $this->getCustomUrls(),
+                       'landingPages' => $this->getLandingPages()
                ];
        }
        
@@ -151,4 +154,45 @@ class RoutingCacheBuilder extends AbstractCacheBuilder {
                
                return $data;
        }
+       
+       /**
+        * Returns the list of landing pages per application.
+        * 
+        * @return      string[]
+        */
+       protected function getLandingPages() {
+               $data = [];
+               
+               if (!PACKAGE_ID) {
+                       return $data;
+               }
+               
+               foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
+                       if ($application->landingPageID) {
+                               $page = new Page($application->landingPageID);
+                               if ($page->controller) {
+                                       $controller = $page->controller;
+                               }
+                               else {
+                                       $controller = '__WCF_CMS__' . $page->pageID;
+                                       $controller = [$controller, $controller];
+                               }
+                       }
+                       else if ($application->packageID == 1) {
+                               // WCF has no default controller
+                               $controller = ['', ''];
+                       }
+                       else {
+                               $controller = preg_replace('~^.*?\\\([^\\\]+)(?:Action|Form|Page)$~', '\\1', WCF::getApplicationObject($application)->getPrimaryController());
+                               $controller = [
+                                       $controller,
+                                       ControllerMap::transformController($controller)
+                               ];
+                       }
+                       
+                       $data[ApplicationHandler::getInstance()->getAbbreviation($application->packageID)] = $controller;
+               }
+               
+               return $data;
+       }
 }
index 5f20d75d367c83ee6dcabf8a383335e0fce5bc0e..f1b1ebd93381cb92a7fe1f8d280eb7ec398906c3 100644 (file)
@@ -19,13 +19,7 @@ use wcf\system\menu\TreeMenu;
  */
 class PageMenu extends TreeMenu {
        /**
-        * landing page menu item
-        * @var \wcf\data\page\menu\item\PageMenuItem
-        */
-       protected $landingPage = null;
-       
-       /**
-        * @see \wcf\system\SingletonFactory::init()
+        * @inheritDoc
         * @throws      SystemException
         */
        protected function init() {
@@ -42,34 +36,10 @@ 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;
-                               }
-                       }
-               }
-               
-               if ($this->landingPage === null) {
-                       throw new SystemException("Missing landing page");
-               }
-               
-               $this->setActiveMenuItem($this->landingPage->menuItem);
        }
        
        /**
-        * Returns landing page menu item.
-        * 
-        * @return      \wcf\data\page\menu\item\PageMenuItem
-        */
-       public function getLandingPage() {
-               return $this->landingPage;
-       }
-       
-       /**
-        * @see \wcf\system\menu\TreeMenu::loadCache()
+        * @inheritDoc
         */
        protected function loadCache() {
                parent::loadCache();
@@ -79,14 +49,9 @@ class PageMenu extends TreeMenu {
        }
        
        /**
-        * @see \wcf\system\menu\TreeMenu::checkMenuItem()
+        * @inheritDoc
         */
        protected function checkMenuItem(ITreeMenuItem $item) {
-               // landing page must always be accessible
-               if ($item->isLandingPage) {
-                       return true;
-               }
-               
                if (!parent::checkMenuItem($item)) return false;
                
                if ($item instanceof ProcessibleDatabaseObject && $item->getProcessor() instanceof IPageMenuItemProvider) {
@@ -97,7 +62,7 @@ class PageMenu extends TreeMenu {
        }
        
        /**
-        * @see \wcf\system\menu\TreeMenu::setActiveMenuItem()
+        * @inheritDoc
         */
        public function setActiveMenuItem($menuItem) {
                if (isset($this->menuItemList[$menuItem]) && $this->menuItemList[$menuItem]->menuPosition == 'footer') {
index e9fe7b17dcbf5e5f47f7b3ac8f7103663a9d5dd0..e77a3a05c5be3f0bb3543f4f9548a9db5ae87b78 100644 (file)
@@ -11,7 +11,6 @@ use wcf\data\package\installation\queue\PackageInstallationQueueEditor;
 use wcf\data\package\Package;
 use wcf\data\package\PackageEditor;
 use wcf\system\application\ApplicationHandler;
-use wcf\data\object\type\ObjectTypeCache;
 use wcf\system\cache\builder\TemplateListenerCodeCacheBuilder;
 use wcf\system\cache\CacheHandler;
 use wcf\system\database\statement\PreparedStatement;
@@ -56,27 +55,27 @@ class PackageInstallationDispatcher {
        
        /**
         * instance of PackageArchive
-        * @var \wcf\system\package\PackageArchive
+        * @var PackageArchive
         */
-       public $archive = null;
+       public $archive;
        
        /**
         * instance of PackageInstallationNodeBuilder
-        * @var \wcf\system\package\PackageInstallationNodeBuilder
+        * @var PackageInstallationNodeBuilder
         */
-       public $nodeBuilder = null;
+       public $nodeBuilder;
        
        /**
         * instance of Package
-        * @var \wcf\data\package\Package
+        * @var Package
         */
-       public $package = null;
+       public $package;
        
        /**
         * instance of PackageInstallationQueue
-        * @var \wcf\system\package\PackageInstallationQueue
+        * @var PackageInstallationQueue
         */
-       public $queue = null;
+       public $queue;
        
        /**
         * default name of the config file
@@ -86,14 +85,14 @@ class PackageInstallationDispatcher {
        
        /**
         * data of previous package in queue
-        * @var array<string>
+        * @var string[]
         */
-       protected $previousPackageData = null;
+       protected $previousPackageData;
        
        /**
         * Creates a new instance of PackageInstallationDispatcher.
         * 
-        * @param       \wcf\data\package\installation\queue\PackageInstallationQueue   $queue
+        * @param       PackageInstallationQueue        $queue
         */
        public function __construct(PackageInstallationQueue $queue) {
                $this->queue = $queue;
@@ -105,7 +104,7 @@ class PackageInstallationDispatcher {
        /**
         * Sets data of previous package in queue.
         * 
-        * @param       array<string>   $packageData
+        * @param       string[]        $packageData
         */
        public function setPreviousPackage(array $packageData) {
                $this->previousPackageData = $packageData;
@@ -115,12 +114,13 @@ class PackageInstallationDispatcher {
         * Installs node components and returns next node.
         * 
         * @param       string          $node
-        * @return      \wcf\system\package\PackageInstallationStep
+        * @return      PackageInstallationStep
         */
        public function install($node) {
                $nodes = $this->nodeBuilder->getNodeData($node);
                
                // invoke node-specific actions
+               $step = null;
                foreach ($nodes as $data) {
                        $nodeData = unserialize($data['nodeData']);
                        
@@ -152,7 +152,6 @@ class PackageInstallationDispatcher {
                $this->nodeBuilder->completeNode($node);
                
                // assign next node
-               $tmp = $node;
                $node = $this->nodeBuilder->getNextNode($node);
                $step->setNode($node);
                
@@ -163,10 +162,10 @@ class PackageInstallationDispatcher {
                                SET     optionValue = ?
                                WHERE   optionName = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array(
+                       $statement->execute([
                                TIME_NOW,
                                'last_update_time'
-                       ));
+                       ]);
                        
                        // update options.inc.php
                        OptionEditor::resetCache();
@@ -184,10 +183,10 @@ class PackageInstallationDispatcher {
                                                WHERE   optionName = ?";
                                        $statement = WCF::getDB()->prepareStatement($sql);
                                        
-                                       $statement->execute(array(
+                                       $statement->execute([
                                                StringUtil::getUUID(),
                                                'wcf_uuid'
-                                       ));
+                                       ]);
                                        
                                        try {
                                                $statement->execute([
@@ -201,14 +200,14 @@ class PackageInstallationDispatcher {
                                        }
                                        
                                        if (WCF::getSession()->getVar('__wcfSetup_developerMode')) {
-                                               $statement->execute(array(
+                                               $statement->execute([
                                                        1,
                                                        'enable_debug_mode'
-                                               ));
-                                               $statement->execute(array(
+                                               ]);
+                                               $statement->execute([
                                                        'public',
                                                        'exception_privacy'
-                                               ));
+                                               ]);
                                        }
                                        
                                        // update options.inc.php
@@ -217,7 +216,6 @@ class PackageInstallationDispatcher {
                                
                                // rebuild application paths
                                ApplicationHandler::rebuild();
-                               ApplicationEditor::setup();
                        }
                        
                        // remove template listener cache
@@ -242,11 +240,11 @@ class PackageInstallationDispatcher {
                                                AND package.packageID <> ?
                                                AND package.isApplication = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array(
+                       $statement->execute([
                                $this->queue->processNo,
                                1,
                                1
-                       ));
+                       ]);
                        while ($row = $statement->fetchArray()) {
                                Package::writeConfigFile($row['packageID']);
                        }
@@ -258,7 +256,7 @@ class PackageInstallationDispatcher {
                                FROM    wcf".WCF_N."_package_installation_queue
                                WHERE   processNo = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array($this->queue->processNo));
+                       $statement->execute([$this->queue->processNo]);
                        while ($row = $statement->fetchArray()) {
                                @unlink($row['archive']);
                        }
@@ -267,7 +265,7 @@ class PackageInstallationDispatcher {
                        $sql = "DELETE FROM     wcf".WCF_N."_package_installation_queue
                                WHERE           processNo = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array($this->queue->processNo));
+                       $statement->execute([$this->queue->processNo]);
                }
                
                return $step;
@@ -276,7 +274,7 @@ class PackageInstallationDispatcher {
        /**
         * Returns current package archive.
         * 
-        * @return      \wcf\system\package\PackageArchive
+        * @return      PackageArchive
         */
        public function getArchive() {
                if ($this->archive === null) {
@@ -296,9 +294,7 @@ class PackageInstallationDispatcher {
                                // package_installation_queue with this value
                                $archive = $this->archive->downloadArchive();
                                $queueEditor = new PackageInstallationQueueEditor($this->queue);
-                               $queueEditor->update(array(
-                                       'archive' => $archive
-                               ));
+                               $queueEditor->update(['archive' => $archive]);
                        }
                        
                        $this->archive->openArchive();
@@ -310,7 +306,8 @@ class PackageInstallationDispatcher {
        /**
         * Installs current package.
         * 
-        * @param       array           $nodeData
+        * @param       mixed[]         $nodeData
+        * @return      PackageInstallationStep
         */
        protected function installPackage(array $nodeData) {
                $installationStep = new PackageInstallationStep();
@@ -324,7 +321,7 @@ class PackageInstallationDispatcher {
                                                FROM    wcf".WCF_N."_package
                                                WHERE   packageID = ?";
                                        $statement = WCF::getDB()->prepareStatement($sql);
-                                       $statement->execute(array($requirementData['packageID']));
+                                       $statement->execute([$requirementData['packageID']]);
                                }
                                else {
                                        // try to find matching package
@@ -332,7 +329,7 @@ class PackageInstallationDispatcher {
                                                FROM    wcf".WCF_N."_package
                                                WHERE   package = ?";
                                        $statement = WCF::getDB()->prepareStatement($sql);
-                                       $statement->execute(array($package));
+                                       $statement->execute([$package]);
                                }
                                $row = $statement->fetchArray();
                                
@@ -360,13 +357,13 @@ class PackageInstallationDispatcher {
                        $sql = "DELETE FROM     wcf".WCF_N."_package_exclusion
                                WHERE           packageID = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array($this->queue->packageID));
+                       $statement->execute([$this->queue->packageID]);
                        
                        // delete old requirements and dependencies
                        $sql = "DELETE FROM     wcf".WCF_N."_package_requirement
                                WHERE           packageID = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute(array($this->queue->packageID));
+                       $statement->execute([$this->queue->packageID]);
                }
                else {
                        // create package entry
@@ -374,9 +371,7 @@ class PackageInstallationDispatcher {
                        
                        // update package id for current queue
                        $queueEditor = new PackageInstallationQueueEditor($this->queue);
-                       $queueEditor->update(array(
-                               'packageID' => $package->packageID
-                       ));
+                       $queueEditor->update(['packageID' => $package->packageID]);
                        
                        // reload queue
                        $this->queue = new PackageInstallationQueue($this->queue->queueID);
@@ -384,16 +379,16 @@ class PackageInstallationDispatcher {
                        
                        if ($package->isApplication) {
                                $host = str_replace(RouteHandler::getProtocol(), '', RouteHandler::getHost());
-                               $path = RouteHandler::getPath(array('acp'));
+                               $path = RouteHandler::getPath(['acp']);
                                
                                // insert as application
-                               ApplicationEditor::create(array(
+                               ApplicationEditor::create([
                                        'domainName' => $host,
                                        'domainPath' => $path,
                                        'cookieDomain' => $host,
                                        'cookiePath' => $path,
                                        'packageID' => $package->packageID
-                               ));
+                               ]);
                        }
                }
                
@@ -405,7 +400,11 @@ class PackageInstallationDispatcher {
                        $statement = WCF::getDB()->prepareStatement($sql);
                        
                        foreach ($this->getArchive()->getExcludedPackages() as $excludedPackage) {
-                               $statement->execute(array($this->queue->packageID, $excludedPackage['name'], (!empty($excludedPackage['version']) ? $excludedPackage['version'] : '')));
+                               $statement->execute([
+                                       $this->queue->packageID,
+                                       $excludedPackage['name'],
+                                       (!empty($excludedPackage['version']) ? $excludedPackage['version'] : '')
+                               ]);
                        }
                }
                
@@ -420,7 +419,10 @@ class PackageInstallationDispatcher {
                        foreach ($requirements as $identifier => $possibleRequirements) {
                                $requirement = array_shift($possibleRequirements);
                                
-                               $statement->execute(array($this->queue->packageID, $requirement['packageID']));
+                               $statement->execute([
+                                       $this->queue->packageID,
+                                       $requirement['packageID']
+                               ]);
                        }
                }
                
@@ -532,7 +534,7 @@ class PackageInstallationDispatcher {
        /**
         * Executes a package installation plugin.
         * 
-        * @param       array           step
+        * @param       mixed[]         $nodeData
         * @return      boolean
         */
        protected function executePIP(array $nodeData) {
@@ -544,9 +546,7 @@ class PackageInstallationDispatcher {
                        FROM    wcf".WCF_N."_package_installation_plugin
                        WHERE   pluginName = ?";
                $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute(array(
-                       $nodeData['pip']
-               ));
+               $statement->execute([$nodeData['pip']]);
                $row = $statement->fetchArray();
                
                // PIP is unknown
@@ -562,7 +562,7 @@ class PackageInstallationDispatcher {
                
                // set default value
                if (empty($nodeData['value'])) {
-                       $defaultValue = call_user_func(array($className, 'getDefaultFilename'));
+                       $defaultValue = call_user_func([$className, 'getDefaultFilename']);
                        if ($defaultValue) {
                                $nodeData['value'] = $defaultValue;
                        }
@@ -571,7 +571,7 @@ class PackageInstallationDispatcher {
                $plugin = new $className($this, $nodeData);
                
                if (!($plugin instanceof IPackageInstallationPlugin)) {
-                       throw new SystemException("'".$className."' does not implement 'wcf\system\package\plugin\IPackageInstallationPlugin'");
+                       throw new SystemException("'".$className."' does not implement 'wcf\\system\\package\\plugin\\IPackageInstallationPlugin'");
                }
                
                // execute PIP
diff --git a/wcfsetup/install/files/lib/system/package/plugin/BoxPackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/package/plugin/BoxPackageInstallationPlugin.class.php
new file mode 100644 (file)
index 0000000..4ca155a
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+namespace wcf\system\package\plugin;
+use wcf\data\box\Box;
+use wcf\data\box\BoxEditor;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+
+/**
+ * Installs, updates and deletes boxes.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage acp.package.plugin
+ * @category   Community Framework
+ */
+class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+       /**
+        * @inheritDoc
+        */
+       public $className = BoxEditor::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $tagName = 'box';
+       
+       /**
+        * @inheritDoc
+        */
+       protected function handleDelete(array $items) {
+               $sql = "DELETE FROM     wcf".WCF_N."_box
+                       WHERE           identifier = ?
+                                       AND packageID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               
+               WCF::getDB()->beginTransaction();
+               foreach ($items as $item) {
+                       $statement->execute([
+                               $item['attributes']['identifier'],
+                               $this->installation->getPackageID()
+                       ]);
+               }
+               WCF::getDB()->commitTransaction();
+       }
+       
+       /**
+        * @inheritDoc
+        * @throws      SystemException
+        */
+       protected function getElement(\DOMXPath $xpath, array &$elements, \DOMElement $element) {
+               $nodeValue = $element->nodeValue;
+               
+               if ($element->tagName === 'name' || $element->tagName === 'title') {
+                       if (empty($element->getAttribute('language'))) {
+                               throw new SystemException("Missing required attribute 'language' for '" . $element->tagName . "' element (box '" . $element->parentNode->getAttribute('identifier') . "')");
+                       }
+                       
+                       // element can occur multiple times using the `language` attribute
+                       if (!isset($elements[$element->tagName])) $elements[$element->tagName] = [];
+                       
+                       $elements[$element->tagName][$element->getAttribute('language')] = $element->nodeValue;
+               }
+               else if ($element->tagName === 'content') {
+                       // content can occur multiple times using the `language` attribute
+                       if (!isset($elements['content'])) $elements['content'] = [];
+                       
+                       $children = [];
+                       /** @var \DOMElement $child */
+                       foreach ($xpath->query('child::*', $element) as $child) {
+                               $children[$child->tagName] = $child->nodeValue;
+                       }
+                       
+                       if (empty($children['title'])) {
+                               throw new SystemException("Expected non-empty child element 'title' for 'content' element (box '" . $element->parentNode->getAttribute('identifier') . "')");
+                       }
+                       if (empty($children['content'])) {
+                               throw new SystemException("Expected non-empty child element 'content' for 'content' element (box '" . $element->parentNode->getAttribute('identifier') . "')");
+                       }
+                       
+                       $elements['content'][$element->getAttribute('language')] = [
+                               'content' => $children['content'],
+                               'title' => $children['title']
+                       ];
+               }
+               else {
+                       $elements[$element->tagName] = $nodeValue;
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        * @throws      SystemException
+        */
+       protected function prepareImport(array $data) {
+               $boxType = $data['elements']['boxtype'];
+               $className = '';
+               $identifier = $data['attributes']['identifier'];
+               $isMultilingual = false;
+               $position = $data['elements']['position'];
+               
+               if (!in_array($position, ['bottom', 'contentBottom', 'contentTop', 'footer', 'footerBoxes', 'headerBoxes', 'hero', 'sidebarLeft', 'sidebarRight', 'top'])) {
+                       throw new SystemException("Unknown box position '{$position}' for box '{$identifier}'");
+               }
+               
+               switch ($boxType) {
+                       case 'system':
+                               if (empty($data['elements']['classname'])) {
+                                       throw new SystemException("Missing required element 'classname' for 'system'-type box '{$identifier}'");
+                               }
+                               
+                               $className = $data['elements']['classname'];
+                               break;
+                       
+                       case 'html':
+                       case 'text':
+                               if (empty($data['elements']['content'])) {
+                                       throw new SystemException("Missing required 'content' element(s) for box '{$identifier}'");
+                               }
+                               
+                               if (count($data['elements']['content']) === 1) {
+                                       if (!isset($data['elements']['content'][''])) {
+                                               throw new SystemException("Expected one 'content' element without a 'language' attribute for box '{$identifier}'");
+                                       }
+                               }
+                               else {
+                                       $isMultilingual = true;
+                                       
+                                       if (isset($data['elements']['content'][''])) {
+                                               throw new SystemException("Cannot mix 'content' elements with and without 'language' attribute for box '{$identifier}'");
+                                       }
+                               }
+                               
+                               break;
+                       
+                       default:
+                               throw new SystemException("Unknown type '{$boxType}' for box '{$identifier}");
+                               break;
+               }
+               
+               return [
+                       'identifier' => $identifier,
+                       'name' => $this->getI18nValues($data['elements']['name'], true),
+                       'boxType' => $boxType,
+                       'position' => $position,
+                       'showOrder' => $this->getItemOrder($position),
+                       'visibleEverywhere' => (isset($data['elements']['visibleeverywhere']) && $data['elements']['visibleeverywhere'] === '1') ? '1' : '0',
+                       'isMultilingual' => ($isMultilingual) ? '1' : '0',
+                       'cssClassName' => (!empty($data['elements']['cssClassName'])) ? $data['elements']['cssClassName'] : '',
+                       'showHeader' => (isset($data['elements']['showheader']) && $data['elements']['showheader'] === '1') ? '1' : '0',
+                       'originIsSystem' => 1,
+                       'className' => $className
+               ];
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function findExistingItem(array $data) {
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_box
+                       WHERE   identifier = ?
+                               AND packageID = ?";
+               $parameters = array(
+                       $data['identifier'],
+                       $this->installation->getPackageID()
+               );
+               
+               return array(
+                       'sql' => $sql,
+                       'parameters' => $parameters
+               );
+       }
+       
+       /**
+        * Returns the show order for a new item that will append it to the current
+        * menu or parent item.
+        *
+        * @param       string  $position       box position
+        * @return      integer
+        */
+       protected function getItemOrder($position) {
+               $sql = "SELECT  MAX(showOrder) AS showOrder
+                       FROM    wcf".WCF_N."_box
+                       WHERE   position = ?";
+               $statement = WCF::getDB()->prepareStatement($sql, 1);
+               $statement->execute([$position]);
+               
+               $row = $statement->fetchSingleRow();
+               
+               return (!$row['showOrder']) ? 1 : $row['showOrder'] + 1;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function import(array $row, array $data) {
+               // updating boxes is only supported for 'system' type boxes, all other
+               // types would potentially overwrite changes made by the user if updated
+               if (!empty($row) && $row['boxType'] !== 'system') {
+                       return new Box(null, $row);
+               }
+               
+               return parent::import($row, $data);
+       }
+}
index 9bb9e0f3dc4fd38c74e8eb36caa8412f47d7bd68..6739726de2c0513541e01cb591f4239ad27127e9 100644 (file)
@@ -1,7 +1,10 @@
 <?php
 namespace wcf\system\package\plugin;
+use wcf\data\box\Box;
+use wcf\data\box\BoxEditor;
 use wcf\data\menu\Menu;
 use wcf\data\menu\MenuEditor;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\WCF;
 
@@ -16,6 +19,12 @@ use wcf\system\WCF;
  * @category   Community Framework
  */
 class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+       /**
+        * box meta data per menu
+        * @var string[]
+        */
+       public $boxData = [];
+       
        /**
         * @inheritDoc
         */
@@ -62,6 +71,26 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        
                        $elements['title'][$element->getAttribute('language')] = $element->nodeValue;
                }
+               else if ($element->tagName === 'box') {
+                       $elements['box'] = [];
+                       
+                       /** @var \DOMElement $child */
+                       foreach ($xpath->query('child::*', $element) as $child) {
+                               if ($child->tagName === 'name') {
+                                       if (empty($child->getAttribute('language'))) {
+                                               throw new SystemException("Missing required attribute 'language' for box name (menu '" . $element->parentNode->getAttribute('identifier') . "')");
+                                       }
+                                       
+                                       // <title> can occur multiple times using the `language` attribute
+                                       if (!isset($elements['box']['name'])) $elements['box']['name'] = [];
+                                       
+                                       $elements['box']['name'][$element->getAttribute('language')] = $element->nodeValue;
+                               }
+                               else {
+                                       $elements['box'][$child->tagName] = $child->nodeValue;
+                               }
+                       }
+               }
                else {
                        $elements[$element->tagName] = $nodeValue;
                }
@@ -71,8 +100,36 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         * @inheritDoc
         */
        protected function prepareImport(array $data) {
+               $identifier = $data['attributes']['identifier'];
+               
+               if (!empty($data['elements']['box'])) {
+                       $position = $data['elements']['box']['position'];
+                       $visibleEverywhere = false;
+                       
+                       if ($identifier === 'com.woltlab.wcf.MainMenu') {
+                               $position = 'mainMenu';
+                               $visibleEverywhere = true;
+                       }
+                       else if (!in_array($position, ['bottom', 'contentBottom', 'contentTop', 'footer', 'footerBoxes', 'headerBoxes', 'hero', 'sidebarLeft', 'sidebarRight', 'top'])) {
+                               throw new SystemException("Unknown box position '{$position}' for menu box '{$identifier}'");
+                       }
+                       
+                       $this->boxData[$identifier] = [
+                               'identifier' => $identifier,
+                               'name' => $this->getI18nValues($data['elements']['title'], true),
+                               'boxType' => 'menu',
+                               'position' => $position,
+                               'visibleEverywhere' => ($visibleEverywhere) ? 1 : 0,
+                               'cssClassName' => (!empty($data['elements']['box']['cssclassname'])),
+                               'originIsSystem' => 1,
+                               'packageID' => $this->installation->getPackageID()
+                       ];
+                       
+                       unset($data['elements']['box']);
+               }
+               
                return [
-                       'identifier' => $data['attributes']['identifier'],
+                       'identifier' => $identifier,
                        'title' => $this->getI18nValues($data['elements']['title']),
                        'originIsSystem' => 1
                ];
@@ -109,4 +166,40 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                
                return parent::import($row, $data);
        }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function postImport() {
+               if (empty($this->boxData)) return;
+               
+               // all boxes belonging to the identifiers
+               $conditions = new PreparedStatementConditionBuilder();
+               $conditions->add("identifier IN (?)", [array_keys($this->boxData)]);
+               $conditions->add("packageID = ?", [$this->installation->getPackageID()]);
+               
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_box
+                       ".$conditions;
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute($conditions->getParameters());
+               $boxes = [];
+               while ($box = $statement->fetchObject(Box::class)) {
+                       $boxes[$box->identifier] = $box;
+               }
+               
+               foreach ($this->boxData as $identifier => $data) {
+                       if (isset($boxes[$identifier])) {
+                               // skip both 'identifier' and 'packageID' as these properties are immutable
+                               unset($data['identifier']);
+                               unset($data['packageID']);
+                               
+                               $boxEditor = new BoxEditor($boxes[$identifier]);
+                               $boxEditor->update($data);
+                       }
+                       else {
+                               BoxEditor::create($data);
+                       }
+               }
+       }
 }
index cdd33f2370e826eb4a8c9b45053291561bc7ddce..793008d93188baf503208afcc97f911033a599b1 100644 (file)
@@ -38,7 +38,7 @@ class PagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         */
        protected function handleDelete(array $items) {
                $sql = "DELETE FROM     wcf".WCF_N."_page
-                       WHERE           name = ?
+                       WHERE           identifier = ?
                                        AND packageID = ?";
                $statement = WCF::getDB()->prepareStatement($sql);
                
@@ -127,7 +127,7 @@ class PagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                if (!empty($data['elements']['parent'])) {
                        $sql = "SELECT  pageID
                                FROM    wcf".WCF_N."_".$this->tableName."
-                               WHERE   name = ?";
+                               WHERE   identifier = ?";
                        $statement = WCF::getDB()->prepareStatement($sql, 1);
                        $statement->execute([$data['elements']['parent']]);
                        $row = $statement->fetchSingleRow();
@@ -162,7 +162,7 @@ class PagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
        protected function findExistingItem(array $data) {
                $sql = "SELECT  *
                        FROM    wcf".WCF_N."_".$this->tableName."
-                       WHERE   name = ?
+                       WHERE   identifier = ?
                                AND packageID = ?";
                $parameters = array(
                        $data['identifier'],
index 1bf707e48de6d5daccb5140d081a19c75d0ae9f6..5f27483651db0cada876ead6f40b5da42a055ffe 100644 (file)
@@ -1,9 +1,11 @@
 <?php
 namespace wcf\system\request;
 use wcf\page\CmsPage;
+use wcf\system\application\ApplicationHandler;
 use wcf\system\cache\builder\RoutingCacheBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\SingletonFactory;
+use wcf\system\WCF;
 
 /**
  * Resolves incoming requests and performs lookups for controller to url mappings.
@@ -26,6 +28,11 @@ class ControllerMap extends SingletonFactory {
         */
        protected $customUrls;
        
+       /**
+        * @var string[]
+        */
+       protected $landingPages;
+       
        /**
         * list of <ControllerName> to <controller-name> mappings
         * @var array<string>
@@ -39,6 +46,7 @@ class ControllerMap extends SingletonFactory {
        protected function init() {
                $this->ciControllers = RoutingCacheBuilder::getInstance()->getData([], 'ciControllers');
                $this->customUrls = RoutingCacheBuilder::getInstance()->getData([], 'customUrls');
+               $this->landingPages = RoutingCacheBuilder::getInstance()->getData([], 'landingPages');
        }
        
        /**
@@ -146,10 +154,7 @@ class ControllerMap extends SingletonFactory {
                        $urlController = $this->customUrls['reverse'][$application][$controller];
                }
                else {
-                       $parts = preg_split('~([A-Z][a-z0-9]+)~', $controller, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
-                       $parts = array_map('strtolower', $parts);
-                       
-                       $urlController = implode('-', $parts);
+                       $urlController = self::transformController($controller);
                }
                
                $this->lookupCache[$lookupKey] = $urlController;
@@ -179,6 +184,68 @@ class ControllerMap extends SingletonFactory {
                return null;
        }
        
+       /**
+        * Lookups default controller for given application.
+        * 
+        * @param       string  $application    application identifier
+        * @return      null|string[]           default controller
+        */
+       public function lookupDefaultController($application) {
+               $controller = $this->landingPages[$application][1];
+               
+               if ($application === 'wcf' && empty($controller)) {
+                       return null;
+               }
+               else if (preg_match('~^__WCF_CMS__(?P<pageID>\d+)$~', $controller, $matches)) {
+                       $cmsPageData = $this->lookupCmsPage($matches['pageID'], 0);
+                       if ($cmsPageData === null) {
+                               // page is multilingual, use current language id to resolve request
+                               $cmsPageData = $this->lookupCmsPage($matches['pageID'], WCF::getLanguage()->languageID);
+                               
+                               if ($cmsPageData === null) {
+                                       throw new SystemException("Unable to resolve CMS page");
+                               }
+                       }
+                       
+                       // different application, redirect instead
+                       if ($cmsPageData['application'] !== $application) {
+                               return ['redirect' => LinkHandler::getInstance()->getCmsLink($matches['pageID'])];
+                       }
+                       else {
+                               return $this->resolveCustomController($cmsPageData['application'], $cmsPageData['controller']);
+                       }
+               }
+               
+               return [
+                       'application' => $application,
+                       'controller' => $controller
+               ];
+       }
+       
+       /**
+        * Returns true if given controller is the application's default.
+        * 
+        * @param       string  $application    application identifier
+        * @param       string  $controller     url controller name
+        * @return      boolean true if controller is the application's default
+        */
+       public function isDefaultController($application, $controller) {
+               // lookup custom urls first
+               if (isset($this->customUrls['lookup'][$application], $this->customUrls['lookup'][$application][$controller])) {
+                       $controller = $this->customUrls['lookup'][$application][$controller];
+                       if (strpos($controller, '__WCF_CMS__') !== false) {
+                               // remove language id component
+                               $controller = preg_replace('~\-\d+$~', '', $controller);
+                       }
+               }
+               
+               if ($this->landingPages[$application][0] === $controller) {
+                       return true;
+               }
+               
+               return false;
+       }
+       
        /**
         * Returns the class data for the active request or null if for the given
         * configuration no proper class exist.
@@ -187,7 +254,7 @@ class ControllerMap extends SingletonFactory {
         * @param       string          $controller     controller name
         * @param       boolean         $isAcpRequest   true if this is an ACP request
         * @param       string          $pageType       page type, e.g. 'form' or 'action'
-        * @return      array<string>   className, controller and pageType
+        * @return      string[]        className, controller and pageType
         */
        protected function getClassData($application, $controller, $isAcpRequest, $pageType) {
                $className = $application . '\\' . ($isAcpRequest ? 'acp\\' : '') . $pageType . '\\' . $controller . ucfirst($pageType);
@@ -214,4 +281,17 @@ class ControllerMap extends SingletonFactory {
                        'pageType' => $pageType
                ];
        }
+       
+       /**
+        * Transforms a controller into its URL representation.
+        *
+        * @param       string  $controller     controller, e.g. 'BoardList'
+        * @return      string  url representation, e.g. 'board-list'
+        */
+       public static function transformController($controller) {
+               $parts = preg_split('~([A-Z][a-z0-9]+)~', $controller, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+               $parts = array_map('strtolower', $parts);
+               
+               return implode('-', $parts);
+       }
 }
index 664310fe683df8f05f14de71921db3c2eaf10456..3dc5d065c62a488952b638521802a3f2621589b6 100644 (file)
@@ -36,12 +36,6 @@ class FlexibleRoute implements IRoute {
         */
        protected $pattern = '';
        
-       /**
-        * primary application's abbreviation (e.g. "wbb")
-        * @var string
-        */
-       protected $primaryApplication = '';
-       
        /**
         * list of required components
         * @var array<string>
@@ -141,15 +135,14 @@ class FlexibleRoute implements IRoute {
                        
                        if (!RequestHandler::getInstance()->isACPRequest()) {
                                $landingPage = PageMenu::getInstance()->getLandingPage();
-                               if ($this->primaryApplication === '') {
-                                       $primaryApplication = ApplicationHandler::getInstance()->getPrimaryApplication();
-                                       $this->primaryApplication = ApplicationHandler::getInstance()->getAbbreviation($primaryApplication->packageID);
-                               }
                                
                                // check if this is the default controller
                                if (strcasecmp(RouteHandler::getInstance()->getDefaultController($application), $components['controller']) === 0) {
                                        // check if this matches the primary application
-                                       if ($this->primaryApplication === $application) {
+                                       /*
+                                        * TODO: what exactly is this doing?
+                                        */
+                                       /*if ($this->primaryApplication === $application) {
                                                if (strcasecmp($landingPage->getController(), $components['controller']) === 0) {
                                                        // skip controller if it matches the default controller
                                                        $ignoreController = true;
@@ -159,6 +152,7 @@ class FlexibleRoute implements IRoute {
                                                // skip default controller
                                                $ignoreController = true;
                                        }
+                                       */
                                }
                                else if (strcasecmp($landingPage->getController(), $components['controller']) === 0) {
                                        // landing page
index 5f7306fc451f44a47859c843b3999f12d6bbc5e7..c2fdd77b23ecfcc7a36cd98fbefd336d9d8e44b7 100644 (file)
@@ -22,21 +22,21 @@ use wcf\util\StringUtil;
 class LinkHandler extends SingletonFactory {
        /**
         * regex object to filter title
-        * @var \wcf\system\RegEx
+        * @var RegEx
         */
-       protected $titleRegex = null;
+       protected $titleRegex;
        
        /**
         * title search strings
-        * @var array<string>
+        * @var string[]
         */
-       protected $titleSearch = array();
+       protected $titleSearch = [];
        
        /**
         * title replacement strings
-        * @var array<string>
+        * @var string[]
         */
-       protected $titleReplace = array();
+       protected $titleReplace = [];
        
        
        /**
@@ -64,11 +64,11 @@ class LinkHandler extends SingletonFactory {
         * @param       string          $url
         * @return      string
         */
-       public function getLink($controller = null, array $parameters = array(), $url = '') {
+       public function getLink($controller = null, array $parameters = [], $url = '') {
                $abbreviation = 'wcf';
                $anchor = '';
                $isACP = $originIsACP = RequestHandler::getInstance()->isACPRequest();
-               $forceWCF = $isRaw = false;
+               $isRaw = false;
                $appendSession = $encodeTitle = true;
                
                // enforce a certain level of sanitation and protection for links embedded in emails
@@ -106,9 +106,7 @@ class LinkHandler extends SingletonFactory {
                        unset($parameters['forceFrontend']);
                }
                if (isset($parameters['forceWCF'])) {
-                       if ($parameters['forceWCF'] && $isACP) {
-                               $forceWCF = true;
-                       }
+                       /** @deprecated 2.2 */
                        unset($parameters['forceWCF']);
                }
                
@@ -129,7 +127,7 @@ class LinkHandler extends SingletonFactory {
                                $controller = 'Index';
                        }
                        else {
-                               return PageMenu::getInstance()->getLandingPage()->getProcessor()->getLink();
+                               return '';// PageMenu::getInstance()->getLandingPage()->getProcessor()->getLink();
                        }
                }
                
@@ -171,7 +169,7 @@ class LinkHandler extends SingletonFactory {
                
                // encode certain characters
                if (!empty($url)) {
-                       $url = str_replace(array('[', ']'), array('%5B', '%5D'), $url);
+                       $url = str_replace(['[', ']'], ['%5B', '%5D'], $url);
                }
                
                $url = $routeURL . $url;
@@ -183,28 +181,14 @@ class LinkHandler extends SingletonFactory {
                
                // handle applications
                if (!PACKAGE_ID) {
-                       $url = RouteHandler::getHost() . RouteHandler::getPath(array('acp')) . ($isACP ? 'acp/' : '') . $url;
+                       $url = RouteHandler::getHost() . RouteHandler::getPath(['acp']) . ($isACP ? 'acp/' : '') . $url;
                }
                else {
                        if (RequestHandler::getInstance()->inRescueMode()) {
-                               $pageURL = RouteHandler::getHost() . str_replace('//', '/', RouteHandler::getPath(array('acp')));
+                               $pageURL = RouteHandler::getHost() . str_replace('//', '/', RouteHandler::getPath(['acp']));
                        }
                        else {
-                               // try to resolve abbreviation
-                               $application = null;
-                               if ($abbreviation != 'wcf') {
-                                       $application = ApplicationHandler::getInstance()->getApplication($abbreviation);
-                               }
-                               
-                               // fallback to primary application if abbreviation is 'wcf' or unknown
-                               if ($forceWCF) {
-                                       $application = ApplicationHandler::getInstance()->getWCF();
-                               }
-                               else if ($application === null) {
-                                       $application = ApplicationHandler::getInstance()->getPrimaryApplication();
-                               }
-                               
-                               $pageURL = $application->getPageURL();
+                               $pageURL = ApplicationHandler::getInstance()->getApplication($abbreviation)->getPageURL();
                        }
                        
                        $url = $pageURL . ($isACP ? 'acp/' : '') . $url;
@@ -227,7 +211,6 @@ class LinkHandler extends SingletonFactory {
         * @param       integer         $pageID         page id
         * @param       integer         $languageID     language id, optional
         * @return      string          full URL of empty string if `$pageID` is invalid
-        * @throws      \wcf\system\exception\SystemException
         */
        public function getCmsLink($pageID, $languageID = -1) {
                // use current language
index 9ef31df7fb58ae3f33fbc02a0b734dc11ca0b98e..00f838396571a2aabff155301bc0424fb2c8e7aa 100644 (file)
@@ -137,7 +137,7 @@ class RequestHandler extends SingletonFactory {
                                $this->handleDefaultController($application, $routeData);
                                
                                // check if accessing from the wrong domain (e.g. "www." omitted but domain was configured with)
-                               if (!defined('WCF_RUN_MODE') || WCF_RUN_MODE != 'embedded') {
+                               if (!defined('WCF_RUN_MODE') || WCF_RUN_MODE !== 'embedded') {
                                        $applicationObject = ApplicationHandler::getInstance()->getApplication($application);
                                        if ($applicationObject->domainName != $_SERVER['HTTP_HOST']) {
                                                // build URL, e.g. http://example.net/forum/
@@ -223,52 +223,33 @@ class RequestHandler extends SingletonFactory {
         * Checks page access for possible mandatory redirects.
         * 
         * @param       string          $application
-        * @param       array           $routeData
+        * @param       string[]        $routeData
         */
        protected function handleDefaultController($application, array &$routeData) {
                if (!RouteHandler::getInstance()->isDefaultController()) {
                        return;
                }
                
-               $landingPage = PageMenu::getInstance()->getLandingPage();
-               if ($landingPage === null) {
-                       return;
+               $data = ControllerMap::getInstance()->lookupDefaultController($application);
+               if ($data === null) {
+                       // handle WCF which does not have a default controller
+                       throw new IllegalLinkException();
                }
-               
-               if (empty($routeData['controller'])) $routeData['isImplicitController'] = true;
-               
-               // resolve implicit application abbreviation for landing page controller
-               $landingPageApplication = $landingPage->getApplication();
-               $primaryApplication = ApplicationHandler::getInstance()->getPrimaryApplication();
-               $primaryApplicationAbbr = ApplicationHandler::getInstance()->getAbbreviation($primaryApplication->packageID);
-               if ($landingPageApplication == 'wcf') {
-                       $landingPageApplication = $primaryApplicationAbbr;
+               else if (!empty($data['redirect'])) {
+                       // force a redirect
+                       HeaderUtil::redirect($data['redirect'], true);
                }
                
-               // check if currently invoked application matches the landing page
-               if ($landingPageApplication == $application) {
-                       $routeData['controller'] = $landingPage->getController();
-                       $routeData['controller'] = ControllerMap::getInstance()->lookup($application, $routeData['controller']);
-                       
-                       return;
+               // copy route data
+               foreach ($data as $key => $value) {
+                       $routeData[$key] = $value;
                }
-               
-               // redirect if this is the primary application
-               if ($application === $primaryApplicationAbbr) {
-                       HeaderUtil::redirect($landingPage->getLink());
-                       exit;
-               }
-               
-               // set default controller
-               $applicationObj = WCF::getApplicationObject(ApplicationHandler::getInstance()->getApplication($application));
-               $routeData['controller'] = preg_replace('~^.*?\\\([^\\\]+)(?:Action|Form|Page)$~', '\\1', $applicationObj->getPrimaryController());
-               $routeData['controller'] = ControllerMap::getInstance()->lookup($application, $routeData['controller']);
        }
        
        /**
         * Returns the active request object.
         * 
-        * @return      \wcf\system\request\Request
+        * @return      Request
         */
        public function getActiveRequest() {
                return $this->activeRequest;
index 71c4aea58aa887bc7b5993f592b5f5ff6080f06c..883690a14e289dc3721bdb7467c61c3497244c38 100644 (file)
@@ -282,6 +282,8 @@ class Route implements IRoute {
                                }
                                
                                // check if this is the default controller of the requested application
+                               /*
+                                * TODO: what exactly is the check for the primary application doing?
                                if (!URL_LEGACY_MODE && !$ignoreController && $application !== null) {
                                        if (isset(self::$defaultControllers[$application]) && self::$defaultControllers[$application] == $components['controller']) {
                                                // check if this is the primary application and the landing page originates to the same application
@@ -292,6 +294,7 @@ class Route implements IRoute {
                                                }
                                        }
                                }
+                               */
                        }
                        
                        // drops controller from route
index e6f5963bb2eb9c791ebec9127e5daf8351dd4a5a..6e3a5e83117eb0581017db747590b97396bddbe4 100644 (file)
@@ -35,12 +35,6 @@ class DynamicRequestRoute implements IRequestRoute {
         */
        protected $pattern = '';
        
-       /**
-        * primary application's abbreviation (e.g. "wbb")
-        * @var string
-        */
-       protected $primaryApplication = '';
-       
        /**
         * list of required components
         * @var array<string>
@@ -139,7 +133,7 @@ class DynamicRequestRoute implements IRequestRoute {
        }
        
        /**
-        * @see IRoute::buildLink()
+        * @inheritDoc
         */
        public function buildLink(array $components) {
                $application = (isset($components['application'])) ? $components['application'] : null;
@@ -150,37 +144,8 @@ class DynamicRequestRoute implements IRequestRoute {
                // handle default values for controller
                $useBuildSchema = true;
                if (count($components) == 1 && isset($components['controller'])) {
-                       $ignoreController = false;
-                       
-                       if (!RequestHandler::getInstance()->isACPRequest()) {
-                               $landingPage = PageMenu::getInstance()->getLandingPage();
-                               if ($this->primaryApplication === '') {
-                                       $primaryApplication = ApplicationHandler::getInstance()->getPrimaryApplication();
-                                       $this->primaryApplication = ApplicationHandler::getInstance()->getAbbreviation($primaryApplication->packageID);
-                               }
-                               
-                               // check if this is the default controller
-                               if (strcasecmp(RouteHandler::getInstance()->getDefaultController($application), $components['controller']) === 0) {
-                                       // check if this matches the primary application
-                                       if ($this->primaryApplication === $application) {
-                                               if (strcasecmp($landingPage->getController(), $components['controller']) === 0) {
-                                                       // skip controller if it matches the default controller
-                                                       $ignoreController = true;
-                                               }
-                                       }
-                                       else {
-                                               // skip default controller
-                                               $ignoreController = true;
-                                       }
-                               }
-                               else if (strcasecmp($landingPage->getController(), $components['controller']) === 0) {
-                                       // landing page
-                                       $ignoreController = true;
-                               }
-                       }
-                       
-                       // drops controller from route
-                       if ($ignoreController) {
+                       if (!RequestHandler::getInstance()->isACPRequest() && ControllerMap::getInstance()->isDefaultController($application, $components['controller'])) {
+                               // drops controller from route
                                $useBuildSchema = false;
                                
                                // unset the controller, since it would otherwise be added with http_build_query()
@@ -195,7 +160,7 @@ class DynamicRequestRoute implements IRequestRoute {
         * Builds the actual link, the parameter $useBuildSchema can be set to false for
         * empty routes, e.g. for the default page.
         *
-        * @param       array           $components
+        * @param       string[]        $components
         * @param       string          $application
         * @param       boolean         $useBuildSchema
         * @return      string
@@ -260,7 +225,7 @@ class DynamicRequestRoute implements IRequestRoute {
        }
        
        /**
-        * @see IRoute::canHandle()
+        * @inheritDoc
         */
        public function canHandle(array $components) {
                if (!empty($this->requireComponents)) {
@@ -279,21 +244,21 @@ class DynamicRequestRoute implements IRequestRoute {
        }
        
        /**
-        * @see IRoute::getRouteData()
+        * @inheritDoc
         */
        public function getRouteData() {
                return $this->routeData;
        }
        
        /**
-        * @see IRoute::isACP()
+        * @inheritDoc
         */
        public function isACP() {
                return $this->isACP;
        }
        
        /**
-        * @see IRoute::matches()
+        * @inheritDoc
         */
        public function matches($requestURL) {
                if (preg_match($this->pattern, $requestURL, $matches)) {
index 186894359091154b4d58730ce3a653219d06a409..76afedd19357026f29ac07bb2ec6b18695f29cb1 100644 (file)
@@ -13,9 +13,9 @@ namespace wcf\util\exception;
  */
 class CryptoException extends \Exception {
        /**
-        * @see \Exception::__construct()
+        * @inheritDoc
         */
-       public function __construct($message, $previous) {
+       public function __construct($message, $previous = null) {
                parent::__construct($message, 0, $previous);
        }
 }
index 105a51349610204b2390ff6979789ddcaa14bbe7..80f0ae12c25f3afabd104ec834dcb830b1ebdefc 100644 (file)
@@ -59,9 +59,6 @@
                <item name="wcf.acp.application.edit"><![CDATA[Anwendung bearbeiten]]></item>
                <item name="wcf.acp.application.edit.title"><![CDATA[Anwendung bearbeiten: „<a href="{link controller='Package' id=$application->packageID}{/link}">{$application->getPackage()->getName()}</a>“]]></item>
                <item name="wcf.acp.application.list"><![CDATA[Installierte Anwendungen]]></item>
-               <item name="wcf.acp.application.primaryApplication"><![CDATA[Primäre Anwendung]]></item>
-               <item name="wcf.acp.application.setAsPrimary"><![CDATA[Als primäre Anwendung festlegen]]></item>
-               <item name="wcf.acp.application.setAsPrimary.confirmMessage"><![CDATA[Wollen Sie „{$application->getPackage()->getName()}“ wirklich als primäre Anwendung festlegen?]]></item>
        </category>
        
        <category name="wcf.acp.attachment">
index 733a1493cbbd3fe0073d7c2a625b63b47a93d483..350793f5e496453a8a17c09afcfb972e0c1e918a 100644 (file)
@@ -59,9 +59,6 @@
                <item name="wcf.acp.application.edit"><![CDATA[Edit Application]]></item>
                <item name="wcf.acp.application.edit.title"><![CDATA[Edit Application: “<a href="{link controller='Package' id=$application->packageID}{/link}">{$application->getPackage()->getName()}</a>”]]></item>
                <item name="wcf.acp.application.list"><![CDATA[Installed Applications]]></item>
-               <item name="wcf.acp.application.primaryApplication"><![CDATA[Primary Application]]></item>
-               <item name="wcf.acp.application.setAsPrimary"><![CDATA[Set As Primary Application]]></item>
-               <item name="wcf.acp.application.setAsPrimary.confirmMessage"><![CDATA[Do you really want to set “{$application->getPackage()->getName()}” as the primary application?]]></item>
        </category>
        
        <category name="wcf.acp.attachment">
index b0a070e1e2caf4668edae7c482256001780924e9..3cce2a4fec5550bc33bcf6ab35ec4d25d3c77376 100644 (file)
@@ -129,7 +129,7 @@ CREATE TABLE wcf1_application (
        domainPath VARCHAR(255) NOT NULL DEFAULT '/',
        cookieDomain VARCHAR(255) NOT NULL,
        cookiePath VARCHAR(255) NOT NULL DEFAULT '/',
-       isPrimary TINYINT(1) NOT NULL DEFAULT 0
+       landingPageID INT(10) NULL
 );
 
 DROP TABLE IF EXISTS wcf1_attachment;
@@ -228,7 +228,8 @@ CREATE TABLE wcf1_box (
        showHeader TINYINT(1) NOT NULL DEFAULT 1,
        originIsSystem TINYINT(1) NOT NULL DEFAULT 0,
        packageID INT(10) NOT NULL,
-       className VARCHAR(255) NOT NULL DEFAULT ''
+       className VARCHAR(255) NOT NULL DEFAULT '',
+       menuID INT(10) NULL
 );
 
 DROP TABLE IF EXISTS wcf1_box_content;
@@ -1590,6 +1591,7 @@ ALTER TABLE wcf1_acp_template ADD FOREIGN KEY (packageID) REFERENCES wcf1_packag
 ALTER TABLE wcf1_ad ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
 
 ALTER TABLE wcf1_application ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_application ADD FOREIGN KEY (landingPageID) REFERENCES wcf1_page (pageID) ON DELETE SET NULL;
 
 ALTER TABLE wcf1_attachment ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
 ALTER TABLE wcf1_attachment ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
@@ -1599,6 +1601,7 @@ ALTER TABLE wcf1_bbcode ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (pac
 ALTER TABLE wcf1_bbcode_attribute ADD FOREIGN KEY (bbcodeID) REFERENCES wcf1_bbcode (bbcodeID) ON DELETE CASCADE;
 
 ALTER TABLE wcf1_box ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_box ADD FOREIGN KEY (menuID) REFERENCES wcf1_menu (menuID) ON DELETE CASCADE;
 
 ALTER TABLE wcf1_box_content ADD FOREIGN KEY (boxID) REFERENCES wcf1_box (boxID) ON DELETE CASCADE;
 ALTER TABLE wcf1_box_content ADD FOREIGN KEY (languageID) REFERENCES wcf1_language (languageID) ON DELETE CASCADE;