Add support for conditioned boxes and object list boxes
authorMatthias Schmidt <gravatronics@live.com>
Wed, 4 May 2016 13:04:18 +0000 (15:04 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Wed, 4 May 2016 13:04:18 +0000 (15:04 +0200)
50 files changed:
com.woltlab.wcf/box.xml
com.woltlab.wcf/objectType.xml
com.woltlab.wcf/objectTypeDefinition.xml
com.woltlab.wcf/option.xml
com.woltlab.wcf/templates/boxUserList.tpl [new file with mode: 0644]
constants.php
wcfsetup/install/files/acp/post_install.php
wcfsetup/install/files/acp/templates/boxAdd.tpl
wcfsetup/install/files/acp/templates/boxConditions.tpl [new file with mode: 0644]
wcfsetup/install/files/js/WoltLab/WCF/Acp/Ui/Box/Controller/Handler.js [new file with mode: 0644]
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/data/box/BoxAction.class.php
wcfsetup/install/files/lib/data/object/type/ObjectType.class.php
wcfsetup/install/files/lib/system/box/AbstractBoxController.class.php
wcfsetup/install/files/lib/system/box/AbstractDatabaseObjectListBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/BoxHandler.class.php
wcfsetup/install/files/lib/system/box/FollowingsOnlineBoxController.class.php
wcfsetup/install/files/lib/system/box/IBoxController.class.php
wcfsetup/install/files/lib/system/box/IConditionBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/MostActiveMembersBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/box/MostLikedMembersBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/box/NewestMembersBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/box/PageCommentListBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/PageCommentsBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/box/PaidSubscriptionsBoxController.class.php
wcfsetup/install/files/lib/system/box/RecentActivityBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/RegisterButtonBoxController.class.php
wcfsetup/install/files/lib/system/box/SignedInAsBoxController.class.php
wcfsetup/install/files/lib/system/box/StaffOnlineBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/box/StaffOnlineListBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/StatisticsBoxController.class.php
wcfsetup/install/files/lib/system/box/TagCloudBoxController.class.php
wcfsetup/install/files/lib/system/box/TodaysBirthdaysBoxController.class.php
wcfsetup/install/files/lib/system/box/TodaysFollowingBirthdaysBoxController.class.php
wcfsetup/install/files/lib/system/box/UserListBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/UserOnlineListBoxController.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/box/UsersOnlineBoxController.class.php [deleted file]
wcfsetup/install/files/lib/system/cache/builder/AbstractSortedUserCacheBuilder.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/cache/builder/MostActiveMembersCacheBuilder.class.php
wcfsetup/install/files/lib/system/cache/builder/MostLikedMembersCacheBuilder.class.php
wcfsetup/install/files/lib/system/cache/builder/NewestMembersCacheBuilder.class.php
wcfsetup/install/files/lib/system/condition/AbstractCheckboxCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/ICondition.class.php
wcfsetup/install/files/lib/system/package/plugin/BoxPackageInstallationPlugin.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index cccf7d63a7182e559fef757eb5166ef8b77f6557..fdbfc9142999e98b7ad8e532ca04c20a4af8c609 100644 (file)
@@ -3,55 +3,85 @@
        <import>
                <box identifier="com.woltlab.wcf.RecentActivity">
                        <name language="de">Letzte Aktivitäten</name>
-                       <name language="en">Recent Activity</name>
+                       <name language="en">Recent Activities</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\RecentActivityBoxController</controller>
+                       <objectType>com.woltlab.wcf.recentActivityList</objectType>
                        <position>contentBottom</position>
                        <showHeader>0</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
                        <visibilityExceptions>
                                <page>com.woltlab.wcf.Dashboard</page>
                        </visibilityExceptions>
+                       <limit>10</limit>
+                       
+                       <content language="de">
+                               <title>Letzte Aktivitäten</title>
+                       </content>
+                       <content language="en">
+                               <title>Recent Activities</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.RecentActivitySidebar">
                        <name language="de">Letzte Aktivitäten (Sidebar)</name>
-                       <name language="en">Recent Activity (Sidebar)</name>
+                       <name language="en">Recent Activities (Sidebar)</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\RecentActivityBoxController</controller>
+                       <objectType>com.woltlab.wcf.recentActivityList</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
                        <visibilityExceptions>
                                <page>com.woltlab.wcf.Dashboard</page>
                        </visibilityExceptions>
+                       <limit>10</limit>
+                       
+                       <content language="de">
+                               <title>Letzte Aktivitäten</title>
+                       </content>
+                       <content language="en">
+                               <title>Recent Activities</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.PaidSubscriptions">
                        <name language="de">Bezahlte Mitgliedschaften</name>
                        <name language="en">Paid Subscriptions</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\PaidSubscriptionsBoxController</controller>
+                       <objectType>com.woltlab.wcf.paidSubscriptions</objectType>
                        <position>contentBottom</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Bezahlte Mitgliedschaften</title>
+                       </content>
+                       <content language="en">
+                               <title>Paid Subscriptions</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.PaidSubscriptionsSidebar">
                        <name language="de">Bezahlte Mitgliedschaften (Sidebar)</name>
                        <name language="en">Paid Subscriptions (Sidebar)</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\PaidSubscriptionsBoxController</controller>
+                       <objectType>com.woltlab.wcf.paidSubscriptions</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Bezahlte Mitgliedschaften</title>
+                       </content>
+                       <content language="en">
+                               <title>Paid Subscriptions</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.RegisterButton">
                        <name language="de">Registrierungs-Button</name>
                        <name language="en">Register Button</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\RegisterButtonBoxController</controller>
+                       <objectType>com.woltlab.wcf.registerButton</objectType>
                        <position>sidebarRight</position>
                        <showHeader>0</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
                        <visibilityExceptions>
                                <page>com.woltlab.wcf.Dashboard</page>
                        </visibilityExceptions>
+                       
+                       <content language="de">
+                               <title>Registrierung</title>
+                       </content>
+                       <content language="en">
+                               <title>Registration</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.SignedInAs">
                        <name language="de">Angemeldet als</name>
                        <name language="en">Signed In As</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\SignedInAsBoxController</controller>
+                       <objectType>com.woltlab.wcf.signedInAs</objectType>
                        <position>sidebarRight</position>
                        <showHeader>0</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
                        <visibilityExceptions>
                                <page>com.woltlab.wcf.Dashboard</page>
                        </visibilityExceptions>
+                       
+                       <!-- TODO: content title -->
                </box>
                
                <box identifier="com.woltlab.wcf.Statistics">
                        <name language="de">Statistiken</name>
                        <name language="en">Statistics</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\StatisticsBoxController</controller>
+                       <objectType>com.woltlab.wcf.statistics</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
                        <visibilityExceptions>
                                <page>com.woltlab.wcf.Dashboard</page>
                        </visibilityExceptions>
+                       
+                       <content language="de">
+                               <title>Statistiken</title>
+                       </content>
+                       <content language="en">
+                               <title>Statistics</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.NewestMembers">
                        <name language="de">Neueste Mitglieder</name>
                        <name language="en">Newest Members</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\NewestMembersBoxController</controller>
+                       <objectType>com.woltlab.wcf.userList</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       <sortField>registrationDate</sortField>
+                       <sortOrder>DESC</sortOrder>
+                       
+                       <content language="de">
+                               <title>Neueste Mitglieder</title>
+                       </content>
+                       <content language="en">
+                               <title>Newest Members</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.MostActiveMembers">
                        <name language="de">Aktivste Mitglieder</name>
                        <name language="en">Most Active Members</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\MostActiveMembersBoxController</controller>
+                       <objectType>com.woltlab.wcf.userList</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       <sortField>activityPoints</sortField>
+                       <sortOrder>DESC</sortOrder>
+                       
+                       <content language="de">
+                               <title>Aktivste Mitglieder</title>
+                       </content>
+                       <content language="en">
+                               <title>Most Active Members</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.MostLikedMembers">
                        <name language="de">Mitglieder mit den meisten Likes</name>
                        <name language="en">Most Liked Members</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\MostLikedMembersBoxController</controller>
+                       <objectType>com.woltlab.wcf.userList</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       <sortField>likesReceived</sortField>
+                       <sortOrder>DESC</sortOrder>
+                       
+                       <content language="de">
+                               <title>Mitglieder mit den meisten Likes</title>
+                       </content>
+                       <content language="en">
+                               <title>Most Liked Members</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.TodaysBirthdays">
                        <name language="de">Heutige Geburtstage</name>
                        <name language="en">Today’s Birthdays</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\TodaysBirthdaysBoxController</controller>
+                       <objectType>com.woltlab.wcf.todaysBirthdays</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Heutige Geburtstage</title>
+                       </content>
+                       <content language="en">
+                               <title>Today’s Birthdays</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.TodaysFollowingBirthdays">
                        <name language="de">Heutige Geburtstage von Nutzern, denen der Nutzer folgt</name>
                        <name language="en">Today’s Birthdays of Users the User Follows</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\TodaysFollowingBirthdaysBoxController</controller>
+                       <objectType>com.woltlab.wcf.todaysFollowingBirthdays</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <!-- TODO: content title -->
                </box>
                
                <box identifier="com.woltlab.wcf.UsersOnline">
                        <name language="de">Benutzer online</name>
                        <name language="en">Users Online</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\UsersOnlineBoxController</controller>
+                       <objectType>com.woltlab.wcf.userOnlineList</objectType>
                        <position>footerBoxes</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Benutzer online</title>
+                       </content>
+                       <content language="en">
+                               <title>Users Online</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.UsersOnlineSidebar">
                        <name language="de">Benutzer online (Sidebar)</name>
                        <name language="en">Users Online (Sidebar)</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\UsersOnlineBoxController</controller>
+                       <objectType>com.woltlab.wcf.userOnlineList</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Benutzer online</title>
+                       </content>
+                       <content language="en">
+                               <title>Users Online</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.StaffOnline">
                        <name language="de">Team-Mitglieder online</name>
                        <name language="en">Staff-Members Online</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\StaffOnlineBoxController</controller>
+                       <objectType>com.woltlab.wcf.staffOnlineList</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Team-Mitglieder online</title>
+                       </content>
+                       <content language="en">
+                               <title>Staff-Members Online</title>
+                       </content>
                </box>
                
                <box identifier="com.woltlab.wcf.FollowingsOnline">
                        <name language="de">Benutzer online, denen der Nutzer folgt</name>
                        <name language="en">Users Online the User Follows</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\FollowingsBoxController</controller>
+                       <objectType>com.woltlab.wcf.followingsOnline</objectType>
                        <position>sidebarRight</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       <limit>10</limit>
+                       
+                       <!-- TODO: content title -->
                </box>
                
                <box identifier="com.woltlab.wcf.PageComments">
-                       <name language="de">Kommentare</name>
+                       <name language="de">Seiten-Kommentare</name>
                        <name language="en">Page Comments</name>
                        <boxType>system</boxType>
-                       <controller>wcf\system\box\PageCommentsBoxController</controller>
+                       <objectType>com.woltlab.wcf.pageCommentList</objectType>
                        <position>contentBottom</position>
                        <showHeader>1</showHeader>
                        <visibleEverywhere>0</visibleEverywhere>
+                       
+                       <content language="de">
+                               <title>Kommentare</title>
+                       </content>
+                       <content language="en">
+                               <title>Comments</title>
+                       </content>
                </box>
        </import>
 </data>
index 08badc908f08ea86cb4b2ef9c3ab772827a1f255..d2ba8179fccfc5a6e90638363189095c366fff79 100644 (file)
                </type>
                <!-- /user search conditions -->
                
+               <!-- box controllers -->
+               <type>
+                       <name>com.woltlab.wcf.recentActivityList</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\RecentActivityListBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.paidSubscriptions</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\PaidSubscriptionsBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.registerButton</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\RegisterButtonBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.signedInAs</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\SignedInAsBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.statistics</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\StatisticsBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.userList</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\userListBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.userOnlineList</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\UserOnlineListBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.staffOnlineList</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\StaffOnlineListBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.todaysBirthdays</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\TodaysBirthdaysBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.todaysFollowingBirthdays</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\TodaysFollowingBirthdaysBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.followingsOnline</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\FollowingsOnlineBoxController</classname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.pageCommentList</name>
+                       <definitionname>com.woltlab.wcf.boxController</definitionname>
+                       <classname>wcf\system\box\PageCommentListBoxController</classname>
+               </type>
+               <!-- /box controllers -->
+               
                <!-- deprecated -->
                <type>
                        <name>com.woltlab.wcf.page.controller</name>
index 3902359b0a1fb52a550c78d0aaf45060e78d3071..4dc1a6b34028588cb78cfa1069ecc192d0e001f0 100644 (file)
                        <interfacename><![CDATA[wcf\system\bulk\processing\IBulkProcessingAction]]></interfacename>
                </definition>
                
+               <definition>
+                       <name>com.woltlab.wcf.boxController</name>
+                       <interfacename>wcf\system\box\IBoxController</interfacename>
+               </definition>
+               
                <!-- deprecated -->
                <definition>
                        <name>com.woltlab.wcf.user.online.location</name>
index 2af2a7bab3f322c1f49cb70a1246be31c1e93d57..c6dbdd3bd9344fc554abb72de36d4dcb5528c897 100644 (file)
                                <category name="dashboard.content">
                                        <parent>dashboard</parent>
                                </category>
-                                       <category name="dashboard.content.recentActivities">
-                                               <parent>dashboard.content</parent>
-                                       </category>
                                
                                <category name="dashboard.sidebar">
                                        <parent>dashboard</parent>
                                </category>
-                                       <category name="dashboard.sidebar.recentActivities">
-                                               <parent>dashboard.sidebar</parent>
-                                       </category>
                        
                        <!-- cms -->
                        <category name="cms"></category>
@@ -1348,24 +1342,6 @@ DESC:wcf.global.sortOrder.descending]]></selectoptions>
                        </option>
                        <!-- /user.cleanup -->
                        
