Improved box system
authorMarcel Werk <burntime@woltlab.com>
Sun, 10 Apr 2016 17:57:00 +0000 (19:57 +0200)
committerMarcel Werk <burntime@woltlab.com>
Sun, 10 Apr 2016 18:03:55 +0000 (20:03 +0200)
* Added support for visibility exceptions in PIP
* Renamed classname to controller
* Added box controller interface
* Added some default boxes

19 files changed:
com.woltlab.wcf/box.xml [new file with mode: 0644]
com.woltlab.wcf/package.xml
com.woltlab.wcf/page.xml
com.woltlab.wcf/templates/boxRecentActivity.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/boxRecentActivitySidebar.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/install.php
wcfsetup/install/files/acp/post_install.php
wcfsetup/install/files/acp/templates/boxAdd.tpl
wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php
wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php
wcfsetup/install/files/lib/data/box/Box.class.php
wcfsetup/install/files/lib/system/box/AbstractBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/IBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/RecentActivityBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/package/PackageUninstallationDispatcher.class.php
wcfsetup/install/files/lib/system/package/plugin/BoxPackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/package/plugin/MenuPackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/package/plugin/PagePackageInstallationPlugin.class.php
wcfsetup/setup/db/install.sql

diff --git a/com.woltlab.wcf/box.xml b/com.woltlab.wcf/box.xml
new file mode 100644 (file)
index 0000000..5d7c6c3
--- /dev/null
@@ -0,0 +1,30 @@
+<?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/box.xsd">
+       <import>
+               <box identifier="com.woltlab.wcf.RecentActivity">
+                       <name language="de">Letzte Aktivitäten</name>
+                       <name language="en">Recent Activity</name>
+                       <boxType>system</boxType>
+                       <controller>wcf\system\box\RecentActivityBoxController</controller>
+                       <position>contentBottom</position>
+                       <showHeader>0</showHeader>
+                       <visibleEverywhere>0</visibleEverywhere>
+                       <visibilityExceptions>
+                               <page>com.woltlab.wcf.Dashboard</page>
+                       </visibilityExceptions>
+               </box>
+               
+               <box identifier="com.woltlab.wcf.RecentActivitySidebar">
+                       <name language="de">Letzte Aktivitäten (Sidebar)</name>
+                       <name language="en">Recent Activity (Sidebar)</name>
+                       <boxType>system</boxType>
+                       <controller>wcf\system\box\RecentActivityBoxController</controller>
+                       <position>sidebarRight</position>
+                       <showHeader>1</showHeader>
+                       <visibleEverywhere>0</visibleEverywhere>
+                       <visibilityExceptions>
+                               <page>com.woltlab.wcf.Dashboard</page>
+                       </visibilityExceptions>
+               </box>
+       </import>
+</data>
index f9a970661092ad0839b69b7e869aa552dfae6d55..9b6d1e5f6e4a6008e66cc5780d29306c19dbe895 100644 (file)
@@ -41,6 +41,7 @@
                <instruction type="page" />
                <instruction type="menu" />
                <instruction type="menuItem" />
+               <instruction type="box" />
                <instruction type="script">acp/post_install.php</instruction>
        </instructions>
        
index 7cb0b33c7f95f739ebfb832044bd3ffc7bd4688c..f47d5099417c9038fe34210605f51cf53514d76b 100644 (file)
@@ -2,14 +2,6 @@
 <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">
-                       <pageType>system</pageType>
-                       <controller>wcf\page\DashboardPage</controller>
-                       <name language="de"><![CDATA[Dashboard]]></name>
-                       <name language="en"><![CDATA[Dashboard]]></name>
-                       <options>module_dashboard_page</options>
-               </page>
-               
                <page identifier="com.woltlab.wcf.MembersList">
                        <pageType>system</pageType>
                        <controller>wcf\page\MembersListPage</controller>
                </page>
                
                <!-- static -->
+               <page identifier="com.woltlab.wcf.Dashboard">
+                       <pageType>text</pageType>
+                       <name language="de"><![CDATA[Dashboard]]></name>
+                       <name language="en"><![CDATA[Dashboard]]></name>
+                       <options>module_dashboard_page</options>
+                       
+                       <content>
+                               <title>Dashboard</title>
+                               <content><![CDATA[]]></content>
+                               <customURL>dashboard</customURL>
+                       </content>
+               </page>
+               
                <page identifier="com.woltlab.wcf.CookiePolicy">
                        <pageType>text</pageType>
                        <name language="en">Cookie Policy</name>