-                       <!-- dashboard TODO -->
-                       <option name="recent_activity_items">
-                               <categoryname>dashboard.content.recentActivities</categoryname>
-                               <optiontype>integer</optiontype>
-                               <defaultvalue>10</defaultvalue>
-                               <minvalue>5</minvalue>
-                               <maxvalue>100</maxvalue>
-                       </option>
-                       
-                       <option name="recent_activity_sidebar_items">
-                               <categoryname>dashboard.sidebar.recentActivities</categoryname>
-                               <optiontype>integer</optiontype>
-                               <defaultvalue>5</defaultvalue>
-                               <minvalue>5</minvalue>
-                               <maxvalue>50</maxvalue>
-                       </option>
-                       <!-- /dashboard -->
-                       
                        <option name="like_allow_for_own_content">
                                <categoryname>message.general.likes</categoryname>
                                <optiontype>boolean</optiontype>
@@ -1550,5 +1526,10 @@ DESC:wcf.global.sortOrder.descending]]></selectoptions>
                <option name="enable_pluginstore_widget" />
                <option name="mail_use_formatted_address" />
                <option name="mail_debug_logfile_path" />
+               <option name="recent_activity_items" />
+               <option name="recent_activity_sidebar_items" />
+               
+               <optioncategory name="dashboard.content.recentActivities" />
+               <optioncategory name="dashboard.sidebar.recentActivities" />
        </delete>
 </data>
diff --git a/com.woltlab.wcf/templates/boxUserList.tpl b/com.woltlab.wcf/templates/boxUserList.tpl
new file mode 100644 (file)
index 0000000..1b09289
--- /dev/null
@@ -0,0 +1,16 @@
+<ul class="sidebarBoxList">
+       {foreach from=$boxUsers item=boxUser}
+               <li class="box24">
+                       <a href="{link controller='User' object=$boxUser}{/link}">{@$boxUser->getAvatar()->getImageTag(24)}</a>
+                       
+                       <div class="sidebarBoxHeadline">
+                               <h3><a href="{link controller='User' object=$boxUser}{/link}" class="userLink" data-user-id="{@$boxUser->userID}">{$boxUser->username}</a></h3>
+                               {capture assign='__boxUserLanguageItem'}{lang __optional=true}wcf.user.boxList.description.{$boxSortField}{/lang}{/capture}
+                               {if $__boxUserLanguageItem}
+                                       <small>{@$__boxUserLanguageItem}</small>
+                               {* TODO: else? *}
+                               {/if}
+                       </div>
+               </li>
+       {/foreach}
+</ul>
index 7f735f83efefba59a4d4be3d4a1b0115dc1ced0a..4efae3b4580a60c252f42352136ea953ed25b79e 100644 (file)
@@ -191,8 +191,6 @@ define('USERS_ONLINE_RECORD_TIME', 0);
 define('USER_CLEANUP_NOTIFICATION_LIFETIME', 14);
 define('USER_CLEANUP_ACTIVITY_EVENT_LIFETIME', 60);
 define('USER_CLEANUP_PROFILE_VISITOR_LIFETIME', 60);
-define('RECENT_ACTIVITY_ITEMS', 10);
-define('RECENT_ACTIVITY_SIDEBAR_ITEMS', 5);
 define('LIKE_ALLOW_FOR_OWN_CONTENT', 1);
 define('LIKE_ENABLE_DISLIKE', 1);
 define('LIKE_SHOW_SUMMARY', 1);
index fbd220d11328ab09e286312f0bb719d07246681d..c606ef1f3b1360e077d90e73f81984becf6c32ae 100644 (file)
@@ -4,9 +4,9 @@ use wcf\data\user\UserProfileAction;
 use wcf\system\WCF;
 
 // set default landing page
-$sql = "UPDATE  wcf".WCF_N."_page
-       SET     isLandingPage = ?
-       WHERE   identifier = ?";
+$sql = "UPDATE wcf".WCF_N."_page
+       SET     isLandingPage = ?
+       WHERE   identifier = ?";
 $statement = WCF::getDB()->prepareStatement($sql);
 $statement->execute([
        1,
@@ -15,7 +15,7 @@ $statement->execute([
 
 // update administrator user rank and user online marking
 $editor = new UserEditor(WCF::getUser());
-$action = new UserProfileAction(array($editor), 'updateUserRank');
+$action = new UserProfileAction([$editor], 'updateUserRank');
 $action->executeAction();
-$action = new UserProfileAction(array($editor), 'updateUserOnlineMarking');
+$action = new UserProfileAction([$editor], 'updateUserOnlineMarking');
 $action->executeAction();
index 5f190776f2741f5b3e534f1a67880ad4a17cd923..31c29761770b7540392e28b17dd2fbda64a3f51b 100644 (file)
@@ -4,7 +4,9 @@
        <script data-relocate="true">
                {include file='mediaJavaScript'}
                
-               require(['WoltLab/WCF/Media/Manager/Select'], function(MediaManagerSelect) {
+               require(['WoltLab/WCF/Acp/Ui/Box/Controller/Handler', 'WoltLab/WCF/Media/Manager/Select'], function(AcpUiBoxControllerHandler, MediaManagerSelect) {
+                       AcpUiBoxControllerHandler.init({if $boxController}{@$boxController->objectTypeID}{/if});
+                       
                        new MediaManagerSelect({
                                dialogTitle: '{lang}wcf.acp.box.image.dialog.title{/lang}',
                                fileTypeFilters: {
                        </dd>
                </dl>
                
+               <dl id="boxControllerContainer"{if $errorField == 'boxControllerID'} class="formError"{/if}{if !$boxController} style="display: none;"{/if}>
+                       <dt><label for="boxControllerID">{lang}wcf.acp.box.boxController{/lang}</label></dt>
+                       <dd>
+                               <select name="boxControllerID" id="boxControllerID">
+                                       {foreach from=$availableBoxControllers item=availableBoxController}
+                                               <option value="{@$availableBoxController->objectTypeID}"{if $boxController && $availableBoxController->objectTypeID == $boxController->objectTypeID} selected="selected"{/if}>{lang}wcf.acp.box.boxController.{@$availableBoxController->objectType}{/lang}</option>
+                                       {/foreach}
+                               </select>
+                               
+                               {if $errorField == 'boxType'}
+                                       <small class="innerError">
+                                               {if $errorType == 'empty'}
+                                                       {lang}wcf.global.form.error.empty{/lang}
+                                               {else}
+                                                       {lang}wcf.acp.box.boxController.error.{@$errorType}{/lang}
+                                               {/if}
+                                       </small>
+                               {/if}
+                       </dd>
+               </dl>
+               
                <dl{if $errorField == 'position'} class="formError"{/if}>
                        <dt><label for="position">{lang}wcf.acp.box.position{/lang}</label></dt>
                        <dd>
                        </dd>
                </dl>
                
-               <dl{if $errorField == 'controller'} class="formError"{/if}>
-                       <dt><label for="controller">{lang}wcf.acp.box.controller{/lang}</label></dt>
-                       <dd>
-                               <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.controller.error.{@$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
                <dl>
                        <dt></dt>
                        <dd>
                {event name='linkFields'}
        </section>
        
+       <div id="boxConditions">
+               {if $boxController && $boxController->getProcessor()|is_subclass_of:'wcf\system\box\IConditionBoxController'}
+                       {@$boxController->getProcessor()->getConditionsTemplate()}
+               {/if}
+       </div>
+       
        {if !$isMultilingual}
                <section class="section">
-                       <h2 class="sectionTitle">content</h2>
+                       <h2 class="sectionTitle">{* TODO *}content</h2>
                        
                        {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
                                <dl{if $errorField == 'image'} class="formError"{/if}>
diff --git a/wcfsetup/install/files/acp/templates/boxConditions.tpl b/wcfsetup/install/files/acp/templates/boxConditions.tpl
new file mode 100644 (file)
index 0000000..94547f9
--- /dev/null
@@ -0,0 +1,53 @@
+{if !$errorField|isset}{assign var=errorField value=''}{/if}
+
+<section class="section">
+       <h2 class="sectionTitle">{lang}wcf.acp.box.settings{/lang}</h2>
+       
+       {if $defaultLimit !== null}
+               <dl{if $errorField === 'limit'} class="formError"{/if}>
+                       <dt>{lang}wcf.acp.box.settings.limit{/lang}</dt>
+                       <dd>
+                               <input type="number" name="limit" id="limit" value="{$limit}" min="{$minimumLimit}"{if $maximumLimit !== null} max="{$maximumLimit}"{/if} class="tiny" />
+                               {if $errorField === 'limit'}
+                                       <small class="innerError">
+                                               {if $errorType === 'lessThan'}
+                                                       {lang lessThan=$maximumLimit+1}wcf.global.form.error.lessThan{/lang}
+                                               {elseif $errorType === 'greaterThan'}
+                                                       {lang greaterThan=$minimumLimit-1}wcf.global.form.error.greaterThan{/lang}
+                                               {/if}
+                                       </small>
+                               {/if}
+                       </dd>
+               </dl>
+       {/if}
+       
+       {if !$validSortFields|empty}
+               <dl{if $errorField === 'sorting'} class="formError"{/if}>
+                       <dt>{lang}wcf.global.sorting{/lang}</dt>
+                       <dd>
+                               <select name="sortField" id="sortField">
+                                       {foreach from=$validSortFields item=validSortField}
+                                               <option value="{$validSortField}"{if $validSortField == $sortField} selected="selected"{/if}>{lang}{$sortFieldLanguageItemPrefix}.{$validSortField}{/lang}</option>
+                                       {/foreach}
+                               </select>
+                               
+                               <select name="sortOrder" id="sortOrder">
+                                       <option value="ASC"{if $sortOrder == 'ASC'} selected="selected"{/if}>{lang}wcf.global.sortOrder.ascending{/lang}</option>
+                                       <option value="DESC"{if $sortOrder == 'DESC'} selected="selected"{/if}>{lang}wcf.global.sortOrder.descending{/lang}</option>
+                               </select>
+                               
+                               {if $errorField === 'sorting'}
+                                       <small class="innerError">
+                                               {lang}wcf.global.sorting.error.{$errorType}{/lang}
+                                       </small>
+                               {/if}
+                       </dd>
+               </dl>
+       {/if}
+       
+       {event name='fields'}
+       
+       {foreach from=$conditionObjectTypes item=conditionObjectType}
+               {@$conditionObjectType->getProcessor()->getHtml()}
+       {/foreach}
+</section>
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Acp/Ui/Box/Controller/Handler.js b/wcfsetup/install/files/js/WoltLab/WCF/Acp/Ui/Box/Controller/Handler.js
new file mode 100644 (file)
index 0000000..860ef59
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Provides the interface logic to add and edit menu items.
+ *
+ * @author     Matthias Schmidt
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Acp/Ui/Box/Controller/Handler
+ */
+define(['Ajax', 'Dictionary'], function(Ajax, Dictionary) {
+       "use strict";
+       
+       var _boxType = elById('boxType');
+       var _boxControllerContainer = elById('boxControllerContainer');
+       var _boxController = elById('boxControllerID');
+       var _boxConditions = elById('boxConditions');
+       var _templates = new Dictionary();
+       
+       /**
+        * @exports     WoltLab/WCF/Acp/Ui/Box/Controller/Handler
+        */
+       return {
+               init: function(initialObjectTypeId) {
+                       _boxType.addEventListener('change', this._updateControllers.bind(this));
+                       _boxController.addEventListener('change', this._updateConditions.bind(this));
+                       
+                       if (initialObjectTypeId) {
+                               _templates.set(~~initialObjectTypeId, _boxConditions.innerHTML);
+                       }
+                       
+                       this._updateControllers();
+               },
+               
+               /**
+                * Sets up ajax request object.
+                *
+                * @return      {object}        request options
+                */
+               _ajaxSetup: function() {
+                       return {
+                               data: {
+                                       actionName: 'getBoxConditionsTemplate',
+                                       className: 'wcf\\data\\box\\BoxAction'
+                               }
+                       };
+               },
+               
+               /**
+                * Handles successful AJAX requests.
+                *
+                * @param       {object}        data    response data
+                */
+               _ajaxSuccess: function(data) {
+                       _templates.set(~~data.returnValues.objectTypeID, data.returnValues.template);
+                       
+                       _boxConditions.innerHTML = data.returnValues.template;
+               },
+               
+               /**
+                * Updates the displayed box conditions based on the selected dynamic box controller.
+                * 
+                * @protected
+                */
+               _updateConditions: function() {
+                       var objectTypeId = ~~_boxController.value;
+                       
+                       if (_templates.has(objectTypeId)) {
+                               if (_templates.get(objectTypeId) !== null) {
+                                       _boxConditions.innerHTML = _templates.get(objectTypeId);
+                               }
+                       }
+                       else {
+                               _templates.set(objectTypeId, null);
+                               
+                               Ajax.api(this, {
+                                       parameters: {
+                                               objectTypeID: objectTypeId
+                                       }
+                               });
+                       }
+               },
+               
+               /**
+                * Shows or hides the list of dynamic box controllers based on the selected box type.
+                * 
+                * @protected
+                */
+               _updateControllers: function() {
+                       if (_boxType.value === 'system') {
+                               elShow(_boxControllerContainer);
+                               
+                               this._updateConditions();
+                       }
+                       else {
+                               elHide(_boxControllerContainer);
+                               
+                               _boxConditions.innerHTML = '';
+                       }
+               }
+       };
+});
index 596d15725aefceb6f9f0851f3f6ed25c71494bbf..f18b23675bb07b538ef7b3247d7572b6b7f902c5 100644 (file)
@@ -5,9 +5,12 @@ use wcf\data\box\BoxAction;
 use wcf\data\box\BoxEditor;
 use wcf\data\media\Media;
 use wcf\data\media\ViewableMediaList;
+use wcf\data\object\type\ObjectType;
+use wcf\data\object\type\ObjectTypeCache;
 use wcf\data\page\Page;
 use wcf\data\page\PageNodeTree;
 use wcf\form\AbstractForm;
+use wcf\system\box\IConditionBoxController;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
 use wcf\system\language\LanguageFactory;
@@ -80,12 +83,6 @@ class BoxAddForm extends AbstractForm {
         */
        public $showHeader = 1;
        
-       /**
-        * box controller
-        * @var string
-        */
-       public $controller = '';
-       
        /**
         * box name
         * @var string
@@ -122,6 +119,18 @@ class BoxAddForm extends AbstractForm {
         */
        public $pageIDs = [];
        
+       /**
+        * object type id of the selected box controller
+        * @var integer
+        */
+       public $boxControllerID = 0;
+       
+       /**
+        * selected box controller object type 
+        * @var ObjectType
+        */
+       public $boxController;
+       
        /**
         * link type
         * @var string
@@ -193,7 +202,6 @@ 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['controller'])) $this->controller = StringUtil::trim($_POST['controller']);
                if (isset($_POST['pageIDs']) && is_array($_POST['pageIDs'])) $this->pageIDs = ArrayUtil::toIntegerArray($_POST['pageIDs']);
                
                if (isset($_POST['linkType'])) $this->linkType = $_POST['linkType'];
@@ -203,6 +211,7 @@ class BoxAddForm extends AbstractForm {
                
                if (isset($_POST['title']) && is_array($_POST['title'])) $this->title = ArrayUtil::trim($_POST['title']);
                if (isset($_POST['content']) && is_array($_POST['content'])) $this->content = ArrayUtil::trim($_POST['content']);
+               if (isset($_POST['boxControllerID'])) $this->boxControllerID = intval($_POST['boxControllerID']);
                
                if (WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
                        if (isset($_POST['imageID']) && is_array($_POST['imageID'])) $this->imageID = ArrayUtil::toIntegerArray($_POST['imageID']);
@@ -243,20 +252,25 @@ class BoxAddForm extends AbstractForm {
                        throw new UserInputException('boxType');
                }
                
+               if ($this->boxType === 'system') {
+                       $this->boxController = ObjectTypeCache::getInstance()->getObjectType($this->boxControllerID);
+                       if ($this->boxController === null || $this->boxController->getDefinition()->definitionName != 'com.woltlab.wcf.boxController') {
+                               throw new UserInputException('boxController');
+                       }
+                       
+                       if ($this->boxController && $this->boxController->getProcessor() instanceof IConditionBoxController) {
+                               $this->boxController->getProcessor()->readConditions();
+                       }
+               }
+               else {
+                       $this->boxControllerID = 0;
+               }
+               
                // validate box position
                if (!in_array($this->position, Box::$availablePositions)) {
                        throw new UserInputException('position');
                }
                
-               // validate class name
-               if ($this->boxType == 'system') {
-                       if (empty($this->controller)) {
-                               throw new UserInputException('controller');
-                       }
-                       
-                       // @todo check controller
-               }
-               
                // validate link
                if ($this->linkType == 'internal') {
                        $this->externalURL = '';
@@ -317,6 +331,10 @@ class BoxAddForm extends AbstractForm {
                                }
                        }
                }
+               
+               if ($this->boxController && $this->boxController->getProcessor() instanceof IConditionBoxController) {
+                       $this->boxController->getProcessor()->validateConditions();
+               }
        }
        
        /**
@@ -357,6 +375,7 @@ class BoxAddForm extends AbstractForm {
                
                $this->objectAction = new BoxAction([], 'create', ['data' => array_merge($this->additionalFields, [
                        'name' => $this->name,
+                       'objectTypeID' => $this->boxControllerID,
                        'packageID' => 1,
                        'isMultilingual' => $this->isMultilingual,
                        'boxType' => $this->boxType,
@@ -365,20 +384,24 @@ class BoxAddForm extends AbstractForm {
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'cssClassName' => $this->cssClassName,
                        'showHeader' => $this->showHeader,
-                       'controller' => $this->controller,
                        'linkPageID' => $this->linkPageID,
                        'linkPageObjectID' => ($this->linkPageObjectID ?: 0),
                        'externalURL' => $this->externalURL,
                        'identifier' => ''
                ]), 'content' => $content, 'pageIDs' => $this->pageIDs ]);
-               $returnValues = $this->objectAction->executeAction();
+               $box = $this->objectAction->executeAction()['returnValues'];
                
                // set generic box identifier
-               $boxEditor = new BoxEditor($returnValues['returnValues']);
+               $boxEditor = new BoxEditor($box);
                $boxEditor->update([
                        'identifier' => 'com.woltlab.wcf.genericBox'.$boxEditor->boxID
                ]);
                
+               if ($this->boxController && $this->boxController->getProcessor() instanceof IConditionBoxController) {
+                       $this->boxController->getProcessor()->setBox($box, false);
+                       $this->boxController->getProcessor()->saveConditions();
+               }
+               
                // call saved event
                $this->saved();
                
@@ -386,10 +409,11 @@ class BoxAddForm extends AbstractForm {
                WCF::getTPL()->assign('success', true);
                
                // reset variables
-               $this->boxType = $this->position = $this->cssClassName = $this->controller = $this->name = '';
-               $this->showOrder = 0;
+               $this->boxType = $this->position = $this->cssClassName = $this->name = '';
+               $this->showOrder = $this->boxControllerID = 0;
                $this->visibleEverywhere = $this->showHeader = 1;
                $this->title = $this->content = $this->images = $this->imageID = [];
+               $this->boxController = null;
        }
        
        /**
@@ -405,7 +429,6 @@ class BoxAddForm extends AbstractForm {
                        'boxType' => $this->boxType,
                        'position' => $this->position,
                        'cssClassName' => $this->cssClassName,
-                       'controller' => $this->controller,
                        'showOrder' => $this->showOrder,
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'showHeader' => $this->showHeader,
@@ -421,6 +444,8 @@ class BoxAddForm extends AbstractForm {
                        'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
                        'availableBoxTypes' => Box::$availableBoxTypes,
                        'availablePositions' => Box::$availablePositions,
+                       'availableBoxControllers' => ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.boxController'),
+                       'boxController' => $this->boxController,
                        'pageNodeList' => $this->pageNodeList,
                        'pageHandlers' => $this->pageHandlers
                ]);
index 79c889e960ec2f0e15def27ea52d44d7abf1587b..ecb9b043e7c20668f5aaf1466d217512eaf45ee0 100644 (file)
@@ -2,7 +2,9 @@
 namespace wcf\acp\form;
 use wcf\data\box\Box;
 use wcf\data\box\BoxAction;
+use wcf\data\object\type\ObjectTypeCache;
 use wcf\form\AbstractForm;
+use wcf\system\box\IConditionBoxController;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\language\LanguageFactory;
 use wcf\system\WCF;
@@ -69,7 +71,7 @@ class BoxEditForm extends BoxAddForm {
        public function save() {
                AbstractForm::save();
                
-               $content = array();
+               $content = [];
                if ($this->isMultilingual) {
                        foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
                                $content[$language->languageID] = [
@@ -89,6 +91,7 @@ class BoxEditForm extends BoxAddForm {
                
                $this->objectAction = new BoxAction([$this->box], 'update', ['data' => array_merge($this->additionalFields, [
                        'name' => $this->name,
+                       'objectTypeID' => $this->boxControllerID,
                        'isMultilingual' => $this->isMultilingual,
                        'boxType' => $this->boxType,
                        'position' => $this->position,
@@ -96,13 +99,17 @@ class BoxEditForm extends BoxAddForm {
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'cssClassName' => $this->cssClassName,
                        'showHeader' => $this->showHeader,
-                       'controller' => $this->controller,
                        'linkPageID' => $this->linkPageID,
                        'linkPageObjectID' => ($this->linkPageObjectID ?: 0),
                        'externalURL' => $this->externalURL
                ]), 'content' => $content, 'pageIDs' => $this->pageIDs]);
                $this->objectAction->executeAction();
                
+               if ($this->boxController && $this->boxController->getProcessor() instanceof IConditionBoxController) {
+                       $this->boxController->getProcessor()->setBox($this->box, false);
+                       $this->boxController->getProcessor()->saveConditions();
+               }
+               
                // call saved event
                $this->saved();
                
@@ -130,7 +137,7 @@ class BoxEditForm extends BoxAddForm {
                        $this->position = $this->box->position;
                        $this->showOrder = $this->box->showOrder;
                        $this->cssClassName = $this->box->cssClassName;
-                       $this->controller = $this->box->controller;
+                       $this->boxControllerID = $this->box->objectTypeID;
                        if ($this->box->showHeader) $this->showHeader = 1;
                        if ($this->box->visibleEverywhere) $this->visibleEverywhere = 1;
                        else $this->visibleEverywhere = 0;
@@ -147,6 +154,13 @@ class BoxEditForm extends BoxAddForm {
                                $this->imageID[$languageID] = $content['imageID'];
                        }
                        
+                       if ($this->boxControllerID) {
+                               $this->boxController = ObjectTypeCache::getInstance()->getObjectType($this->boxControllerID);
+                               if ($this->boxController->getProcessor() instanceof IConditionBoxController) {
+                                       $this->boxController->getProcessor()->setBox($this->box);
+                               }
+                       }
+                       
                        $this->readBoxImages();
                }
        }
@@ -157,10 +171,10 @@ class BoxEditForm extends BoxAddForm {
        public function assignVariables() {
                parent::assignVariables();
                
-               WCF::getTPL()->assign(array(
+               WCF::getTPL()->assign([
                        'action' => 'edit',
                        'boxID' => $this->boxID,
                        'box' => $this->box
-               ));
+               ]);
        }
 }
index ef64b5827773b6e5b9d98b2fb52b2275d31e95cd..15a5acac0d0216a044ed3ec44496223785694fec 100644 (file)
@@ -1,8 +1,12 @@
 <?php
 namespace wcf\data\box;
+use wcf\data\condition\Condition;
 use wcf\data\media\ViewableMedia;
 use wcf\data\menu\Menu;
 use wcf\data\menu\MenuCache;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\box\IConditionBoxController;
+use wcf\system\condition\ConditionHandler;
 use wcf\data\page\Page;
 use wcf\data\page\PageCache;
 use wcf\data\DatabaseObject;
@@ -24,6 +28,7 @@ use wcf\util\StringUtil;
  * @since      2.2
  *
  * @property-read      integer         $boxID
+ * @property-read      integer|null    $objectTypeID           id of the box controller object type
  * @property-read      string          $identifier
  * @property-read      string          $name
  * @property-read      string          $boxType
@@ -35,11 +40,11 @@ use wcf\util\StringUtil;
  * @property-read      integer         $showHeader
  * @property-read      integer         $originIsSystem
  * @property-read      integer         $packageID
- * @property-read      string          $controller
  * @property-read      integer|null    $menuID
  * @property-read      integer         $linkPageID
  * @property-read      integer         $linkPageObjectID
  * @property-read      string          $externalURL
+ * @property-read      mixed[]         $additionalData
  */
 class Box extends DatabaseObject {
        /**
@@ -90,15 +95,41 @@ class Box extends DatabaseObject {
        
        /**
         * box to page assignments
-        * @var integer[]
+        * @var integer[]
         */
        protected $pageIDs;
        
        /**
         * box controller
-        * @var \wcf\system\box\IBoxController
+        * @var \wcf\system\box\IBoxController
         */
-       protected $__controller;
+       protected $controller;
+       
+       /**
+        * @inheritDoc
+        */
+       public function __get($name) {
+               $value = parent::__get($name);
+               
+               if ($value === null && isset($this->data['additionalData'][$name])) {
+                       $value = $this->data['additionalData'][$name];
+               }
+               
+               return $value;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function handleData($data) {
+               parent::handleData($data);
+               
+               // handle condition data
+               $this->data['additionalData'] = @unserialize($data['additionalData']);
+               if (!is_array($this->data['additionalData'])) {
+                       $this->data['additionalData'] = [];
+               }
+       }
        
        /**
         * @var IMenuPageHandler
@@ -150,29 +181,36 @@ class Box extends DatabaseObject {
                return $this->boxContent;
        }
        
+       /**
+        * Returns the title of the box as set in the box content database table.
+        * 
+        * @return      string
+        */
+       public function getBoxContentTitle() {
+               $boxContent = $this->getBoxContent();
+               if ($this->isMultilingual) {
+                       if (isset($boxContent[WCF::getLanguage()->languageID])) {
+                               return $boxContent[WCF::getLanguage()->languageID]['title'];
+                       }
+               }
+               else if (isset($boxContent[0])) {
+                       return $boxContent[0]['title'];
+               }
+               
+               return '';
+       }
+       
        /**
         * Returns the title for the rendered version of this box.
         * 
         * @return      string
         */
        public function getTitle() {
-               if ($this->boxType == 'system') {
-                       return $this->getController()->getTitle();
-               }
-               else if ($this->boxType == 'menu') {
+               if ($this->boxType == 'menu') {
                        return $this->getMenu()->getTitle();
                }
-               else {
-                       $boxContent = $this->getBoxContent();
-                       if ($this->isMultilingual) {
-                               if (isset($boxContent[WCF::getLanguage()->languageID])) return $boxContent[WCF::getLanguage()->languageID]['title'];
-                       }
-                       else {
-                               if (isset($boxContent[0])) return $boxContent[0]['title'];
-                       }
-               }
                
-               return '';
+               return $this->getBoxContentTitle();
        }
        
        /**
@@ -253,14 +291,14 @@ class Box extends DatabaseObject {
         * @return      \wcf\system\box\IBoxController
         */
        public function getController() {
-               if ($this->__controller === null) {
-                       if ($this->controller && class_exists($this->controller)) {
-                               $this->__controller = new $this->controller;
-                               $this->__controller->setBox($this);
-                       }
+               if ($this->controller === null && $this->objectTypeID) {
+                       $className = ObjectTypeCache::getInstance()->getObjectType($this->objectTypeID)->className;
+                       
+                       $this->controller = new $className;
+                       $this->controller->setBox($this);
                }
                
-               return $this->__controller;
+               return $this->controller;
        }
        
        /**
@@ -422,14 +460,43 @@ class Box extends DatabaseObject {
                                WHERE   boxID = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
                        $statement->execute([$this->boxID]);
-                       while ($row = $statement->fetchArray()) {
-                               $this->pageIDs[] = $row['pageID'];
+                       while ($pageID = $statement->fetchColumn()) {
+                               $this->pageIDs[] = $pageID;
                        }
                }
                
                return $this->pageIDs;
        }
        
+       /**
+        * Returns the conditions of the notice.
+        *
+        * @return      Condition[]
+        */
+       public function getConditions() {
+               if ($this->boxType === 'system' && $this->getController() instanceof IConditionBoxController && $this->getController()->getConditionDefinition()) {
+                       return ConditionHandler::getInstance()->getConditions($this->getController()->getConditionDefinition(), $this->boxID);
+               }
+               
+               return [];
+       }
+       
+       /**
+        * Returns the box with the given idnetifier.
+        *
+        * @param       string          $identifier
+        * @return      Box
+        */
+       public static function getBoxByIdentifier($identifier) {
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_box
+                       WHERE   identifier = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute([$identifier]);
+               
+               return $statement->fetchObject(self::class);
+       }
+       
        /**
         * Returns the box with the given name.
         *
@@ -449,7 +516,7 @@ class Box extends DatabaseObject {
        /**
         * Returns the box with the menu id.
         *
-        * @param       int             $menuID
+        * @param       int     $menuID
         * @return      Box
         */
        public static function getBoxByMenuID($menuID) {
index 4234338d297c0c4b26b43c94974abfddd46b66f4..775fe8260cc4b948948bfeac203b24bec20d6bc1 100644 (file)
@@ -1,14 +1,18 @@
 <?php
 namespace wcf\data\box;
 use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\object\type\ObjectType;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\box\IConditionBoxController;
 use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
 
 /**
  * Executes box related actions.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage data.box
@@ -39,7 +43,13 @@ class BoxAction extends AbstractDatabaseObjectAction {
        /**
         * @inheritDoc
         */
-       protected $requireACP = ['create', 'delete', 'update'];
+       protected $requireACP = ['create', 'delete', 'getBoxConditionsTemplate', 'update'];
+       
+       /**
+        * object type for which the conditions template is fetched
+        * @var ObjectType
+        */
+       public $boxController;
        
        /**
         * @inheritDoc
@@ -184,4 +194,29 @@ class BoxAction extends AbstractDatabaseObjectAction {
                
                parent::delete();
        }
+       
+       /**
+        * Validates the 'getBoxConditionsTemplate' action.
+        */
+       public function validateGetBoxConditionsTemplate() {
+               WCF::getSession()->checkPermissions(['admin.content.cms.canManageBox']);
+               
+               $this->readInteger('objectTypeID');
+               $this->boxController = ObjectTypeCache::getInstance()->getObjectType($this->parameters['objectTypeID']);
+               if ($this->boxController === null) {
+                       throw new UserInputException('objectTypeID');
+               }
+       }
+       
+       /**
+        * Returns the template
+        * 
+        * @return      mixed[]
+        */
+       public function getBoxConditionsTemplate() {
+               return [
+                       'objectTypeID' => $this->boxController->objectTypeID,
+                       'template' => $this->boxController->getProcessor() instanceof IConditionBoxController ? $this->boxController->getProcessor()->getConditionsTemplate() : ''
+               ];
+       }
 }
index 7f68acacca02bf54e2960106f0549ef86f6c6795..1f0af441d0b46c0245b3771305814a79dcdc14fa 100644 (file)
@@ -1,20 +1,22 @@
 <?php
 namespace wcf\data\object\type;
+use wcf\data\object\type\definition\ObjectTypeDefinition;
 use wcf\data\ProcessibleDatabaseObject;
 use wcf\data\TDatabaseObjectOptions;
 use wcf\data\TDatabaseObjectPermissions;
 use wcf\system\exception\SystemException;
+use wcf\system\SingletonFactory;
 
 /**
  * Represents an object type.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage data.object.type
  * @category   Community Framework
- *
+ * 
  * @property-read      integer         $objectTypeID
  * @property-read      integer         $definitionID
  * @property-read      integer         $packageID
@@ -27,17 +29,17 @@ class ObjectType extends ProcessibleDatabaseObject {
        use TDatabaseObjectPermissions;
        
        /**
-        * @see \wcf\data\DatabaseObject::$databaseTableName
+        * @inheritDoc
         */
        protected static $databaseTableName = 'object_type';
        
        /**
-        * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+        * @inheritDoc
         */
        protected static $databaseTableIndexName = 'objectTypeID';
        
        /**
-        * @see \wcf\data\IStorableObject::__get()
+        * @inheritDoc
         */
        public function __get($name) {
                $value = parent::__get($name);
@@ -60,23 +62,23 @@ class ObjectType extends ProcessibleDatabaseObject {
        public final function __sleep() {
                // 'processor' isn't returned since it can be an instance of
                // wcf\system\SingletonFactory which may not be serialized
-               return array('data');
+               return ['data'];
        }
        
        /**
-        * @see \wcf\data\DatabaseObject::handleData()
+        * @inheritDoc
         */
        protected function handleData($data) {
                parent::handleData($data);
                
                $this->data['additionalData'] = @unserialize($this->data['additionalData']);
                if (!is_array($this->data['additionalData'])) {
-                       $this->data['additionalData'] = array();
+                       $this->data['additionalData'] = [];
                }
        }
        
        /**
-        * @see \wcf\data\ProcessibleDatabaseObject::getProcessor()
+        * @inheritDoc
         */
        public function getProcessor() {
                if ($this->processor === null) {
@@ -88,8 +90,8 @@ class ObjectType extends ProcessibleDatabaseObject {
                                        throw new SystemException("'".$this->className."' does not implement '".$definitionInterface."'");
                                }
                                
-                               if (is_subclass_of($this->className, 'wcf\system\SingletonFactory')) {
-                                       $this->processor = call_user_func(array($this->className, 'getInstance'));
+                               if (is_subclass_of($this->className, SingletonFactory::class)) {
+                                       $this->processor = call_user_func([$this->className, 'getInstance']);
                                }
                                else {
                                        $this->processor = new $this->className($this);
@@ -99,4 +101,14 @@ class ObjectType extends ProcessibleDatabaseObject {
                
                return $this->processor;
        }
+       
+       /**
+        * Returns the object type definition of the object type.
+        * 
+        * @return      ObjectTypeDefinition
+        * @since       2.2
+        */
+       public function getDefinition() {
+               return ObjectTypeCache::getInstance()->getDefinition($this->definitionID);
+       }
 }
index 7eb6239bedcfef0ebedd2df542cde9dbaeaa8fdc..c6c97433d096e2abf25ca0435c1ebe15b0e488b5 100644 (file)
@@ -12,6 +12,7 @@ use wcf\system\event\EventHandler;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 abstract class AbstractBoxController implements IBoxController {
        /**
@@ -33,10 +34,10 @@ abstract class AbstractBoxController implements IBoxController {
        protected $supportedPositions = [];
        
        /**
-        * @inheritDoc
+        * Creates a new instance of AbstractBoxController.
         */
-       public function getTitle() {
-               return '';
+       public function __construct() {
+               EventHandler::getInstance()->fireAction($this, '__construct');
        }
        
        /**
diff --git a/wcfsetup/install/files/lib/system/box/AbstractDatabaseObjectListBoxController.class.php b/wcfsetup/install/files/lib/system/box/AbstractDatabaseObjectListBoxController.class.php
new file mode 100644 (file)
index 0000000..adb2bb4
--- /dev/null
@@ -0,0 +1,331 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\box\Box;
+use wcf\data\box\BoxAction;
+use wcf\data\DatabaseObjectList;
+use wcf\data\object\type\ObjectType;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\condition\ConditionHandler;
+use wcf\system\condition\IObjectListCondition;
+use wcf\system\event\EventHandler;
+use wcf\system\exception\SystemException;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Default implementation of a box controller based on an object list.
+ * 
+ * @author     Matthias Schmidt
+ * @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
+ * @since      2.2
+ */
+abstract class AbstractDatabaseObjectListBoxController extends AbstractBoxController implements IConditionBoxController {
+       /**
+        * name of the object type definition for the box controller's condition object types
+        * @var string
+        */
+       protected $conditionDefinition = '';
+       
+       /**
+        * condition objects types registered for the dynamic box controller
+        * @var ObjectType[]
+        */
+       protected $conditionObjectTypes = [];
+       
+       /**
+        * default limit value for the maximum number of shown database objects
+        * if this property is null, setting a limit is disabled
+        * @var integer
+        */
+       public $defaultLimit;
+       
+       /**
+        * default sort field
+        * @var string
+        */
+       public $defaultSortField;
+       
+       /**
+        * limit value for the maximum number of shown database objects
+        * @var integer
+        */
+       public $limit;
+       
+       /**
+        * maximum limit value, if `null` no maximum is set
+        * @var integer
+        */
+       public $maximumLimit;
+       
+       /**
+        * minimum limit value
+        * @var integer
+        */
+       public $minimumLimit = 1;
+       
+       /**
+        * database object list used to read the objects displayed in the box
+        * @var DatabaseObjectList
+        */
+       public $objectList;
+       
+       /**
+        * name of the database table column used for sorting
+        * @var string
+        */
+       public $sortField;
+       
+       /**
+        * prefix used for the titles of the sort fields
+        * @var string
+        */
+       protected $sortFieldLanguageItemPrefix;
+       
+       /**
+        * order used for sorting the database objects
+        * @var string
+        */
+       public $sortOrder;
+       
+       /**
+        * list of valid sort fields
+        * if this property is null, sorting is disabled
+        * @var string[]
+        */
+       public $validSortFields;
+       
+       /**
+        * Creates a new instance of AbstractDynamicBoxController.
+        * 
+        * @throws      SystemException
+        */
+       public function __construct() {
+               if ($this->conditionDefinition) {
+                       if (ObjectTypeCache::getInstance()->getDefinitionByName($this->conditionDefinition) === null) {
+                               throw new SystemException("Unknown object type definition '" . $this->conditionDefinition . "'");
+                       }
+                       
+                       $this->conditionObjectTypes = ObjectTypeCache::getInstance()->getObjectTypes($this->conditionDefinition);
+               }
+               
+               if (!empty($this->validSortFields)) {
+                       $this->sortField = $this->defaultSortField;
+               }
+               
+               if ($this->defaultLimit !== null) {
+                       if ($this->defaultLimit <= 0) {
+                               throw new SystemException("The default limit may has to be positive.");
+                       }
+                       
+                       $this->limit = $this->defaultLimit;
+               }
+               
+               parent::__construct();
+       }
+       
+       /**
+        * Returns the additional data saved with the box..
+        * 
+        * @return      array
+        */
+       protected function getAdditionalData() {
+               return [
+                       'limit' => $this->limit,
+                       'sortField' => $this->sortField,
+                       'sortOrder' => $this->sortOrder
+               ];
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getConditionDefinition() {
+               return $this->conditionDefinition;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getConditionObjectTypes() {
+               return $this->conditionObjectTypes;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getConditionsTemplate() {
+               if ($this->defaultLimit !== null || !empty($this->validSortFields) || !empty($this->conditionObjectTypes)) {
+                       return WCF::getTPL()->fetch('boxConditions', 'wcf', [
+                               'boxController' => $this,
+                               'conditionObjectTypes' => $this->conditionObjectTypes,
+                               'defaultLimit' => $this->defaultLimit,
+                               'limit' => $this->limit,
+                               'maximumLimit' => $this->maximumLimit,
+                               'minimumLimit' => $this->minimumLimit,
+                               'sortField' => $this->sortField,
+                               'sortFieldLanguageItemPrefix' => $this->sortFieldLanguageItemPrefix,
+                               'sortOrder' => $this->sortOrder,
+                               'validSortFields' => $this->validSortFields
+                       ]);
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the database object list used to read the objects displayed in the box.
+        *
+        * @return      DatabaseObjectList
+        */
+       abstract protected function getObjectList();
+       
+       /**
+        * Returns the template to display the box.
+        * 
+        * @return      string
+        */
+       abstract protected function getTemplate();
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasContent() {
+               if ($this->objectList === null) {
+                       $this->loadContent();
+               }
+               
+               return count($this->objectList) > 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function loadContent() {
+               $this->objectList = $this->getObjectList();
+               
+               if ($this->box->limit) {
+                       $this->objectList->sqlLimit = $this->box->limit;
+               }
+               
+               if ($this->box->sortOrder && $this->box->sortField) {
+                       $alias = $this->objectList->getDatabaseTableAlias();
+                       $this->objectList->sqlOrderBy = $this->box->sortField . ' ' . $this->box->sortOrder . ", " . ($alias ? $alias . "." : "") . $this->objectList->getDatabaseTableIndexName() . " " . $this->box->sortOrder;
+               }
+               
+               if ($this->conditionDefinition) {
+                       foreach ($this->box->getConditions() as $condition) {
+                               /** @var IObjectListCondition $processor */
+                               $processor = $condition->getObjectType()->getProcessor();
+                               $processor->addObjectListCondition($this->objectList, $condition->conditionData);
+                       }
+               }
+               
+               $this->readObjects();
+               
+               $this->content = $this->getTemplate();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readConditions() {
+               if (isset($_POST['limit'])) $this->limit = intval($_POST['limit']);
+               if (isset($_POST['sortField'])) $this->sortField = StringUtil::trim($_POST['sortField']);
+               if (isset($_POST['sortOrder'])) $this->sortOrder = StringUtil::trim($_POST['sortOrder']);
+               
+               foreach ($this->conditionObjectTypes as $objectType) {
+                       $objectType->getProcessor()->readFormParameters();
+               }
+       }
+       
+       /**
+        * Reads the displayed database objects.
+        */
+       protected function readObjects() {
+               EventHandler::getInstance()->fireAction($this, 'readObjects');
+               
+               $this->objectList->readObjects();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function saveConditions() {
+               if (($this->sortField && $this->sortOrder) || $this->limit) {
+                       (new BoxAction([$this->box], 'update', [
+                               'data' => [
+                                       'additionalData' => serialize(array_merge($this->box->additionalData, $this->getAdditionalData()))
+                               ]
+                       ]))->executeAction();
+               }
+               
+               if ($this->conditionDefinition) {
+                       // do not use Box::getConditions() here to avoid setting box data by internally calling
+                       // Box::getController()
+                       ConditionHandler::getInstance()->updateConditions($this->box->boxID, ConditionHandler::getInstance()->getConditions($this->conditionDefinition, $this->box->boxID), $this->conditionObjectTypes);
+               }
+       }
+       
+       /**
+        * Sets the box the controller object belongs to and populates the condition object types
+        * with the box conditions.
+        * 
+        * @param       Box             $box                    box object
+        * @param       boolean         $setConditionData       if true, the condition object types are populated witht the box conditions' data
+        */
+       public function setBox(Box $box, $setConditionData = true) {
+               parent::setBox($box);
+               
+               if ($setConditionData) {
+                       $this->limit = $this->box->limit;
+                       $this->sortOrder = $this->box->sortOrder;
+                       $this->sortField = $this->box->sortField;
+                       
+                       if ($this->conditionDefinition) {
+                               $conditions = [];
+                               foreach ($this->box->getConditions() as $condition) {
+                                       $conditions[$condition->objectTypeID] = $condition;
+                               }
+                               
+                               foreach ($this->conditionObjectTypes as $objectType) {
+                                       if (isset($conditions[$objectType->objectTypeID])) {
+                                               $objectType->getProcessor()->setData($conditions[$objectType->objectTypeID]);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function validateConditions() {
+               if ($this->defaultLimit !== null) {
+                       if ($this->limit < $this->minimumLimit) {
+                               throw new UserInputException('limit', 'greaterThan');
+                       }
+                       else if ($this->maximumLimit !== null && $this->limit > $this->maximumLimit) {
+                               throw new UserInputException('limit', 'lessThan');
+                       }
+               }
+               
+               if (!empty($this->validSortFields)) {
+                       if (!in_array($this->sortField, $this->validSortFields)) {
+                               throw new UserInputException('sorting', 'sortFieldNotValid');
+                       }
+                       
+                       if ($this->sortOrder !== 'ASC' && $this->sortOrder !== 'DESC') {
+                               throw new UserInputException('sorting', 'sortOrderNotValid');
+                       }
+               }
+               
+               foreach ($this->conditionObjectTypes as $objectType) {
+                       $objectType->getProcessor()->validate();
+               }
+       }
+}
index 159e17be49d3cc24fe0f02546e636409e440214c..0d185a6cab44fe83ba0aace160f06ed5b8eec11b 100644 (file)
@@ -2,8 +2,11 @@
 namespace wcf\system\box;
 use wcf\data\box\Box;
 use wcf\data\box\BoxList;
+use wcf\data\condition\ConditionAction;
+use wcf\system\exception\SystemException;
 use wcf\system\request\RequestHandler;
 use wcf\system\SingletonFactory;
+use wcf\system\WCF;
 
 /**
  * Handles boxes.
@@ -45,6 +48,47 @@ class BoxHandler extends SingletonFactory {
                }
        }
        
+       /**
+        * Creates a new condition for an existing box.
+        * 
+        * Note: The primary use of this method is to be used during package installation.
+        * 
+        * @param       string          $boxIdentifier
+        * @param       string          $conditionDefinition
+        * @param       string          $conditionObjectType
+        * @param       array           $conditionData
+        * @throws      SystemException
+        */
+       public function createBoxCondition($boxIdentifier, $conditionDefinition, $conditionObjectType, array $conditionData) {
+               // do not rely on caches during package installation
+               $sql = "SELECT          objectTypeID
+                       FROM            wcf".WCF_N."_object_type object_type
+                       INNER JOIN      wcf".WCF_N."_object_type_definition object_type_definition
+                       ON              (object_type.definitionID = object_type_definition.definitionID)
+                       WHERE           objectType = ?
+                                       AND definitionName = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute([$conditionObjectType, $conditionDefinition]);
+               $objectTypeID = $statement->fetchSingleColumn();
+               
+               if (!$objectTypeID) {
+                       throw new SystemException("Unknown box condition '{$conditionObjectType}' of condition definition '{$conditionDefinition}'");
+               }
+               
+               $box = Box::getBoxByIdentifier($boxIdentifier);
+               if ($box === null) {
+                       throw new SystemException("Unknown box with idenifier '{$boxIdentifier}'");
+               }
+               
+               (new ConditionAction([], 'create', [
+                       'data' => [
+                               'conditionData' => serialize($conditionData),
+                               'objectID' => $box->boxID,
+                               'objectTypeID' => $objectTypeID
+                       ]
+               ]))->executeAction();
+       }
+       
        /**
         * Returns boxes for the given position.
         * 
index e8717f8471e202be8e2b64f2119427f075142dd7..f936d6e92861024024213a9f698833a2f7c19fb9 100644 (file)
@@ -5,15 +5,21 @@ use wcf\system\WCF;
 
 /**
  * Lists online users the active user is following.
- *
+ * 
  * @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
+ * @since      2.2
  */
-class FollowingsOnlineBoxController extends AbstractBoxController {
+class FollowingsOnlineBoxController extends AbstractDatabaseObjectListBoxController {
+       /**
+        * @inheritDoc
+        */
+       public $defaultLimit = 10;
+       
        /**
         * @inheritDoc
         */
@@ -22,27 +28,28 @@ class FollowingsOnlineBoxController extends AbstractBoxController {
        /**
         * @inheritDoc
         */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.user.followingsOnline'); // @todo
+       protected function getObjectList() {
+               $objectList = new UsersOnlineList();
+               $objectList->getConditionBuilder()->add('session.userID IN (?)', [WCF::getUserProfileHandler()->getFollowingUsers()]);
+               
+               return $objectList;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getTemplate() {
+               return WCF::getTPL()->fetch('boxFollowingsOnline', 'wcf', ['usersOnlineList' => $this->objectList]);
        }
        
        /**
         * @inheritDoc
         */
-       protected function loadContent() {
-               if (MODULE_USERS_ONLINE && WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList') && count(WCF::getUserProfileHandler()->getFollowingUsers())) {
-                       $usersOnlineList = new UsersOnlineList();
-                       $usersOnlineList->getConditionBuilder()->add('session.userID IN (?)', array(WCF::getUserProfileHandler()->getFollowingUsers()));
-                       $usersOnlineList->sqlLimit = 10;
-                       $usersOnlineList->readObjects();
-                       
-                       if (count($usersOnlineList)) {
-                               WCF::getTPL()->assign([
-                                       'usersOnlineList' => $usersOnlineList
-                               ]);
-                               
-                               $this->content = WCF::getTPL()->fetch('boxFollowingsOnline');
-                       }
+       public function hasContent() {
+               if (!MODULE_USERS_ONLINE || !WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList') || empty(WCF::getUserProfileHandler()->getFollowingUsers())) {
+                       return false;
                }
+               
+               return parent::hasContent();
        }
 }
index a0e35802123e58f7fa554bf16ad61de7ca0e0145..577838f3e489b0abb3a96ba833839bbe73f99911 100644 (file)
@@ -11,19 +11,13 @@ use wcf\data\box\Box;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 interface IBoxController {
-       /**
-        * Returns the title of this box.
-        * 
-        * @return      string
-        */
-       public function getTitle();
-       
        /**
         * Returns the content of this box.
         *
-        * @return      string
+        * @return      string
         */
        public function getContent();
        
@@ -72,14 +66,14 @@ interface IBoxController {
        /**
         * Sets the database object of this box.
         *
-        * @param       Box            $box
+        * @param       Box     $box
         */
        public function setBox(Box $box);
        
        /**
         * Returns a list of supported box positions.
         * 
-        * @return      string[]
+        * @return      string[]
         */
        public function getSupportedPositions();
 }
diff --git a/wcfsetup/install/files/lib/system/box/IConditionBoxController.class.php b/wcfsetup/install/files/lib/system/box/IConditionBoxController.class.php
new file mode 100644 (file)
index 0000000..be54f68
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\object\type\ObjectType;
+
+/**
+ * Interface for dynamic box controller.
+ *
+ * @author     Matthias Schmidt
+ * @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
+ * @since      2.2
+ */
+interface IConditionBoxController extends IBoxController {
+       /**
+        * Returns the name of the object type definition for the box controller's condition object types.
+        * 
+        * @return      string
+        */
+       public function getConditionDefinition();
+       
+       /**
+        * Returns the condition objects types registered for the dynamic box controller.
+        * 
+        * @return      ObjectType[]
+        */
+       public function getConditionObjectTypes();
+       
+       /**
+        * Returns the template containing the box conditions.
+        * 
+        * @return      string
+        */
+       public function getConditionsTemplate();
+       
+       /**
+        * Reads the box conditions.
+        */
+       public function readConditions();
+       
+       /**
+        * Saves the conditions for the box.
+        */
+       public function saveConditions();
+       
+       /**
+        * Validates the read conditions for the box.
+        */
+       public function validateConditions();
+}
diff --git a/wcfsetup/install/files/lib/system/box/MostActiveMembersBoxController.class.php b/wcfsetup/install/files/lib/system/box/MostActiveMembersBoxController.class.php
deleted file mode 100644 (file)
index 77ead13..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-namespace wcf\system\box;
-use wcf\data\DatabaseObject;
-use wcf\system\cache\builder\MostActiveMembersCacheBuilder;
-use wcf\system\cache\runtime\UserProfileRuntimeCache;
-use wcf\system\request\LinkHandler;
-use wcf\system\WCF;
-
-/**
- * Shows a list of the most active members.
- *
- * @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 MostActiveMembersBoxController extends AbstractBoxController {
-       /**
-        * @inheritDoc
-        */
-       protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.mostActiveMembers'); // @todo
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function hasLink() {
-               if (MODULE_MEMBERS_LIST) {
-                       return true;
-               }
-               
-               return false;
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function getLink() {
-               if (MODULE_MEMBERS_LIST) {
-                       return LinkHandler::getInstance()->getLink('MembersList', [], 'sortField=activityPoints&sortOrder=DESC');
-               }
-               
-               return '';
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       protected function loadContent() {
-               // get ids
-               $mostActiveMemberIDs = MostActiveMembersCacheBuilder::getInstance()->getData();
-               if (!empty($mostActiveMemberIDs)) {
-                       UserProfileRuntimeCache::getInstance()->cacheObjectIDs($mostActiveMemberIDs);
-                       
-                       // get users
-                       $mostActiveMembers = UserProfileRuntimeCache::getInstance()->getObjects($mostActiveMemberIDs);
-                       DatabaseObject::sort($mostActiveMembers, 'activityPoints', 'DESC');
-                       
-                       WCF::getTPL()->assign([
-                               'mostActiveMembers' => $mostActiveMembers
-                       ]);
-                       $this->content = WCF::getTPL()->fetch('boxMostActiveMembers');
-               }
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/box/MostLikedMembersBoxController.class.php b/wcfsetup/install/files/lib/system/box/MostLikedMembersBoxController.class.php
deleted file mode 100644 (file)
index 6e2bf10..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-namespace wcf\system\box;
-use wcf\data\DatabaseObject;
-use wcf\system\cache\builder\MostLikedMembersCacheBuilder;
-use wcf\system\cache\runtime\UserProfileRuntimeCache;
-use wcf\system\request\LinkHandler;
-use wcf\system\WCF;
-
-/**
- * Shows a list of the most liked members.
- *
- * @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 MostLikedMembersBoxController extends AbstractBoxController {
-       /**
-        * @inheritDoc
-        */
-       protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.mostLikedMembers'); // @todo
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function hasLink() {
-               if (MODULE_MEMBERS_LIST) {
-                       return true;
-               }
-               
-               return false;
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function getLink() {
-               if (MODULE_MEMBERS_LIST) {
-                       return LinkHandler::getInstance()->getLink('MembersList', [], 'sortField=likesReceived&sortOrder=DESC');
-               }
-               
-               return '';
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       protected function loadContent() {
-               // get ids
-               $mostLikedMemberIDs = MostLikedMembersCacheBuilder::getInstance()->getData();
-               if (!empty($mostLikedMemberIDs)) {
-                       UserProfileRuntimeCache::getInstance()->cacheObjectIDs($mostLikedMemberIDs);
-                       
-                       // get users
-                       $mostLikedMembers = UserProfileRuntimeCache::getInstance()->getObjects($mostLikedMemberIDs);
-                       DatabaseObject::sort($mostLikedMembers, 'likesReceived', 'DESC');
-                       
-                       WCF::getTPL()->assign([
-                               'mostLikedMembers' => $mostLikedMembers
-                       ]);
-                       $this->content = WCF::getTPL()->fetch('boxMostLikedMembers');
-               }
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/box/NewestMembersBoxController.class.php b/wcfsetup/install/files/lib/system/box/NewestMembersBoxController.class.php
deleted file mode 100644 (file)
index 8edfd81..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-namespace wcf\system\box;
-use wcf\data\DatabaseObject;
-use wcf\system\cache\builder\NewestMembersCacheBuilder;
-use wcf\system\cache\runtime\UserProfileRuntimeCache;
-use wcf\system\request\LinkHandler;
-use wcf\system\WCF;
-
-/**
- * Shows a list of the newest members.
- *
- * @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 NewestMembersBoxController extends AbstractBoxController {
-       /**
-        * @inheritDoc
-        */
-       protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.newestMembers'); // @todo
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function hasLink() {
-               if (MODULE_MEMBERS_LIST) {
-                       return true;
-               }
-               
-               return false;
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function getLink() {
-               if (MODULE_MEMBERS_LIST) {
-                       return LinkHandler::getInstance()->getLink('MembersList', [], 'sortField=registrationDate&sortOrder=DESC');
-               }
-               
-               return '';
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       protected function loadContent() {
-               // get ids
-               $newestMemberIDs = NewestMembersCacheBuilder::getInstance()->getData();
-               if (!empty($newestMemberIDs)) {
-                       UserProfileRuntimeCache::getInstance()->cacheObjectIDs($newestMemberIDs);
-                       
-                       // get users
-                       $newestMembers = UserProfileRuntimeCache::getInstance()->getObjects($newestMemberIDs);
-                       DatabaseObject::sort($newestMembers, 'registrationDate', 'DESC');
-                       
-                       WCF::getTPL()->assign([
-                               'newestMembers' => $newestMembers
-                       ]);
-                       $this->content = WCF::getTPL()->fetch('boxNewestMembers');
-               }
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/box/PageCommentListBoxController.class.php b/wcfsetup/install/files/lib/system/box/PageCommentListBoxController.class.php
new file mode 100644 (file)
index 0000000..5f54fa8
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+namespace wcf\system\box;
+use wcf\system\comment\CommentHandler;
+use wcf\system\request\RequestHandler;
+use wcf\system\WCF;
+
+/**
+ * Box for the comments of the active page.
+ * 
+ * @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 PageCommentListBoxController extends AbstractDatabaseObjectListBoxController {
+       /**
+        * @inheritDoc
+        */
+       protected $supportedPositions = ['contentTop', 'contentBottom'];
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getObjectList() {
+               $commentObjectTypeID = CommentHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.page');
+               $commentManager = CommentHandler::getInstance()->getObjectType($commentObjectTypeID)->getProcessor();
+               
+               return CommentHandler::getInstance()->getCommentList($commentManager, $commentObjectTypeID, RequestHandler::getInstance()->getActiveRequest()->getPageID(), false);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getTemplate() {
+               return WCF::getTPL()->fetch('boxPageComments', 'wcf', [
+                       'commentCanAdd' => WCF::getSession()->getPermission('user.pageComment.canAddComment'),
+                       'commentList' => $this->objectList,
+                       'commentObjectTypeID' => CommentHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.page'),
+                       'lastCommentTime' => $this->objectList->getMinCommentTime(),
+                       'pageID' => RequestHandler::getInstance()->getActiveRequest()->getPageID()
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasContent() {
+               return WCF::getSession()->getPermission('user.pageComment.canAddComment') || parent::hasContent();
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/box/PageCommentsBoxController.class.php b/wcfsetup/install/files/lib/system/box/PageCommentsBoxController.class.php
deleted file mode 100644 (file)
index 85a00a6..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-namespace wcf\system\box;
-use wcf\system\comment\CommentHandler;
-use wcf\system\request\RequestHandler;
-use wcf\system\WCF;
-
-/**
- * Box for page comments.
- *
- * @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 PageCommentsBoxController extends AbstractBoxController {
-       /**
-        * @inheritDoc
-        */
-       protected $supportedPositions = ['contentTop', 'contentBottom'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.comments');
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       protected function loadContent() {
-               $commentObjectTypeID = CommentHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.page');
-               $commentManager = CommentHandler::getInstance()->getObjectType($commentObjectTypeID)->getProcessor();
-               $commentList = CommentHandler::getInstance()->getCommentList($commentManager, $commentObjectTypeID, RequestHandler::getInstance()->getActiveRequest()->getPageID());
-               
-               if (WCF::getSession()->getPermission('user.pageComment.canAddComment') || count($commentList)) {
-                       WCF::getTPL()->assign([
-                               'pageID' => RequestHandler::getInstance()->getActiveRequest()->getPageID(),
-                               'commentCanAdd' => WCF::getSession()->getPermission('user.pageComment.canAddComment'),
-                               'commentList' => $commentList,
-                               'commentObjectTypeID' => $commentObjectTypeID,
-                               'lastCommentTime' => $commentList->getMinCommentTime()
-                       ]);
-                       
-                       $this->content = WCF::getTPL()->fetch('boxPageComments');
-               }       
-       }
-}
index 7afa636ab947853a519c2d4612df99542808d74d..942b0d290e86226a59144550ddbbcee3329a04a3 100644 (file)
@@ -14,6 +14,7 @@ use wcf\system\WCF;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 class PaidSubscriptionsBoxController extends AbstractBoxController {
        /**
@@ -21,13 +22,6 @@ class PaidSubscriptionsBoxController extends AbstractBoxController {
         */
        protected $supportedPositions = ['contentTop', 'contentBottom', 'sidebarLeft', 'sidebarRight'];
        
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.user.menu.settings.paidSubscription');
-       }
-       
        /**
         * @inheritDoc
         */
@@ -86,7 +80,6 @@ class PaidSubscriptionsBoxController extends AbstractBoxController {
                                
                                $this->content = WCF::getTPL()->fetch($templateName);
                        }
-                       
                }
        }
 }
diff --git a/wcfsetup/install/files/lib/system/box/RecentActivityBoxController.class.php b/wcfsetup/install/files/lib/system/box/RecentActivityBoxController.class.php
deleted file mode 100644 (file)
index 70a3ac8..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-<?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');
-                       }
-               }
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php b/wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php
new file mode 100644 (file)
index 0000000..cdd616f
--- /dev/null
@@ -0,0 +1,123 @@
+<?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 controller for a list of recent activities.
+ * 
+ * @author     Matthias Schmidt
+ * @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
+ * @since      2.2
+ */
+class RecentActivityListBoxController extends AbstractDatabaseObjectListBoxController {
+       /**
+        * is true if the list of recent activity can be filtered to only include
+        * activities by followed users
+        * @var boolean
+        */
+       public $canFilterByFollowedUsers = false;
+       
+       /**
+        * is true if the list of recent activity is filtered to only include
+        * activities by followed users
+        * @var boolean
+        */
+       public $filteredByFollowedUsers = false;
+       
+       /**
+        * @inheritDoc
+        */
+       public $defaultLimit = 10;
+       
+       /**
+        * @inheritDoc
+        */
+       public $maximumLimit = 50;
+       
+       /**
+        * @inheritDoc
+        */
+       public $minimumLimit = 5;
+       
+       /**
+        * @inheritDoc
+        */
+       protected $supportedPositions = ['contentTop', 'contentBottom', 'sidebarLeft', 'sidebarRight'];
+       
+       /**
+        * @inheritDoc
+        */
+       public function __construct() {
+               if (WCF::getUser()->userID && count(WCF::getUserProfileHandler()->getFollowingUsers())) {
+                       $this->canFilterByFollowedUsers = true;
+               }
+               
+               if ($this->canFilterByFollowedUsers && WCF::getUser()->recentActivitiesFilterByFollowing) {
+                       $this->filteredByFollowedUsers = true;
+               }
+               
+               parent::__construct();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return LinkHandler::getInstance()->getLink('RecentActivityList');
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getObjectList() {
+               return new ViewableUserActivityEventList();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTemplate() {
+               if ($this->getBox()->position == 'contentTop' || $this->getBox()->position == 'contentBottom') {
+                       return WCF::getTPL()->fetch('boxRecentActivity', 'wcf', [
+                               'canFilterByFollowedUsers' => $this->canFilterByFollowedUsers,
+                               'eventList' => $this->objectList,
+                               'lastEventTime' => $this->objectList->getLastEventTime(),
+                               'filteredByFollowedUsers' => $this->filteredByFollowedUsers
+                       ]);
+               }
+               else {
+                       return WCF::getTPL()->fetch('boxRecentActivitySidebar', 'wcf', [
+                               'eventList' => $this->objectList
+                       ]);
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasLink() {
+               return true;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function readObjects() {
+               // apply filter
+               if (($this->getBox()->position == 'contentTop' || $this->getBox()->position == 'contentBottom') && $this->filteredByFollowedUsers) {
+                       $this->objectList->getConditionBuilder()->add('user_activity_event.userID IN (?)', [WCF::getUserProfileHandler()->getFollowingUsers()]);
+               }
+               
+               parent::readObjects();
+               
+               // removes orphaned and non-accessable events
+               UserActivityEventHandler::validateEvents($this->objectList);
+       }
+}
index 19ae0b0461615b25c55134e4715f1f79df5a74c4..dd8466e18e3d789d021831b464320d09887608dc 100644 (file)
@@ -4,13 +4,14 @@ use wcf\system\WCF;
 
 /**
  * Box that shows the register button.
- *
+ * 
  * @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
+ * @since      2.2
  */
 class RegisterButtonBoxController extends AbstractBoxController {
        /**
@@ -18,13 +19,6 @@ class RegisterButtonBoxController extends AbstractBoxController {
         */
        protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
        
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.user.register');
-       }
-       
        /**
         * @inheritDoc
         */
index 264a029fec7605087d8daeeaa85d2dcc3edab781..b9aa8e3a14e4f09002dff0e89a47650535af8548 100644 (file)
@@ -11,6 +11,7 @@ use wcf\system\WCF;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 class SignedInAsBoxController extends AbstractBoxController {
        /**
diff --git a/wcfsetup/install/files/lib/system/box/StaffOnlineBoxController.class.php b/wcfsetup/install/files/lib/system/box/StaffOnlineBoxController.class.php
deleted file mode 100644 (file)
index 6f66a6a..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-namespace wcf\system\box;
-use wcf\data\user\online\UsersOnlineList;
-use wcf\system\WCF;
-
-/**
- * Lists staff members who are online.
- *
- * @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 StaffOnlineBoxController extends AbstractBoxController {
-       /**
-        * @inheritDoc
-        */
-       protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.user.staffOnline'); // @todo
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       protected function loadContent() {
-               if (MODULE_USERS_ONLINE && WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList')) {
-                       $usersOnlineList = new UsersOnlineList();
-                       $usersOnlineList->getConditionBuilder()->add('session.userID IN (SELECT userID FROM wcf'.WCF_N.'_user_to_group WHERE groupID IN (SELECT groupID FROM wcf'.WCF_N.'_user_group WHERE showOnTeamPage = ?))', array(1));
-                       $usersOnlineList->readObjects();
-                       
-                       if (count($usersOnlineList)) {
-                               WCF::getTPL()->assign([
-                                       'usersOnlineList' => $usersOnlineList
-                               ]);
-                               
-                               $this->content = WCF::getTPL()->fetch('boxStaffOnline');
-                       }
-               }
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/box/StaffOnlineListBoxController.class.php b/wcfsetup/install/files/lib/system/box/StaffOnlineListBoxController.class.php
new file mode 100644 (file)
index 0000000..9914e8e
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\user\online\UsersOnlineList;
+use wcf\system\WCF;
+
+/**
+ * Box controller for a list of staff members who are currently online.
+ *
+ * @author     Matthias Schmidt
+ * @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
+ * @since      2.2
+ */
+class StaffOnlineListBoxController extends AbstractDatabaseObjectListBoxController {
+       /**
+        * @inheritDoc
+        */
+       protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getObjectList() {
+               $objectList = new UsersOnlineList();
+               $objectList->getConditionBuilder()->add('session.userID IN (SELECT userID FROM wcf'.WCF_N.'_user_to_group WHERE groupID IN (SELECT groupID FROM wcf'.WCF_N.'_user_group WHERE showOnTeamPage = ?))', [1]);
+               
+               return $objectList;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getTemplate() {
+               return WCF::getTPL()->fetch('boxStaffOnline', 'wcf', ['usersOnlineList' => $this->objectList]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasContent() {
+               if (!MODULE_USERS_ONLINE || WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList')) {
+                       return false;
+               }
+               
+               return parent::hasContent();
+       }
+}
\ No newline at end of file
index f7a57035e7192cea220edd2286389eebe0bf5031..d86bf9e0d18be5afb485990ac0e2836ec9ef43f1 100644 (file)
@@ -12,6 +12,7 @@ use wcf\system\WCF;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 class StatisticsBoxController extends AbstractBoxController {
        /**
@@ -19,13 +20,6 @@ class StatisticsBoxController extends AbstractBoxController {
         */
        protected $supportedPositions = ['sidebarLeft', 'sidebarRight'];
        
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.statistics'); // @todo
-       }
-       
        /**
         * @inheritDoc
         */
index 706788954794d1cd8ce8dd816800dcbb5fad705d..e0a300776a1b68866c707b5ac1b63e0f425fbe29 100644 (file)
@@ -33,13 +33,6 @@ class TagCloudBoxController extends AbstractBoxController {
         */
        protected $neededPermission = '';
        
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.tagging.tags');
-       }
-       
        /**
         * @inheritDoc
         */
index e32e00966a7d4b23a2ba3fc800df2653a706f085..43bb4eb7597633a46984838bafe3cc1b231272ec 100644 (file)
@@ -16,6 +16,7 @@ use wcf\util\DateUtil;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 class TodaysBirthdaysBoxController extends AbstractBoxController {
        /**
@@ -28,13 +29,6 @@ class TodaysBirthdaysBoxController extends AbstractBoxController {
         * @var string
         */
        protected $templateName = 'boxTodaysBirthdays';
-               
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.todaysBirthdays'); // @todo
-       }
        
        /**
         * @inheritDoc
@@ -84,5 +78,7 @@ class TodaysBirthdaysBoxController extends AbstractBoxController {
         * 
         * @param       integer[]       $userIDs
         */
-       protected function filterUserIDs(&$userIDs) {}
+       protected function filterUserIDs(&$userIDs) {
+               // does nothing, can be overwritten by child classes
+       }
 }
index f3e7999660915b31e5f45c50d513d161160363b7..49a62645e759aa301cd9dcc5e3dc12b41acecf52 100644 (file)
@@ -11,6 +11,7 @@ use wcf\system\WCF;
  * @package    com.woltlab.wcf
  * @subpackage system.box
  * @category   Community Framework
+ * @since      2.2
  */
 class TodaysFollowingBirthdaysBoxController extends TodaysBirthdaysBoxController {
        /**
@@ -18,13 +19,6 @@ class TodaysFollowingBirthdaysBoxController extends TodaysBirthdaysBoxController
         */
        protected $templateName = 'boxTodaysFollowingBirthdays';
        
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.page.todaysFollowingBirthdays'); // @todo
-       }
-       
        /**
         * @inheritDoc
         */
diff --git a/wcfsetup/install/files/lib/system/box/UserListBoxController.class.php b/wcfsetup/install/files/lib/system/box/UserListBoxController.class.php
new file mode 100644 (file)
index 0000000..0a63175
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\DatabaseObject;
+use wcf\data\user\UserProfileList;
+use wcf\system\cache\builder\MostActiveMembersCacheBuilder;
+use wcf\system\cache\builder\MostLikedMembersCacheBuilder;
+use wcf\system\cache\builder\NewestMembersCacheBuilder;
+use wcf\system\cache\runtime\UserProfileRuntimeCache;
+use wcf\system\event\EventHandler;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+
+/**
+ * Box controller for a list of users.
+ * 
+ * @author     Matthias Schmidt
+ * @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
+ * @since      2.2
+ */
+class UserListBoxController extends AbstractDatabaseObjectListBoxController {
+       /**
+        * maps special sort fields to cache builders
+        * @var string[]
+        */
+       public $cacheBuilders = [
+               'activityPoints' => MostActiveMembersCacheBuilder::class,
+               'likesReceived' => MostLikedMembersCacheBuilder::class,
+               'registrationDate' => NewestMembersCacheBuilder::class
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       protected $sortFieldLanguageItemPrefix = 'wcf.user';
+       
+       /**
+        * ids of the shown users loaded from cache
+        * @var integer[]|null
+        */
+       public $userIDs;
+       
+       /**
+        * @inheritDoc
+        */
+       public $validSortFields = [
+               'username',
+               'activityPoints',
+               'registrationDate'
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       public function __construct() {
+               if (!empty($this->validSortFields) && MODULE_LIKE) {
+                       $this->validSortFields[] = 'likesReceived';
+               }
+               
+               parent::__construct();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               if (MODULE_MEMBERS_LIST) {
+                       $parameters = '';
+                       if ($this->box->sortField) {
+                               $parameters = 'sortField='.$this->box->sortField.'&sortOrder='.$this->box->sortOrder;
+                       }
+                       
+                       return LinkHandler::getInstance()->getLink('MembersList', [], $parameters);
+               }
+               
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getObjectList() {
+               // use specialized cache builders
+               if ($this->box->sortOrder && $this->box->sortField && isset($this->cacheBuilders[$this->box->sortField])) {
+                       $this->userIDs = $this->cacheBuilders[$this->box->sortField]::getInstance()->getData([
+                               'limit' => $this->box->limit,
+                               'sortOrder' => $this->sortOrder
+                       ]);
+               }
+               
+               if ($this->userIDs !== null) {
+                       UserProfileRuntimeCache::getInstance()->cacheObjectIDs($this->userIDs);
+               }
+               
+               return new UserProfileList();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getTemplate() {
+               if ($this->userIDs !== null) {
+                       $userProfiles = UserProfileRuntimeCache::getInstance()->getObjects($this->userIDs);
+                       
+                       DatabaseObject::sort($userProfiles, $this->sortField, $this->sortOrder);
+               }
+               
+               return WCF::getTPL()->fetch('boxUserList', 'wcf', [
+                       'boxUsers' => $this->userIDs !== null ? $userProfiles : $this->objectList->getObjects(),
+                       'boxSortField' => $this->box->sortField
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasContent() {
+               $hasContent = parent::hasContent();
+               
+               if ($this->userIDs !== null) {
+                       return !empty($this->userIDs);
+               }
+               
+               return $hasContent;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasLink() {
+               return MODULE_MEMBERS_LIST == 1;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readObjects() {
+               if ($this->userIDs === null) {
+                       parent::readObjects();
+               }
+               else {
+                       EventHandler::getInstance()->fireAction($this, 'readObjects');
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/box/UserOnlineListBoxController.class.php b/wcfsetup/install/files/lib/system/box/UserOnlineListBoxController.class.php
new file mode 100644 (file)
index 0000000..505d9de
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\user\online\UsersOnlineList;
+use wcf\system\WCF;
+
+/**
+ * Box controller for a list of registered users who are currently online.
+ * 
+ * @author     Matthias Schmidt
+ * @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
+ * @since      2.2
+ */
+class UserOnlineListBoxController extends AbstractDatabaseObjectListBoxController {
+       /**
+        * @inheritDoc
+        */
+       protected $supportedPositions = ['footerBoxes', 'sidebarLeft', 'sidebarRight'];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return LinkHandler::getInstance()->getLink('UsersOnlineList');
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getObjectList() {
+               $objectList = new UsersOnlineList();
+               $objectList->readStats();
+               $objectList->checkRecord();
+               $objectList->getConditionBuilder()->add('session.userID IS NOT NULL');
+               
+               return $objectList;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getTemplate() {
+               $templateName = 'boxUsersOnlineSidebar';
+               if ($this->getBox()->position == 'footerBoxes') {
+                       $templateName = 'boxUsersOnline';
+               }
+               
+               return WCF::getTPL()->fetch($templateName, 'wcf', ['usersOnlineList' => $this->objectList]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasContent() {
+               if (!MODULE_USERS_ONLINE || WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList')) {
+                       return false;
+               }
+               
+               return parent::hasContent();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function hasLink() {
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/wcfsetup/install/files/lib/system/box/UsersOnlineBoxController.class.php b/wcfsetup/install/files/lib/system/box/UsersOnlineBoxController.class.php
deleted file mode 100644 (file)
index bfb4281..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-namespace wcf\system\box;
-use wcf\data\user\online\UsersOnlineList;
-use wcf\system\request\LinkHandler;
-use wcf\system\WCF;
-
-/**
- * Lists all users who are online.
- *
- * @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 UsersOnlineBoxController extends AbstractBoxController {
-       /**
-        * @inheritDoc
-        */
-       protected $supportedPositions = ['footerBoxes', 'sidebarLeft', 'sidebarRight'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return WCF::getLanguage()->get('wcf.user.usersOnline'); // @todo
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function getLink() {
-               return LinkHandler::getInstance()->getLink('UsersOnlineList');
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function hasLink() {
-               return true;
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       protected function loadContent() {
-               if (MODULE_USERS_ONLINE && WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList')) {
-                       $usersOnlineList = new UsersOnlineList();
-                       $usersOnlineList->readStats();
-                       $usersOnlineList->checkRecord();
-                       $usersOnlineList->getConditionBuilder()->add('session.userID IS NOT NULL');
-                       $usersOnlineList->readObjects();
-                       
-                       if (count($usersOnlineList)) {
-                               if ($this->getBox()->position == 'footerBoxes') {
-                                       $templateName = 'boxUsersOnline';
-                               }
-                               else {
-                                       $templateName = 'boxUsersOnlineSidebar';
-                               }
-                               
-                               WCF::getTPL()->assign([
-                                       'usersOnlineList' => $usersOnlineList
-                               ]);
-                               
-                               $this->content = WCF::getTPL()->fetch($templateName);
-                       }
-               }
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/cache/builder/AbstractSortedUserCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/AbstractSortedUserCacheBuilder.class.php
new file mode 100644 (file)
index 0000000..736fda5
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\user\UserList;
+
+/**
+ * Caches a list of the newest members.
+ *
+ * @author     Matthias Schmidt
+ * @copyright  2001-2016 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
+ * @since      2.2
+ */
+abstract class AbstractSortedUserCacheBuilder extends AbstractCacheBuilder {
+       /**
+        * default limit value if no limit parameter is provided
+        * @var integer
+        */
+       protected $defaultLimit = 5;
+       
+       /**
+        * default sort order if no sort order parameter is provided
+        * @var string
+        */
+       protected $defaultSortOrder = 'DESC';
+       
+       /**
+        * @inheritDoc
+        */
+       protected $maxLifetime = 300;
+       
+       /**
+        * if `true`, only positive values of the database column will be considered
+        * @var boolean
+        */
+       protected $positiveValuesOnly = false;
+       
+       /**
+        * database table column used for sorting
+        * @var string
+        */
+       protected $sortField;
+       
+       /**
+        * @inheritDoc
+        */
+       protected function rebuild(array $parameters) {
+               $sortOrder = $this->defaultSortOrder;
+               if (!empty($parameters['sortOrder'])) {
+                       $sortOrder = $parameters['sortOrder'];
+               }
+               
+               $userProfileList = new UserList();
+               if ($this->positiveValuesOnly) {
+                       $userProfileList->getConditionBuilder()->add('user_table.'.$this->sortField.' > ?', [0]);
+               }
+               $userProfileList->sqlOrderBy = 'user_table.'.$this->sortField.' '.$sortOrder;
+               $userProfileList->sqlLimit = !empty($parameters['limit']) ? $parameters['limit'] : $this->defaultLimit;
+               $userProfileList->readObjectIDs();
+               
+               return $userProfileList->getObjectIDs();
+       }
+}
index 04b8e1727f6f87f0da13491babcaa9c46ea2cd15..ca5b799007e387314b006548351a7647c8afc163 100644 (file)
@@ -6,28 +6,25 @@ use wcf\data\user\UserList;
  * Caches a list of the most active members.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage system.cache.builder
  * @category   Community Framework
  */
-class MostActiveMembersCacheBuilder extends AbstractCacheBuilder {
+class MostActiveMembersCacheBuilder extends AbstractSortedUserCacheBuilder {
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::$maxLifetime
+        * @inheritDoc
         */
        protected $maxLifetime = 600;
        
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+        * @inheritDoc
         */
-       protected function rebuild(array $parameters) {
-               $userProfileList = new UserList();
-               $userProfileList->getConditionBuilder()->add('user_table.activityPoints > 0');
-               $userProfileList->sqlOrderBy = 'user_table.activityPoints DESC';
-               $userProfileList->sqlLimit = 5;
-               $userProfileList->readObjectIDs();
-               
-               return $userProfileList->getObjectIDs();
-       }
+       protected $positiveValuesOnly = true;
+       
+       /**
+        * @inheritDoc
+        */
+       protected $sortField = 'activityPoints';
 }
index 639195b010073337f4f81fccd25cf7e978818f6e..b00bfe55ca1e59866bdb0abbd9580e46dfc4bf5a 100644 (file)
@@ -1,33 +1,29 @@
 <?php
 namespace wcf\system\cache\builder;
-use wcf\data\user\UserList;
 
 /**
  * Caches a list of the most liked members.
  * 
- * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @author     Matthias Schmidt
+ * @copyright  2001-2016 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 MostLikedMembersCacheBuilder extends AbstractCacheBuilder {
+class MostLikedMembersCacheBuilder extends AbstractSortedUserCacheBuilder {
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::$maxLifetime
+        * @inheritDoc
         */
        protected $maxLifetime = 600;
        
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+        * @inheritDoc
         */
-       protected function rebuild(array $parameters) {
-               $userProfileList = new UserList();
-               $userProfileList->getConditionBuilder()->add('user_table.likesReceived > 0');
-               $userProfileList->sqlOrderBy = 'user_table.likesReceived DESC';
-               $userProfileList->sqlLimit = 5;
-               $userProfileList->readObjectIDs();
-               
-               return $userProfileList->getObjectIDs();
-       }
+       protected $positiveValuesOnly = true;
+       
+       /**
+        * @inheritDoc
+        */
+       protected $sortField = 'likesReceived';
 }
index ae0f3ebe5fbb453580f640a0a92b8d0565f5b08d..fc3a3a4264a25a73e80cd6ac9eb963b5e68aceec 100644 (file)
@@ -1,32 +1,19 @@
 <?php
 namespace wcf\system\cache\builder;
-use wcf\data\user\UserList;
 
 /**
  * Caches a list of the newest members.
  * 
- * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @author     Matthias Schmidt
+ * @copyright  2001-2016 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 NewestMembersCacheBuilder extends AbstractCacheBuilder {
+class NewestMembersCacheBuilder extends AbstractSortedUserCacheBuilder {
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::$maxLifetime
+        * @inheritDoc
         */
-       protected $maxLifetime = 300;
-       
-       /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
-        */
-       protected function rebuild(array $parameters) {
-               $userProfileList = new UserList();
-               $userProfileList->sqlOrderBy = 'user_table.registrationDate DESC';
-               $userProfileList->sqlLimit = 5;
-               $userProfileList->readObjectIDs();
-               
-               return $userProfileList->getObjectIDs();
-       }
+       protected $sortField = 'registrationDate';
 }
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractCheckboxCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractCheckboxCondition.class.php
new file mode 100644 (file)
index 0000000..c90691e
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\DatabaseObject;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+
+/**
+ * Abstract implementation of a condition realized by a checkbox.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.condition
+ * @category   Community Framework
+ * @since      2.2
+ */
+abstract class AbstractCheckboxCondition extends AbstractSingleFieldCondition {
+       /**
+        * name of the checkbox
+        * @var string
+        */
+       protected $fieldName;
+       
+       /**
+        * is `1` if the checkbox is checked
+        * @var integer
+        */
+       protected $fieldValue = 0;
+       
+       /**
+        * @inheritDoc
+        * @throws      SystemException
+        */
+       public function __construct(DatabaseObject $object) {
+               parent::__construct($object);
+               
+               if ($this->fieldName === null) {
+                       throw new SystemException("Field name has not been set.");
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getData() {
+               if ($this->fieldValue) {
+                       return [$this->fieldName => $this->fieldValue];
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getFieldElement() {
+               return '<label><input type="checkbox" name="' . $this->fieldName . '" id="' . $this->fieldName . '"'.($this->fieldValue ? ' checked="checked"' : '').' /> '.WCF::getLanguage()->get($this->label).'</label>';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getLabel() {
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readFormParameters() {
+               if (!empty($_POST[$this->fieldName])) $this->fieldValue = 1;
+               else $this->fieldValue = 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function reset() {
+               $this->fieldValue = 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function setData(Condition $condition) {
+               $this->fieldValue = $condition->{$this->fieldName};
+       }
+}
\ No newline at end of file
index 4bc8f59c9c9ca15f1e216d572457f1c658a8151d..e3b4f2821de716f652703477db72c6a17d2c9be0 100644 (file)
@@ -18,7 +18,7 @@ interface ICondition extends IDatabaseObjectProcessor {
         * Returns the data saved with the condition used to check if the condition
         * is fulfilled. If null is returned, there is no condition to be created.
         * 
-        * @return      mixed
+        * @return      array|null
         */
        public function getData();
        
index fd01f9faa61abf0e3025255aa0d7e2bb880e18a6..eacbb4933a4629184cc011bce00753001a75a385 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\system\package\plugin;
 use wcf\data\box\Box;
 use wcf\data\box\BoxEditor;
+use wcf\data\object\type\ObjectType;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\language\LanguageFactory;
@@ -29,6 +30,12 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
         */
        protected $content = [];
        
+       /**
+        * list of element names which are not considered as additional data
+        * @var string[]
+        */
+       public static $reservedTags = ['boxType', 'content', 'cssClassName', 'name', 'objectType', 'position', 'showHeader', 'visibilityExceptions', 'visibleEverywhere'];
+       
        /**
         * @inheritDoc
         */
@@ -66,7 +73,7 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
        protected function getElement(\DOMXPath $xpath, array &$elements, \DOMElement $element) {
                $nodeValue = $element->nodeValue;
                
-               if ($element->tagName === 'name' || $element->tagName === 'title') {
+               if ($element->tagName === 'name') {
                        if (empty($element->getAttribute('language'))) {
                                throw new SystemException("Missing required attribute 'language' for '" . $element->tagName . "' element (box '" . $element->parentNode->getAttribute('identifier') . "')");
                        }
@@ -89,12 +96,9 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        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'],
+                               'content' => isset($children['content']) ? $children['content'] : '',
                                'title' => $children['title']
                        ];
                }
@@ -117,7 +121,7 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
        protected function prepareImport(array $data) {
                $content = [];
                $boxType = $data['elements']['boxType'];
-               $controller = '';
+               $objectTypeID = null;
                $identifier = $data['attributes']['identifier'];
                $isMultilingual = false;
                $position = $data['elements']['position'];
@@ -126,19 +130,38 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        throw new SystemException("Unknown box position '{$position}' for box '{$identifier}'");
                }
                
+               $ignoreMissingContent = false;
                switch ($boxType) {
                        case 'system':
-                               if (empty($data['elements']['controller'])) {
-                                       throw new SystemException("Missing required element 'controller' for 'system'-type box '{$identifier}'");
+                               if (empty($data['elements']['objectType'])) {
+                                       throw new SystemException("Missing required element 'objectType' for 'system'-type box '{$identifier}'");
                                }
                                
-                               $controller = $data['elements']['controller'];
-                               break;
+                               $sql = "SELECT          objectTypeID
+                                       FROM            wcf".WCF_N."_object_type object_type
+                                       LEFT JOIN       wcf".WCF_N."_object_type_definition object_type_definition
+                                       ON              (object_type_definition.definitionID = object_type.definitionID)
+                                       WHERE           objectType = ?
+                                                       AND definitionName = ?";
+                               $statement = WCF::getDB()->prepareStatement($sql);
+                               $statement->execute([$data['elements']['objectType'], 'com.woltlab.wcf.boxController']);
+                               $objectTypeID = $statement->fetchSingleColumn();
+                               if (!$objectTypeID) {
+                                       throw new SystemException("Unknown object type '{$data['elements']['objectType']}' for 'system'-type box '{$identifier}'");
+                               }
+                               
+                               $ignoreMissingContent = true;
+                               
+                               // fallthrough
                        
                        case 'html':
                        case 'text':
                        case 'tpl':
                                if (empty($data['elements']['content'])) {
+                                       if ($ignoreMissingContent) {
+                                               break;
+                                       }
+                                       
                                        throw new SystemException("Missing required 'content' element(s) for box '{$identifier}'");
                                }
                                
@@ -168,6 +191,13 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        $this->visibilityExceptions[$identifier] = $data['elements']['visibilityExceptions'];
                }
                
+               $additionalData = [];
+               foreach ($data['elements'] as $tagName => $nodeValue) {
+                       if (!in_array($tagName, self::$reservedTags)) {
+                               $additionalData[$tagName] = $nodeValue;
+                       }
+               }
+               
                return [
                        'identifier' => $identifier,
                        'content' => $content,
@@ -180,7 +210,8 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        'cssClassName' => (!empty($data['elements']['cssClassName'])) ? $data['elements']['cssClassName'] : '',
                        'showHeader' => (!empty($data['elements']['showHeader'])) ? 1 : 0,
                        'originIsSystem' => 1,
-                       'controller' => $controller
+                       'objectTypeID' => $objectTypeID,
+                       'additionalData' => serialize($additionalData)
                ];
        }
        
@@ -192,15 +223,15 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        FROM    wcf".WCF_N."_box
                        WHERE   identifier = ?
                                AND packageID = ?";
-               $parameters = array(
+               $parameters = [
                        $data['identifier'],
                        $this->installation->getPackageID()
-               );
+               ];
                
-               return array(
+               return [
                        'sql' => $sql,
                        'parameters' => $parameters
-               );
+               ];
        }
        
        /**
@@ -300,7 +331,7 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                $deleteStatement = WCF::getDB()->prepareStatement($sql);
                $sql = "INSERT IGNORE   wcf".WCF_N."_box_to_page
                                        (boxID, pageID, visible)
-                       VALUES          (?, ?, ?)";
+                       VALUES          (?, ?, ?)";
                $insertStatement = WCF::getDB()->prepareStatement($sql);
                foreach ($this->visibilityExceptions as $boxIdentifier => $pages) {
                        // delete old visibility exceptions
@@ -308,15 +339,15 @@ class BoxPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin
                        
                        // get page ids
                        $conditionBuilder = new PreparedStatementConditionBuilder();
-                       $conditionBuilder->add('identifier IN (?)', array($pages));
+                       $conditionBuilder->add('identifier IN (?)', [$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'];
+                       while ($pageID = $statement->fetchColumn()) {
+                               $pageIDs[] = $pageID;
                        }
                        
                        // save page ids
index ed6e4075c9cfea8d464de087c56ac299b1e72259..6513048bd35ccaaa43e47951e902fde0580a22c8 100644 (file)
                <item name="wcf.acp.option.category.dashboard"><![CDATA[Dashboard]]></item>
                <item name="wcf.acp.option.category.dashboard.content"><![CDATA[Inhaltsbereich]]></item>
                <item name="wcf.acp.option.category.dashboard.sidebar"><![CDATA[Seitenleiste]]></item>
-               <item name="wcf.acp.option.category.dashboard.content.recentActivities"><![CDATA[Letzte Aktivität]]></item>
-               <item name="wcf.acp.option.category.dashboard.sidebar.recentActivities"><![CDATA[Letzte Aktivität]]></item>
                <item name="wcf.acp.option.category.user.profile"><![CDATA[Profil]]></item>
                <item name="wcf.acp.option.category.user.avatar"><![CDATA[Avatar]]></item>
                <item name="wcf.acp.option.category.user.signature"><![CDATA[Signatur]]></item>
                <item name="wcf.acp.option.user_cleanup_activity_event_lifetime.description"><![CDATA[Zeitraum nach dem letzte Aktivitäten automatisch verworfen werden.]]></item>
                <item name="wcf.acp.option.user_cleanup_profile_visitor_lifetime"><![CDATA[Profil-Besucher]]></item>
                <item name="wcf.acp.option.user_cleanup_profile_visitor_lifetime.description"><![CDATA[Zeitraum nach dem Profil-Besucher automatisch verworfen werden.]]></item>
-               <item name="wcf.acp.option.recent_activity_items"><![CDATA[Anzahl Einträge]]></item>
-               <item name="wcf.acp.option.recent_activity_sidebar_items"><![CDATA[Anzahl Einträge]]></item>
                <item name="wcf.acp.option.category.message.general.likes"><![CDATA[Like-System]]></item>
                <item name="wcf.acp.option.module_like"><![CDATA[Like-System]]></item>
                <item name="wcf.acp.option.like_allow_for_own_content"><![CDATA[Benutzer können eigene Inhalte liken]]></item>
@@ -1932,11 +1928,6 @@ Fehler sind beispielsweise:
                <item name="wcf.condition.timestamp.error.startNotValid"><![CDATA[Das Enddatum ist ungültig.]]></item>
        </category>
        
-       <category name="wcf.dashboard">
-               <item name="wcf.dashboard.box.mostActiveMembers.points"><![CDATA[todo{#$activeMember->activityPoints} Punkt{if $activeMember->activityPoints != 1}e{/if}]]></item>
-               <item name="wcf.dashboard.box.mostLikedMembers.likes"><![CDATA[todo{#$likedMember->likesReceived} Like{if $likedMember->likesReceived != 1}s{/if}]]></item>
-       </category>
-       
        <category name="wcf.date">
                <item name="wcf.date.dateFormat"><![CDATA[j. F Y]]></item>
                <item name="wcf.date.timeFormat"><![CDATA[H:i]]></item>
@@ -2893,6 +2884,9 @@ Wenn Sie Probleme mit der Aktivierung haben, wenden Sie sich bitte an den Admini
                <item name="wcf.user.panel.markAsRead"><![CDATA[Als gelesen markieren]]></item>
                <item name="wcf.user.panel.settings"><![CDATA[Einstellungen]]></item>
                <item name="wcf.user.panel.showAll"><![CDATA[Alle anzeigen]]></item>
+               <item name="wcf.user.boxList.description.activityPoints"><![CDATA[{#$boxUser->activityPoints} Punkt{if $boxUser->activityPoints != 1}e{/if}]]></item>
+               <item name="wcf.user.boxList.description.likesReceived"><![CDATA[{#$boxUser->likesReceived} Like{if $boxUser->likesReceived != 1}s{/if}]]></item>
+               <item name="wcf.user.boxList.description.registrationDate"><![CDATA[{@$boxUser->registrationDate|time}]]></item>
        </category>
        
        <category name="wcf.user.menu">
index b3763fbe00e062293dbebfa21cb17edae6cea1b5..b9ea8f117722eb5e8ca1c73070ee31dceedaeba7 100644 (file)
@@ -879,8 +879,6 @@ Examples for medium ID detection:
                <item name="wcf.acp.option.category.dashboard"><![CDATA[Dashboard]]></item>
                <item name="wcf.acp.option.category.dashboard.content"><![CDATA[Content Area]]></item>
                <item name="wcf.acp.option.category.dashboard.sidebar"><![CDATA[Sidebar]]></item>
-               <item name="wcf.acp.option.category.dashboard.content.recentActivities"><![CDATA[Recent Activities]]></item>
-               <item name="wcf.acp.option.category.dashboard.sidebar.recentActivities"><![CDATA[Recent Activities]]></item>
                <item name="wcf.acp.option.category.user.profile"><![CDATA[Profile]]></item>
                <item name="wcf.acp.option.category.user.avatar"><![CDATA[Avatar]]></item>
                <item name="wcf.acp.option.category.user.signature"><![CDATA[Signature]]></item>
@@ -954,8 +952,6 @@ Examples for medium ID detection:
                <item name="wcf.acp.option.user_cleanup_activity_event_lifetime.description"><![CDATA[Recent Activities will be removed after the selected days.]]></item>
                <item name="wcf.acp.option.user_cleanup_profile_visitor_lifetime"><![CDATA[Profile Visitors]]></item>
                <item name="wcf.acp.option.user_cleanup_profile_visitor_lifetime.description"><![CDATA[Profile Visitors will be removed after the selected days.]]></item>
-               <item name="wcf.acp.option.recent_activity_items"><![CDATA[Number of Entries]]></item>
-               <item name="wcf.acp.option.recent_activity_sidebar_items"><![CDATA[Number of Entries]]></item>
                <item name="wcf.acp.option.category.message.general.likes"><![CDATA[Likes System]]></item>
                <item name="wcf.acp.option.module_like"><![CDATA[Likes System]]></item>
                <item name="wcf.acp.option.like_allow_for_own_content"><![CDATA[Users can like their own content]]></item>
@@ -1941,11 +1937,6 @@ Errors are:
                <item name="wcf.condition.timestamp.error.startNotValid"><![CDATA[The end date is not valid.]]></item>
        </category>
        
-       <category name="wcf.dashboard">
-               <item name="wcf.dashboard.box.mostActiveMembers.points"><![CDATA[todo{#$activeMember->activityPoints} Point{if $activeMember->activityPoints != 1}s{/if}]]></item>
-               <item name="wcf.dashboard.box.mostLikedMembers.likes"><![CDATA[todo{#$likedMember->likesReceived} Like{if $likedMember->likesReceived != 1}s{/if}]]></item>
-       </category>
-       
        <category name="wcf.date">
                <item name="wcf.date.dateFormat"><![CDATA[M jS Y]]></item>
                <item name="wcf.date.timeFormat"><![CDATA[g:ia]]></item>
@@ -2926,6 +2917,9 @@ If you cannot activate your email address or have any troubles following the ins
                <item name="wcf.user.panel.markAsRead"><![CDATA[Mark as Read]]></item>
                <item name="wcf.user.panel.settings"><![CDATA[Settings]]></item>
                <item name="wcf.user.panel.showAll"><![CDATA[Show All]]></item>
+               <item name="wcf.user.boxList.description.activityPoints"><![CDATA[{#$boxUser->activityPoints} Point{if $boxUser->activityPoints != 1}s{/if}]]></item>
+               <item name="wcf.user.boxList.description.likesReceived"><![CDATA[{#$boxUser->likesReceived} Like{if $boxUser->likesReceived != 1}s{/if}]]></item>
+               <item name="wcf.user.boxList.description.registrationDate"><![CDATA[{@$boxUser->registrationDate|time}]]></item>
        </category>
        
        <category name="wcf.user.menu">
index 4d976be425d26842c10c216447aa0b14cfed4e7a..4663990a54067ab470c147dd78dbe619d4af304e 100644 (file)
@@ -226,6 +226,7 @@ CREATE TABLE wcf1_bbcode_media_provider (
 DROP TABLE IF EXISTS wcf1_box;
 CREATE TABLE wcf1_box (
        boxID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       objectTypeID INT(10),
        identifier VARCHAR(255) NOT NULL,
        name VARCHAR(255) NOT NULL,
        boxType VARCHAR(255) NOT NULL,
@@ -237,11 +238,11 @@ CREATE TABLE wcf1_box (
        showHeader TINYINT(1) NOT NULL DEFAULT 1,
        originIsSystem TINYINT(1) NOT NULL DEFAULT 0,
        packageID INT(10) NOT NULL,
-       controller VARCHAR(255) NOT NULL DEFAULT '',
-       menuID INT(10),
+       menuID INT(10) NULL,
        linkPageID INT(10),
        linkPageObjectID INT(10) NOT NULL DEFAULT 0,
-       externalURL VARCHAR(255) NOT NULL DEFAULT ''
+       externalURL VARCHAR(255) NOT NULL DEFAULT '',
+       additionalData TEXT
 );
 
 DROP TABLE IF EXISTS wcf1_box_content;
@@ -1640,6 +1641,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 (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) 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 ADD FOREIGN KEY (linkPageID) REFERENCES wcf1_page (pageID) ON DELETE SET NULL;