diff --git a/com.woltlab.wcf/templates/boxRecentActivity.tpl b/com.woltlab.wcf/templates/boxRecentActivity.tpl
new file mode 100644 (file)
index 0000000..8008352
--- /dev/null
@@ -0,0 +1,33 @@
+<section class="section sectionContainerList dashboardBoxRecentActivity" id="dashboardBoxRecentActivity">
+       <header class="sectionHeader">
+               <h2 class="sectionTitle">{lang}wcf.user.recentActivity{/lang}</h2>
+               
+               {if $canFilterByFollowedUsers}{*todo*}
+                       <nav class="jsMobileNavigation buttonGroupNavigation jsOnly jsRecentActivitySwitchContext">
+                               <ul class="buttonGroup">
+                                       <li><a href="#" class="button small{if !$filteredByFollowedUsers} active{/if}">{lang}wcf.user.recentActivity.scope.all{/lang}</a></li>
+                                       <li><a href="#" class="button small{if $filteredByFollowedUsers} active{/if}">{lang}wcf.user.recentActivity.scope.followedUsers{/lang}</a></li>
+                               </ul>
+                       </nav>
+               {/if}
+       </header>
+       
+       {assign var='__events' value=$eventList->getObjects()}
+       {assign var='__lastEvent' value=$__events|end}
+       <ul id="recentActivities" class="containerList recentActivityList" data-last-event-time="{@$lastEventTime}" data-last-event-id="{if $__lastEvent}{@$__lastEvent->eventID}{else}0{/if}">
+               {include file='recentActivityListItem'}
+       </ul>
+</section>
+
+<script data-relocate="true">
+       //<![CDATA[
+       $(function() {
+               WCF.Language.addObject({
+                       'wcf.user.recentActivity.more': '{lang}wcf.user.recentActivity.more{/lang}',
+                       'wcf.user.recentActivity.noMoreEntries': '{lang}wcf.user.recentActivity.noMoreEntries{/lang}'
+               });
+               
+               new WCF.User.RecentActivityLoader(null, {if $filteredByFollowedUsers}true{else}false{/if});
+       });
+       //]]>
+</script>
diff --git a/com.woltlab.wcf/templates/boxRecentActivitySidebar.tpl b/com.woltlab.wcf/templates/boxRecentActivitySidebar.tpl
new file mode 100644 (file)
index 0000000..695cdc0
--- /dev/null
@@ -0,0 +1,15 @@
+<ul class="sidebarBoxList">
+       {foreach from=$eventList item=event}
+               <li class="box24">
+                       <a href="{link controller='User' object=$event->getUserProfile()}{/link}" title="{$event->getUserProfile()->username}">{@$event->getUserProfile()->getAvatar()->getImageTag(24)}</a>
+                       
+                       <div class="sidebarBoxHeadline">
+                               <h3>
+                                       <a href="{link controller='User' object=$event->getUserProfile()}{/link}" class="userLink" data-user-id="{@$event->getUserProfile()->userID}">{$event->getUserProfile()->username}</a>
+                                       <small class="separatorLeft">{@$event->time|time}</small>
+                               </h3>
+                               <small>{@$event->getTitle()}</small>
+                       </div>
+               </li>
+       {/foreach}
+</ul>
index 98326293c35082f7c2f689f1afb2b80d445e1191..42a7a458f52c77ca3474aa0405d0a587017353d1 100644 (file)
@@ -54,16 +54,6 @@ $sql = "UPDATE       wcf".WCF_N."_user_group_option
 $statement = WCF::getDB()->prepareStatement($sql);
 $statement->execute([1]);
 
-// landing page
-$sql = "UPDATE  wcf".WCF_N."_page
-       SET     isLandingPage = ?
-       WHERE   identifier = ?";
-$statement = WCF::getDB()->prepareStatement($sql);
-$statement->execute([
-       1,
-       'com.woltlab.wcf.Dashboard'
-]);
-
 // get server timezone
 if ($timezone = @date_default_timezone_get()) {
        if ($timezone != 'Europe/London' && in_array($timezone, DateUtil::getAvailableTimezones())) {
index 442713fdd28d8f18c1f47dbae43948d40b8afff6..fbd220d11328ab09e286312f0bb719d07246681d 100644 (file)
@@ -1,22 +1,17 @@
 <?php
 use wcf\data\user\UserEditor;
 use wcf\data\user\UserProfileAction;
-use wcf\system\dashboard\DashboardHandler;
 use wcf\system\WCF;
 
-// set dashboard default values
-DashboardHandler::setDefaultValues('com.woltlab.wcf.user.DashboardPage', array(
-// content
-'com.woltlab.wcf.user.recentActivity' => 1,
-// sidebar
-'com.woltlab.wcf.user.registerButton' => 1,
-'com.woltlab.wcf.user.signedInAs' => 2,
-'com.woltlab.wcf.user.statsSidebar' => 3
-));
-DashboardHandler::setDefaultValues('com.woltlab.wcf.user.MembersListPage', array(
-'com.woltlab.wcf.user.newestMembers' => 1,
-'com.woltlab.wcf.user.mostActiveMembers' => 2
-));
+// set default landing page
+$sql = "UPDATE  wcf".WCF_N."_page
+       SET     isLandingPage = ?
+       WHERE   identifier = ?";
+$statement = WCF::getDB()->prepareStatement($sql);
+$statement->execute([
+       1,
+       'com.woltlab.wcf.Dashboard'
+]);
 
 // update administrator user rank and user online marking
 $editor = new UserEditor(WCF::getUser());
index e77a9bed1ef9eb1830d89f7421e9c046c3848553..dffbeac02f6597f4883049e89a1bf8e56166eb99 100644 (file)
                        </dd>
                </dl>
                
-               <dl{if $errorField == 'className'} class="formError"{/if}>
-                       <dt><label for="className">{lang}wcf.acp.box.className{/lang}</label></dt>
+               <dl{if $errorField == 'controller'} class="formError"{/if}>
+                       <dt><label for="controller">{lang}wcf.acp.box.controller{/lang}</label></dt>
                        <dd>
-                               <input type="text" id="className" name="className" value="{$className}" class="long" />
-                               {if $errorField == 'className'}
+                               <input type="text" id="controller" name="controller" value="{$controller}" class="long" />
+                               {if $errorField == 'controller'}
                                        <small class="innerError">
                                                {if $errorType == 'empty'}
                                                        {lang}wcf.global.form.error.empty{/lang}
                                                {else}
-                                                       {lang}wcf.acp.box.className.error.{@$errorType}{/lang}
+                                                       {lang}wcf.acp.box.controller.error.{@$errorType}{/lang}
                                                {/if}
                                        </small>
                                {/if}
index 35c5889e77c4b1cc2a9e1bff50ff246c6c8abed5..a309a6b53bb927e5e22eeae956cbec63d463c3bf 100644 (file)
@@ -79,10 +79,10 @@ class BoxAddForm extends AbstractForm {
        public $showHeader = 1;
        
        /**
-        * php class name
+        * box controller
         * @var string
         */
-       public $className = '';
+       public $controller = '';
        
        /**
         * box name
@@ -143,7 +143,7 @@ class BoxAddForm extends AbstractForm {
                if (isset($_POST['visibleEverywhere'])) $this->visibleEverywhere = intval($_POST['visibleEverywhere']);
                if (isset($_POST['cssClassName'])) $this->cssClassName = StringUtil::trim($_POST['cssClassName']);
                if (isset($_POST['showHeader'])) $this->showHeader = intval($_POST['showHeader']);
-               if (isset($_POST['className'])) $this->className = StringUtil::trim($_POST['className']);
+               if (isset($_POST['controller'])) $this->controller = StringUtil::trim($_POST['controller']);
                if (isset($_POST['pageIDs']) && is_array($_POST['pageIDs'])) $this->pageIDs = ArrayUtil::toIntegerArray($_POST['pageIDs']);
                
                if (isset($_POST['title']) && is_array($_POST['title'])) $this->title = ArrayUtil::trim($_POST['title']);
@@ -195,11 +195,11 @@ class BoxAddForm extends AbstractForm {
                
                // validate class name
                if ($this->boxType == 'system') {
-                       if (empty($this->className)) {
-                               throw new UserInputException('className');
+                       if (empty($this->controller)) {
+                               throw new UserInputException('controller');
                        }
                        
-                       // @todo check class
+                       // @todo check controller
                }
                
                // validate page ids
@@ -273,7 +273,7 @@ class BoxAddForm extends AbstractForm {
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'cssClassName' => $this->cssClassName,
                        'showHeader' => $this->showHeader,
-                       'className' => $this->className,
+                       'controller' => $this->controller,
                        'identifier' => ''
                ]), 'content' => $content, 'pageIDs' => $this->pageIDs ]);
                $returnValues = $this->objectAction->executeAction();
@@ -291,7 +291,7 @@ class BoxAddForm extends AbstractForm {
                WCF::getTPL()->assign('success', true);
                
                // reset variables
-               $this->boxType = $this->position = $this->cssClassName = $this->className = $this->name = '';
+               $this->boxType = $this->position = $this->cssClassName = $this->controller = $this->name = '';
                $this->showOrder = 0;
                $this->visibleEverywhere = $this->showHeader = 1;
                $this->title = $this->content = $this->images = $this->imageID = [];
@@ -310,7 +310,7 @@ class BoxAddForm extends AbstractForm {
                        'boxType' => $this->boxType,
                        'position' => $this->position,
                        'cssClassName' => $this->cssClassName,
-                       'className' => $this->className,
+                       'controller' => $this->controller,
                        'showOrder' => $this->showOrder,
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'showHeader' => $this->showHeader,
index 2093691a9b54c381c3e1b71ba0c31a85a0c9ba77..817e1adaca7f25933badbdf415562b966ae07316 100644 (file)
@@ -96,7 +96,7 @@ class BoxEditForm extends BoxAddForm {
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'cssClassName' => $this->cssClassName,
                        'showHeader' => $this->showHeader,
-                       'className' => $this->className
+                       'controller' => $this->controller
                ]), 'content' => $content, 'pageIDs' => $this->pageIDs]);
                $this->objectAction->executeAction();
                
@@ -127,7 +127,7 @@ class BoxEditForm extends BoxAddForm {
                        $this->position = $this->box->position;
                        $this->showOrder = $this->box->showOrder;
                        $this->cssClassName = $this->box->cssClassName;
-                       $this->className = $this->box->className;
+                       $this->controller = $this->box->controller;
                        if ($this->box->showHeader) $this->showHeader = 1;
                        if ($this->box->visibleEverywhere) $this->visibleEverywhere = 1;
                        else $this->visibleEverywhere = 0;
index fd32df450eb83c0bd89a5b6e6c8c1757e8cf83d0..3082e4a0c14787855782acdf2f248911a1795aeb 100644 (file)
@@ -30,7 +30,7 @@ use wcf\util\StringUtil;
  * @property-read      integer         $showHeader
  * @property-read      integer         $originIsSystem
  * @property-read      integer         $packageID
- * @property-read      string          $className
+ * @property-read      string          $controller
  * @property-read      integer|null    $menuID
  */
 class Box extends DatabaseObject {
@@ -86,6 +86,12 @@ class Box extends DatabaseObject {
         */
        protected $pageIDs;
        
+       /**
+        * box controller
+        * @var \wcf\system\box\IBoxController
+        */
+       protected $__controller;
+       
        /**
         * Returns true if the active user can delete this box.
         * 
@@ -219,8 +225,20 @@ class Box extends DatabaseObject {
                return !empty($content);
        }
        
+       /**
+        * Returns the box controller.
+        * 
+        * @return      \wcf\system\box\IBoxController
+        */
        public function getController() {
-               // @todo
+               if ($this->__controller === null) {
+                       if ($this->controller && class_exists($this->controller)) {
+                               $this->__controller = new $this->controller;
+                               $this->__controller->setBox($this);
+                       }
+               }
+               
+               return $this->__controller;
        }
        
        /**
diff --git a/wcfsetup/install/files/lib/system/box/AbstractBoxController.class.php b/wcfsetup/install/files/lib/system/box/AbstractBoxController.class.php
new file mode 100644 (file)
index 0000000..ad303db
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\box\Box;
+
+/**
+ * Default implementation for box controllers.
+ *
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.box
+ * @category   Community Framework
+ */
+abstract class AbstractBoxController implements IBoxController {
+       /**
+        * database object of this box
+        * @var Box
+        */
+       protected $box;
+       
+       /**
+        * box content
+        * @var string
+        */
+       protected $content;
+       
+       /**
+        * supported box positions
+        * @var string[]
+        */
+       protected $supportedPositions = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTitle() {
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getContent() {
+               if ($this->content === null) {
+                       $this->content = '';
+                       $this->loadContent();
+               }
+               
+               return $this->content;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasContent() {
+               return !empty($this->getContent());
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getImage() {
+               return null;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasImage() {
+               return false;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasLink() {
+               return false;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getBox() {
+               return $this->box;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function setBox(Box $box) {
+               $this->box = $box;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getSupportedPositions() {
+               if (!empty($this->supportedPositions)) {
+                       return $this->supportedPositions;
+               }
+               
+               return Box::$availablePositions;
+       }
+       
+       /**
+        * Loads the content of this box.
+        */
+       protected abstract function loadContent();
+}
diff --git a/wcfsetup/install/files/lib/system/box/IBoxController.class.php b/wcfsetup/install/files/lib/system/box/IBoxController.class.php
new file mode 100644 (file)
index 0000000..c22f23e
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\box\Box;
+
+/**
+ * Default interface for box controllers.
+ *
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.box
+ * @category   Community Framework
+ */
+interface IBoxController {
+       /**
+        * Returns the title of this box.
+        * 
+        * @return      string
+        */
+       public function getTitle();
+       
+       /**
+        * Returns the content of this box.
+        *
+        * @return      string
+        */
+       public function getContent();
+       
+       /**
+        * Returns false if this box has no content.
+        *
+        * @return      boolean
+        */
+       public function hasContent();
+       
+       /**
+        * Returns the image of this box.
+        *
+        * @return      \wcf\data\media\ViewableMedia
+        */
+       public function getImage();
+       
+       /**
+        * Returns true if this box has an image.
+        *
+        * @return      boolean
+        */
+       public function hasImage();
+       
+       /**
+        * Returns the title link of this box.
+        *
+        * @return      string
+        */
+       public function getLink();
+       
+       /**
+        * Returns true if this box has a title link.
+        *
+        * @return      boolean
+        */
+       public function hasLink();
+       
+       /**
+        * Returns the database object of this box.
+        * 
+        * @return      Box
+        */
+       public function getBox();
+       
+       /**
+        * Sets the database object of this box.
+        *
+        * @param       Box            $box
+        */
+       public function setBox(Box $box);
+       
+       /**
+        * Returns a list of supported box positions.
+        * 
+        * @return      string[]
+        */
+       public function getSupportedPositions();
+}
\ No newline at end of file
diff --git a/wcfsetup/install/files/lib/system/box/RecentActivityBoxController.class.php b/wcfsetup/install/files/lib/system/box/RecentActivityBoxController.class.php
new file mode 100644 (file)
index 0000000..70a3ac8
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\user\activity\event\ViewableUserActivityEventList;
+use wcf\system\request\LinkHandler;
+use wcf\system\user\activity\event\UserActivityEventHandler;
+use wcf\system\WCF;
+
+/**
+ * Box for recent activities.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.box
+ * @category   Community Framework
+ */
+class RecentActivityBoxController extends AbstractBoxController {
+       /**
+        * @inheritDoc
+        */
+       protected $supportedPositions = ['contentTop', 'contentBottom', 'sidebarLeft', 'sidebarRight'];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTitle() {
+               return WCF::getLanguage()->get('wcf.user.recentActivity');
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return LinkHandler::getInstance()->getLink('RecentActivityList');
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasLink() {
+               return true;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function loadContent() {
+               if ($this->getBox()->position == 'contentTop' || $this->getBox()->position == 'contentBottom') {
+                       $canFilterByFollowedUsers = $filteredByFollowedUsers = false;
+                       if (WCF::getUser()->userID && count(WCF::getUserProfileHandler()->getFollowingUsers())) {
+                               $canFilterByFollowedUsers = true;
+                       }
+                       
+                       $eventList = new ViewableUserActivityEventList();
+                       if ($canFilterByFollowedUsers && WCF::getUser()->recentActivitiesFilterByFollowing) {
+                               $filteredByFollowedUsers = true;
+                               $eventList->getConditionBuilder()->add('user_activity_event.userID IN (?)', [WCF::getUserProfileHandler()->getFollowingUsers()]);
+                       }
+                       $eventList->sqlLimit = RECENT_ACTIVITY_ITEMS;
+                       $eventList->readObjects();
+                       $lastEventTime = $eventList->getLastEventTime();
+                       
+                       // removes orphaned and non-accessable events
+                       UserActivityEventHandler::validateEvents($eventList);
+                       
+                       if (count($eventList) || $filteredByFollowedUsers) {
+                               WCF::getTPL()->assign([
+                                       'canFilterByFollowedUsers' => $canFilterByFollowedUsers,
+                                       'eventList' => $eventList, 'lastEventTime' => $lastEventTime,
+                                       'filteredByFollowedUsers' => $filteredByFollowedUsers
+                               ]);
+                               
+                               $this->content = WCF::getTPL()->fetch('boxRecentActivity');
+                       }
+               }
+               else {
+                       $eventList = new ViewableUserActivityEventList();
+                       $eventList->sqlLimit = RECENT_ACTIVITY_SIDEBAR_ITEMS;
+                       $eventList->readObjects();
+                       
+                       // removes orphaned and non-accessable events
+                       UserActivityEventHandler::validateEvents($eventList);
+                       
+                       if (count($eventList)) {
+                               WCF::getTPL()->assign([
+                                       'eventList' => $eventList
+                               ]);
+                               
+                               $this->content = WCF::getTPL()->fetch('boxRecentActivitySidebar');
+                       }
+               }
+       }
+}
index af195827e1c33be47655da647049561f397edec5..710422cc6c7da91b2d84263d710d594dbbac4beb 100644 (file)
@@ -126,7 +126,7 @@ class PackageUninstallationDispatcher extends PackageInstallationDispatcher {
         */
        protected function executeUninstallScript() {
                // check if uninstall script file for the uninstalled package exists
-               $uninstallScript = WCF_DIR.'acp/uninstall/'.$this->package->package.'.php';
+               $uninstallScript = WCF_DIR.'acp/uninstall/'.$this->getPackage()->package.'.php';
                if (file_exists($uninstallScript)) {
                        include($uninstallScript);
                }
index 07b67f444139d09f676c48cbb438c0e91c963127..33672ddc6976d1fc957c216ff3f812322540aaaf 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\system\package\plugin;
 use wcf\data\box\Box;
 use wcf\data\box\BoxEditor;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\WCF;
 
@@ -26,6 +27,12 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         */
        public $tagName = 'box';
        
+       /**
+        * visibility exceptions per box
+        * @var string[]
+        */
+       public $visibilityExceptions = [];
+       
        /**
         * @inheritDoc
         */
@@ -84,6 +91,13 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                                'title' => $children['title']
                        ];
                }
+               else if ($element->tagName === 'visibilityExceptions') {
+                       $elements['visibilityExceptions'] = [];
+                       /** @var \DOMElement $child */
+                       foreach ($xpath->query('child::*', $element) as $child) {
+                               $elements['visibilityExceptions'][] = $child->nodeValue;
+                       }
+               }
                else {
                        $elements[$element->tagName] = $nodeValue;
                }
@@ -95,7 +109,7 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         */
        protected function prepareImport(array $data) {
                $boxType = $data['elements']['boxType'];
-               $className = '';
+               $controller = '';
                $identifier = $data['attributes']['identifier'];
                $isMultilingual = false;
                $position = $data['elements']['position'];
@@ -106,15 +120,16 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                
                switch ($boxType) {
                        case 'system':
-                               if (empty($data['elements']['className'])) {
-                                       throw new SystemException("Missing required element 'classname' for 'system'-type box '{$identifier}'");
+                               if (empty($data['elements']['controller'])) {
+                                       throw new SystemException("Missing required element 'controller' for 'system'-type box '{$identifier}'");
                                }
                                
-                               $className = $data['elements']['className'];
+                               $controller = $data['elements']['controller'];
                                break;
                        
                        case 'html':
                        case 'text':
+                       case 'tpl':
                                if (empty($data['elements']['content'])) {
                                        throw new SystemException("Missing required 'content' element(s) for box '{$identifier}'");
                                }
@@ -139,6 +154,10 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                                break;
                }
                
+               if (!empty($data['elements']['visibilityExceptions'])) {
+                       $this->visibilityExceptions[$identifier] = $data['elements']['visibilityExceptions'];
+               }
+               
                return [
                        'identifier' => $identifier,
                        'name' => $this->getI18nValues($data['elements']['name'], true),
@@ -150,7 +169,7 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        'cssClassName' => (!empty($data['elements']['cssClassName'])) ? $data['elements']['cssClassName'] : '',
                        'showHeader' => (!empty($data['elements']['showHeader'])) ? 1 : 0,
                        'originIsSystem' => 1,
-                       'className' => $className
+                       'controller' => $controller
                ];
        }
        
@@ -204,4 +223,57 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                
                return parent::import($row, $data);
        }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function postImport() {
+               if (empty($this->visibilityExceptions)) return;
+               
+               // get all boxes belonging to the identifiers
+               $conditions = new PreparedStatementConditionBuilder();
+               $conditions->add("identifier IN (?)", [array_keys($this->visibilityExceptions)]);
+               $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;
+               }
+               
+               // save visibility exceptions
+               $sql = "DELETE FROM     wcf".WCF_N."_box_to_page
+                       WHERE           boxID = ?";
+               $deleteStatement = WCF::getDB()->prepareStatement($sql);                
+               $sql = "INSERT IGNORE   wcf".WCF_N."_box_to_page
+                                       (boxID, pageID, visible)
+                       VALUES          (?, ?, ?)";
+               $insertStatement = WCF::getDB()->prepareStatement($sql);
+               foreach ($this->visibilityExceptions as $boxIdentifier => $pages) {
+                       // delete old visibility exceptions
+                       $deleteStatement->execute([$boxes[$boxIdentifier]->boxID]);
+                       
+                       // get page ids
+                       $conditionBuilder = new PreparedStatementConditionBuilder();
+                       $conditionBuilder->add('identifier IN (?)', array($pages));
+                       $sql = "SELECT  pageID
+                               FROM    wcf".WCF_N."_page
+                               ".$conditionBuilder;
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute($conditionBuilder->getParameters());
+                       $pageIDs = [];
+                       while ($row = $statement->fetchArray()) {
+                               $pageIDs[] = $row['pageID'];
+                       }
+                       
+                       // save page ids
+                       foreach ($pageIDs as $pageID) {
+                               $insertStatement->execute([$boxes[$boxIdentifier]->boxID, $pageID, ($boxes[$boxIdentifier]->visibleEverywhere ? 0 : 1)]);
+                       }
+               }
+       }
 }
index 27f3bb872eaed4697858899de9aca4cbe440f10b..82cec96692ea14ce480187aacc4d1da0ac827fee 100644 (file)
@@ -27,6 +27,12 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         */
        public $boxData = [];
        
+       /**
+        * visibility exceptions per box
+        * @var string[]
+        */
+       public $visibilityExceptions = [];
+       
        /**
         * @inheritDoc
         */
@@ -88,6 +94,13 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                                        
                                        $elements['box']['name'][$element->getAttribute('language')] = $element->nodeValue;
                                }
+                               else if ($child->tagName === 'visibilityExceptions') {
+                                       $elements['box']['visibilityExceptions'] = [];
+                                       /** @var \DOMElement $child */
+                                       foreach ($xpath->query('child::*', $child) as $child2) {
+                                               $elements['box']['visibilityExceptions'][] = $child2->nodeValue;
+                                       }
+                               }
                                else {
                                        $elements['box'][$child->tagName] = $child->nodeValue;
                                }
@@ -126,6 +139,10 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                                'packageID' => $this->installation->getPackageID()
                        ];
                        
+                       if (!empty($data['elements']['box']['visibilityExceptions'])) {
+                               $this->visibilityExceptions[$identifier] = $data['elements']['box']['visibilityExceptions'];
+                       }
+                       
                        unset($data['elements']['box']);
                }
                
@@ -199,22 +216,57 @@ class MenuPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        $menus[$menu->identifier] = $menu;
                }
                
+               // handle visibility exceptions
+               $sql = "DELETE FROM     wcf".WCF_N."_box_to_page
+                       WHERE           boxID = ?";
+               $deleteStatement = WCF::getDB()->prepareStatement($sql);
+               $sql = "INSERT IGNORE   wcf".WCF_N."_box_to_page
+                                       (boxID, pageID, visible)
+                       VALUES          (?, ?, ?)";
+               $insertStatement = WCF::getDB()->prepareStatement($sql);
                foreach ($this->boxData as $identifier => $data) {
                        // connect box with menu
                        if (isset($menus[$identifier])) {
                                $data['menuID'] = $menus[$identifier]->menuID;
                        }
                        
+                       $box = null;
                        if (isset($boxes[$identifier])) {
+                               $box = $boxes[$identifier];
+                               
+                               // delete old visibility exceptions
+                               $deleteStatement->execute([$box->boxID]);
+                               
                                // skip both 'identifier' and 'packageID' as these properties are immutable
                                unset($data['identifier']);
                                unset($data['packageID']);
                                
-                               $boxEditor = new BoxEditor($boxes[$identifier]);
+                               $boxEditor = new BoxEditor($box);
                                $boxEditor->update($data);
                        }
                        else {
-                               BoxEditor::create($data);
+                               $box = BoxEditor::create($data);
+                       }
+                       
+                       // save visibility exceptions
+                       if (!empty($this->visibilityExceptions[$identifier])) {
+                               // get page ids
+                               $conditionBuilder = new PreparedStatementConditionBuilder();
+                               $conditionBuilder->add('identifier IN (?)', [$this->visibilityExceptions[$identifier]]);
+                               $sql = "SELECT  pageID
+                                       FROM    wcf" . WCF_N . "_page
+                                       " . $conditionBuilder;
+                               $statement = WCF::getDB()->prepareStatement($sql);
+                               $statement->execute($conditionBuilder->getParameters());
+                               $pageIDs = [];
+                               while ($row = $statement->fetchArray()) {
+                                       $pageIDs[] = $row['pageID'];
+                               }
+                               
+                               // save page ids
+                               foreach ($pageIDs as $pageID) {
+                                       $insertStatement->execute([$box->boxID, $pageID, ($box->visibleEverywhere ? 0 : 1)]);
+                               }
                        }
                }
        }
index cd8bed25b1f1712d31493bfcc7d3453b0095665b..13d26ac66e81df346576496b6c778a65e08b1601 100644 (file)
@@ -88,6 +88,7 @@ class PagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         */
        protected function prepareImport(array $data) {
                $isStatic = false;
+               
                if (!empty($data['elements']['content'])) {
                        $isStatic = true;
                        
@@ -260,18 +261,23 @@ class PagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        WCF::getDB()->beginTransaction();
                        foreach ($this->content as $pageID => $contentData) {
                                foreach ($contentData as $languageCode => $content) {
-                                       $language = LanguageFactory::getInstance()->getLanguageByCode($languageCode);
-                                       if ($language !== null) {
-                                               $statement->execute([
-                                                       $pageID,
-                                                       $language->languageID,
-                                                       $content['title'],
-                                                       $content['content'],
-                                                       $content['metaDescription'],
-                                                       $content['metaKeywords'],
-                                                       $content['customURL']
-                                               ]);
+                                       $languageID = null;
+                                       if ($languageCode != '') {
+                                               $language = LanguageFactory::getInstance()->getLanguageByCode($languageCode);
+                                               if ($language === null) continue;
+                                               
+                                               $languageID = $language->languageID;
                                        }
+                                       
+                                       $statement->execute([
+                                               $pageID,
+                                               $languageID,
+                                               $content['title'],
+                                               $content['content'],
+                                               $content['metaDescription'],
+                                               $content['metaKeywords'],
+                                               $content['customURL']
+                                       ]);
                                }
                        }
                        WCF::getDB()->commitTransaction();
index d925d0da8e300dec8526a487c57eaa77780a9a8c..5ed7d56affcbd1732798e4c54d28658385e7b7ba 100644 (file)
@@ -237,7 +237,7 @@ 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 '',
+       controller VARCHAR(255) NOT NULL DEFAULT '',
        menuID INT(10) NULL
 );