Add condition and automatic user group assignment systems
authorMatthias Schmidt <gravatronics@live.com>
Tue, 13 May 2014 19:21:29 +0000 (21:21 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Tue, 13 May 2014 19:21:29 +0000 (21:21 +0200)
64 files changed:
com.woltlab.wcf/acpMenu.xml
com.woltlab.wcf/cronjob.xml
com.woltlab.wcf/objectType.xml
com.woltlab.wcf/objectTypeDefinition.xml
com.woltlab.wcf/userGroupOption.xml
wcfsetup/install/files/acp/templates/userConditions.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/userGroupAssignmentAdd.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/userGroupAssignmentList.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/userOptionsCondition.tpl [new file with mode: 0644]
wcfsetup/install/files/lib/acp/form/UserGroupAssignmentAddForm.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/acp/form/UserGroupAssignmentEditForm.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/acp/page/UserGroupAssignmentListPage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/condition/Condition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/condition/ConditionAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/condition/ConditionEditor.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/condition/ConditionList.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/user/UserProfile.class.php
wcfsetup/install/files/lib/data/user/UserProfileAction.class.php
wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignment.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentEditor.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentList.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/form/AvatarEditForm.class.php
wcfsetup/install/files/lib/system/cache/builder/ConditionCacheBuilder.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/cache/builder/UserGroupAssignmentCacheBuilder.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/AbstractCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/AbstractIntegerCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/AbstractMultipleFieldsCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/AbstractSelectCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/AbstractSingleFieldCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/AbstractTextCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/ConditionHandler.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/ICondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/IUserCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserAvatarCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserEmailCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserGroupCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserIntegerPropertyCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserLanguageCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserOptionsCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserRegistrationDateCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserRegistrationDateIntervalCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/condition/UserUsernameCondition.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/cronjob/UserGroupAssignmentCronjob.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/option/BirthdayOptionType.class.php
wcfsetup/install/files/lib/system/option/BooleanOptionType.class.php
wcfsetup/install/files/lib/system/option/ISearchableConditionUserOption.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/option/ISearchableUserOption.class.php
wcfsetup/install/files/lib/system/option/MultiSelectOptionType.class.php
wcfsetup/install/files/lib/system/option/OptionHandler.class.php
wcfsetup/install/files/lib/system/option/PasswordOptionType.class.php
wcfsetup/install/files/lib/system/option/RadioButtonOptionType.class.php
wcfsetup/install/files/lib/system/option/SelectOptionType.class.php
wcfsetup/install/files/lib/system/option/TextOptionType.class.php
wcfsetup/install/files/lib/system/option/TextareaOptionType.class.php
wcfsetup/install/files/lib/system/option/UserGroupOptionType.class.php
wcfsetup/install/files/lib/system/option/user/UserOptionHandler.class.php
wcfsetup/install/files/lib/system/user/activity/point/UserActivityPointHandler.class.php
wcfsetup/install/files/lib/system/user/group/assignment/UserGroupAssignmentHandler.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/util/DateUtil.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index 228bd5e3f4763a351a96cc8da2f1f9fb4df0cb41..ff2be66be1e93b7cd3d8227cc822b8134782ff33 100644 (file)
                        <parent>wcf.acp.menu.link.group</parent>
                        <permissions>admin.user.canMailUser</permissions>
                </acpmenuitem>
+               
+               <acpmenuitem name="wcf.acp.menu.link.group.assignment">
+                       <controller><![CDATA[wcf\acp\page\UserGroupAssignmentListPage]]></controller>
+                       <parent>wcf.acp.menu.link.group</parent>
+                       <permissions>admin.user.canManageGroupAssignment</permissions>
+               </acpmenuitem>
                <!-- user groups -->
                
                <acpmenuitem name="wcf.acp.menu.link.user.rank">
index de2e28988de83e943394fc9c6d757af7c7bd3b2d..6a46e9969f26f8363199dad20dd782526dfbaf69 100644 (file)
                        <canbeedited>1</canbeedited>
                        <canbedisabled>1</canbedisabled>
                </cronjob>
+               
+               <cronjob>
+                       <classname><![CDATA[wcf\system\cronjob\UserGroupAssignmentCronjob]]></classname>
+                       <description><![CDATA[Automatically assigns users to user groups]]></description>
+                       <description language="de"><![CDATA[Ordnet Benutzer automatisch Benutzergruppen zu]]></description>
+                       <startminute>*/30</startminute>
+                       <starthour>*</starthour>
+                       <startdom>*</startdom>
+                       <startmonth>*</startmonth>
+                       <startdow>*</startdow>
+                       <active>1</active>
+                       <canbeedited>1</canbeedited>
+                       <canbedisabled>1</canbedisabled>
+               </cronjob>
        </import>
 </data>
\ No newline at end of file
index 95d095a02f818dcd5e036fa37ec92daf0723b277..7e5123e4495d21ec25197d8275e5abe8fddea4fd 100644 (file)
                        <classname><![CDATA[wcf\system\stat\DislikeStatDailyHandler]]></classname>
                </type>
                <!-- /stat handlers -->
+               
+               <!-- user group assignment conditions -->
+               <type>
+                       <name>com.woltlab.wcf.username</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserUsernameCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.email</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserEmailCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.userGroup</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserGroupCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.languages</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserLanguageCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.registrationDate</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserRegistrationDateCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.registrationDateInterval</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserRegistrationDateIntervalCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.avatar</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserAvatarCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.state</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserStateCondition]]></classname>
+                       <conditiongroup>general</conditiongroup>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.activityPoints</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserIntegerPropertyCondition]]></classname>
+                       <conditiongroup>contents</conditiongroup>
+                       <propertyname>activityPoints</propertyname>
+                       <minvalue>0</minvalue>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.likesReceived</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserIntegerPropertyCondition]]></classname>
+                       <conditiongroup>contents</conditiongroup>
+                       <propertyname>likesReceived</propertyname>
+                       <minvalue>0</minvalue>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.userOptions</name>
+                       <definitionname>com.woltlab.wcf.condition.userGroupAssignment</definitionname>
+                       <classname><![CDATA[wcf\system\condition\UserOptionsCondition]]></classname>
+                       <conditiongroup>userOptions</conditiongroup>
+               </type>
+               <!-- /user group assignment conditions -->
        </import>
-</data>
\ No newline at end of file
+</data>
index aa717e42fb024c8fd077ca97486291f374380f44..0f385cf5e24c56e6ae9a0ae29456917a707b7714 100644 (file)
                        <name>com.woltlab.wcf.statDailyHandler</name>
                        <interfacename><![CDATA[wcf\system\stat\IStatDailyHandler]]></interfacename>
                </definition>
+               
+               <definition>
+                       <name>com.woltlab.wcf.condition.userGroupAssignment</name>
+                       <interfacename><![CDATA[wcf\system\condition\IUserCondition]]></interfacename>
+               </definition>
        </import>
 </data>
index a14e67de1b49c36d4d7b5269f5b9b1e9873084d6..ac5ac5cc0f89390eb983de089e6e30c31e46bfec 100644 (file)
                                <defaultvalue>0</defaultvalue>
                                <admindefaultvalue>1</admindefaultvalue>
                        </option>
+                       <option name="admin.user.canManageGroupAssignment">
+                               <categoryname>admin.user.group</categoryname>
+                               <optiontype>boolean</optiontype>
+                               <defaultvalue>0</defaultvalue>
+                               <admindefaultvalue>1</admindefaultvalue>
+                       </option>
                        
                        <option name="admin.user.canManageUserOption">
                                <categoryname>admin.user.option</categoryname>
diff --git a/wcfsetup/install/files/acp/templates/userConditions.tpl b/wcfsetup/install/files/acp/templates/userConditions.tpl
new file mode 100644 (file)
index 0000000..d1382ad
--- /dev/null
@@ -0,0 +1,27 @@
+<div class="tabMenuContainer">
+       <nav class="tabMenu">
+               <ul>
+                       {foreach from=$groupedObjectTypes key='conditionGroup' item='conditionObjectTypes'}
+                               {assign var='__anchor' value='user_'|concat:$conditionGroup}
+                               <li><a href="{@$__wcf->getAnchor($__anchor)}">{lang}wcf.user.condition.conditionGroup.{$conditionGroup}{/lang}</a></li>
+                       {/foreach}
+               </ul>
+       </nav>
+       
+       {foreach from=$groupedObjectTypes key='conditionGroup' item='conditionObjectTypes'}
+               <div id="user_{$conditionGroup}" class="container containerPadding tabMenuContainer tabMenuContent">
+                       {if $conditionGroup != 'userOptions'}
+                               <fieldset>
+                                       <legend>{lang}wcf.user.condition.conditionGroup.{$conditionGroup}{/lang}</legend>
+                       {/if}
+                       
+                       {foreach from=$conditionObjectTypes item='condition'}
+                               {@$condition->getProcessor()->getHtml()}
+                       {/foreach}
+                       
+                       {if $conditionGroup != 'userOptions'}
+                               </fieldset>
+                       {/if}
+               </div>
+       {/foreach}
+</div>
diff --git a/wcfsetup/install/files/acp/templates/userGroupAssignmentAdd.tpl b/wcfsetup/install/files/acp/templates/userGroupAssignmentAdd.tpl
new file mode 100644 (file)
index 0000000..8f28e8d
--- /dev/null
@@ -0,0 +1,93 @@
+{include file='header' pageTitle='wcf.acp.group.assignment.'|concat:$action}
+
+<script data-relocate="true">
+       //<![CDATA[
+       $(function() {
+               WCF.TabMenu.init();
+       });
+       //]]>
+</script>
+
+<header class="boxHeadline">
+       <h1>{lang}wcf.acp.group.assignment.{$action}{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+       <p class="success">{lang}wcf.global.success.{@$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+       <nav>
+               <ul>
+                       <li><a href="{link controller='UserGroupAssignmentList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.group.assignment.button.list{/lang}</span></a></li>
+                       
+                       {event name='contentNavigationButtons'}
+               </ul>
+       </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='UserGroupAssignmentAdd'}{/link}{else}{link controller='UserGroupAssignmentEdit' object=$assignment}{/link}{/if}">
+       <div class="container containerPadding marginTop">
+               <fieldset>
+                       <legend>{lang}wcf.global.form.data{/lang}</legend>
+                       
+                       <dl{if $errorField == 'title'} class="formError"{/if}>
+                               <dt><label for="title">{lang}wcf.global.name{/lang}</label></dt>
+                               <dd>
+                                       <input type="text" id="title" name="title" value="{$title}" class="long" />
+                                       {if $errorField == 'title'}
+                                               <small class="innerError">
+                                                       {if $errorType == 'empty'}
+                                                               {lang}wcf.global.form.error.empty{/lang}
+                                                       {else}
+                                                               {lang}wcf.acp.group.assignment.title.error.{@$errorType}{/lang}
+                                                       {/if}
+                                               </small>
+                                       {/if}
+                               </dd>
+                       </dl>
+                       
+                       <dl{if $errorField == 'groupID'} class="formError"{/if}>
+                               <dt><label for="groupID">{lang}wcf.user.group{/lang}</label></dt>
+                               <dd>
+                                       {htmlOptions name='groupID' options=$userGroups selected=$groupID}
+                                       {if $errorField == 'groupID'}
+                                               <small class="innerError">{lang}wcf.acp.group.assignment.groupID.error.{@$errorType}{/lang}</small>
+                                       {/if}
+                               </dd>
+                       </dl>
+                       
+                       <dl class="formError">
+                               <dt></dt>
+                               <dd>
+                                       <label><input type="checkbox" id="isDisabled" name="isDisabled"{if $isDisabled} checked="checked"{/if} /> {lang}wcf.acp.group.assignment.isDisabled{/lang}</label>
+                               </dd>
+                       </dl>
+                       
+                       {event name='dataFields'}
+               </fieldset>
+               
+               {event name='fieldsets'}
+       </div>
+       
+       <header class="boxHeadline boxSubHeadline">
+               <h2>{lang}wcf.acp.group.assignment.conditions{/lang}</h2>
+               <small>{lang}wcf.acp.group.assignment.conditions.description{/lang}</small>
+       </header>
+       
+       {if $errorField == 'conditions'}
+               <p class="error">{lang}wcf.acp.group.assignment.error.noConditions{/lang}</p>
+       {/if}
+       
+       {include file='userConditions'}
+       
+       <div class="formSubmit">
+               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+               <input type="hidden" name="action" value="{@$action}" />
+               {@SECURITY_TOKEN_INPUT_TAG}
+       </div>
+</form>
+
+{include file='footer'}
diff --git a/wcfsetup/install/files/acp/templates/userGroupAssignmentList.tpl b/wcfsetup/install/files/acp/templates/userGroupAssignmentList.tpl
new file mode 100644 (file)
index 0000000..1c29b3f
--- /dev/null
@@ -0,0 +1,97 @@
+{include file='header' pageTitle='wcf.acp.group.assignment.list'}
+
+<script data-relocate="true">
+       //<![CDATA[
+       $(function() {
+               new WCF.Action.Delete('wcf\\data\\user\\group\\assignment\\UserGroupAssignmentAction', '.jsUserGroupAssignmentRow');
+               new WCF.Action.Toggle('wcf\\data\\user\\group\\assignment\\UserGroupAssignmentAction', '.jsUserGroupAssignmentRow');
+               
+               var options = { };
+               {if $pages > 1}
+                       options.refreshPage = true;
+                       {if $pages == $pageNo}
+                               options.updatePageNumber = -1;
+                       {/if}
+               {else}
+                       options.emptyMessage = '{lang}wcf.global.noItems{/lang}';
+               {/if}
+               
+               new WCF.Table.EmptyTableHandler($('#userGroupAssignmentTableContainer'), 'jsUserGroupAssignmentRow', options);
+       });
+       //]]>
+</script>
+
+<header class="boxHeadline">
+       <h1>{lang}wcf.acp.group.assignment.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+       {pages print=true assign=pagesLinks controller="UserGroupAssignmentList" link="pageNo=%d"}
+       
+       <nav>
+               <ul>
+                       <li><a href="{link controller='UserGroupAssignmentAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.group.assignment.button.add{/lang}</span></a></li>
+                       
+                       {event name='contentNavigationButtonsTop'}
+               </ul>
+       </nav>
+</div>
+
+{if $objects|count}
+       <div class="tabularBox tabularBoxTitle marginTop" id="userGroupAssignmentTableContainer">
+               <header>
+                       <h2>{lang}wcf.acp.group.assignment.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+               </header>
+               
+               <table class="table">
+                       <thead>
+                               <tr>
+                                       <th class="columnID columnAssignmentID" colspan="2"><span>{lang}wcf.global.objectID{/lang}</span></th>
+                                       <th class="columnTitle columnAssignmentName"><span>{lang}wcf.global.name{/lang}</span></th>
+                                       <th class="columnTitle columnGroupName"><span>{lang}wcf.acp.group.assignment.userGroup{/lang}</span></th>
+                                       
+                                       {event name='columnHeads'}
+                               </tr>
+                       </thead>
+                       
+                       <tbody>
+                               {foreach from=$objects item='assignment'}
+                                       <tr class="jsUserGroupAssignmentRow">
+                                               <td class="columnIcon">
+                                                       <span class="icon icon16 icon-check{if $assignment->isDisabled}-empty{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if $assignment->isDisabled}enable{else}disable{/if}{/lang}" data-object-id="{@$assignment->assignmentID}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}"></span>
+                                                       <a href="{link controller='UserGroupAssignmentEdit' object=$assignment}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
+                                                       <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$assignment->assignmentID}" data-confirm-message="{lang}wcf.acp.group.assignment.delete.confirmMessage{/lang}"></span>
+                                                       
+                                                       {event name='rowButtons'}
+                                               </td>
+                                               <td class="columnID columnAssignmentID">{@$assignment->assignmentID}</td>
+                                               <td class="columnTitle columnAssignmentName">
+                                                       <a href="{link controller='UserGroupAssignmentEdit' object=$assignment}{/link}">{$assignment->title}</a>
+                                               </td>
+                                               <td class="columnDigits columnGroupName">
+                                                       {$assignment->getUserGroup()->getName()}
+                                               </td>
+                                               
+                                               {event name='columns'}
+                                       </tr>
+                               {/foreach}
+                       </tbody>
+               </table>
+       </div>
+       
+       <div class="contentNavigation">
+               {@$pagesLinks}
+               
+               <nav>
+                       <ul>
+                               <li><a href="{link controller='UserGroupAssignmentAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.group.assignment.button.add{/lang}</span></a></li>
+                               
+                               {event name='contentNavigationButtonsBottom'}
+                       </ul>
+               </nav>
+       </div>
+{else}
+       <p class="info">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+{include file='footer'}
diff --git a/wcfsetup/install/files/acp/templates/userOptionsCondition.tpl b/wcfsetup/install/files/acp/templates/userOptionsCondition.tpl
new file mode 100644 (file)
index 0000000..874b635
--- /dev/null
@@ -0,0 +1,8 @@
+{foreach from=$optionTree item='category'}
+       <fieldset>
+               <legend>{lang}wcf.user.option.category.{@$category[object]->categoryName}{/lang}</legend>
+               {hascontent}<p>{content}{lang __optional=true}wcf.user.option.category.{@$category[object]->categoryName}.description{/lang}{/content}</p>{/hascontent}
+               
+               {include file='optionFieldList' options=$category[options] langPrefix='wcf.user.option.'}
+       </fieldset>
+{/foreach}
diff --git a/wcfsetup/install/files/lib/acp/form/UserGroupAssignmentAddForm.class.php b/wcfsetup/install/files/lib/acp/form/UserGroupAssignmentAddForm.class.php
new file mode 100644 (file)
index 0000000..6897fda
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+namespace wcf\acp\form;
+use wcf\data\user\group\assignment\UserGroupAssignmentAction;
+use wcf\data\user\group\UserGroup;
+use wcf\form\AbstractForm;
+use wcf\system\condition\ConditionHandler;
+use wcf\system\exception\UserInputException;
+use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the form to create a new automatic user group assignment.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage acp.form
+ * @category   Community Framework
+ */
+class UserGroupAssignmentAddForm extends AbstractForm {
+       /**
+        * @see \wcf\page\AbstractPage::$activeMenuItem
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.group.assignment';
+       
+       /**
+        * list of grouped user group assignment condition object types
+        * @var array
+        */
+       public $conditions = array();
+       
+       /**
+        * id of the selected user group
+        * @var integer
+        */
+       public $groupID = 0;
+       
+       /**
+        * true if the automatic assignment is disabled
+        * @var integer
+        */
+       public $isDisabled = 0;
+       
+       /**
+        * @see \wcf\page\AbstractPage::$neededPermissions
+        */
+       public $neededPermissions = array('admin.user.canManageGroupAssignment');
+       
+       /**
+        * title of the user group assignment
+        * @var string
+        */
+       public $title = '';
+       
+       /**
+        * list of selectable user groups
+        * @var array<\wcf\data\user\group\UserGroup>
+        */
+       public $userGroups = array();
+       
+       /**
+        * @see \wcf\page\IPage::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign(array(
+                       'action' => 'add',
+                       'groupedObjectTypes' => $this->conditions,
+                       'groupID' => $this->groupID,
+                       'isDisabled' => $this->isDisabled,
+                       'title' => $this->title,
+                       'userGroups' => $this->userGroups
+               ));
+       }
+       
+       /**
+        * @see \wcf\page\IPage::readData()
+        */
+       public function readData() {
+               $this->userGroups = UserGroup::getGroupsByType(array(UserGroup::OTHER));
+               foreach ($this->userGroups as $key => $userGroup) {
+                       if (!$userGroup->isAccessible()) {
+                               unset($this->userGroups[$key]);
+                       }
+               }
+               
+               uasort($this->userGroups, function(UserGroup $groupA, UserGroup $groupB) {
+                       return strcmp($groupA->getName(), $groupB->getName());
+               });
+               
+               $this->conditions = UserGroupAssignmentHandler::getInstance()->getGroupedObjectTypes('com.woltlab.wcf.condition.userGroupAssignment');
+               
+               parent::readData();
+       }
+       
+       /**
+        * @see \wcf\form\IForm::readFormParameters()
+        */
+       public function readFormParameters() {
+               parent::readFormParameters();
+               
+               if (isset($_POST['groupID'])) $this->groupID = intval($_POST['groupID']);
+               if (isset($_POST['isDisabled'])) $this->isDisabled = 1;
+               if (isset($_POST['title'])) $this->title = StringUtil::trim($_POST['title']);
+               
+               foreach ($this->conditions as $conditions) {
+                       foreach ($conditions as $condition) {
+                               $condition->getProcessor()->readFormParameters();
+                       }
+               }
+       }
+       
+       /**
+        * @see \wcf\form\IForm::save()
+        */
+       public function save() {
+               parent::save();
+               
+               $this->objectAction = new UserGroupAssignmentAction(array(), 'create', array(
+                       'data' => array(
+                               'groupID' => $this->groupID,
+                               'isDisabled' => $this->isDisabled,
+                               'title' => $this->title
+                       )
+               ));
+               $returnValues = $this->objectAction->executeAction();
+               
+               ConditionHandler::getInstance()->createConditions($returnValues['returnValues']->assignmentID, $this->conditions);
+               
+               $this->saved();
+               
+               // reset values
+               $this->groupID = 0;
+               $this->isDisabled = 0;
+               $this->title = '';
+               
+               foreach ($this->conditions as $conditions) {
+                       foreach ($conditions as $condition) {
+                               $condition->getProcessor()->reset();
+                       }
+               }
+               
+               WCF::getTPL()->assign('success', true);
+       }
+       
+       /**
+        * @see \wcf\form\IForm::validate()
+        */
+       public function validate() {
+               parent::validate();
+               
+               if (empty($this->title)) {
+                       throw new UserInputException('title');
+               }
+               if (strlen($this->title) > 255) {
+                       throw new UserInputException('title', 'tooLong');
+               }
+               
+               if (!isset($this->userGroups[$this->groupID])) {
+                       throw new UserInputException('groupID', 'notValid');
+               }
+               
+               $hasData = false;
+               foreach ($this->conditions as $conditions) {
+                       foreach ($conditions as $condition) {
+                               $condition->getProcessor()->validate();
+                               
+                               if (!$hasData && $condition->getProcessor()->getData() !== null) {
+                                       $hasData = true;
+                               }
+                       }
+               }
+               
+               if (!$hasData) {
+                       throw new UserInputException('conditions');
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/acp/form/UserGroupAssignmentEditForm.class.php b/wcfsetup/install/files/lib/acp/form/UserGroupAssignmentEditForm.class.php
new file mode 100644 (file)
index 0000000..9e5fa51
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+namespace wcf\acp\form;
+use wcf\data\user\group\assignment\UserGroupAssignment;
+use wcf\data\user\group\assignment\UserGroupAssignmentAction;
+use wcf\form\AbstractForm;
+use wcf\system\condition\ConditionHandler;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+
+/**
+ * Shows the form to edit an existing automatic user group assignment.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage acp.form
+ * @category   Community Framework
+ */
+class UserGroupAssignmentEditForm extends UserGroupAssignmentAddForm {
+       /**
+        * edited automatic user group assignment
+        * @var \wcf\data\user\group\assignment\UserGroupAssignment
+        */
+       public $assignment = null;
+       
+       /**
+        * id of the edited automatic user group assignment
+        * @var integer
+        */
+       public $assignmentID = 0;
+       
+       /**
+        * @see \wcf\page\IPage::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign(array(
+                       'action' => 'edit',
+                       'assignment' => $this->assignment
+               ));
+       }
+       
+       /**
+        * @see \wcf\page\IPage::readData()
+        */
+       public function readData() {
+               parent::readData();
+               
+               if (empty($_POST)) {
+                       $this->groupID = $this->assignment->groupID;
+                       $this->title = $this->assignment->title;
+                       
+                       $conditions = $this->assignment->getConditions();
+                       foreach ($conditions as $condition) {
+                               $this->conditions[$condition->getObjectType()->conditiongroup][$condition->objectTypeID]->getProcessor()->setData($condition);
+                       }
+               }
+       }
+       
+       /**
+        * @see \wcf\page\IPage::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+               
+               if (isset($_REQUEST['id'])) $this->assignmentID = intval($_REQUEST['id']);
+               $this->assignment = new UserGroupAssignment($this->assignmentID);
+               if (!$this->assignment->assignmentID) {
+                       throw new IllegalLinkException();
+               }
+       }
+       
+       /**
+        * @see \wcf\form\IForm::save()
+        */
+       public function save() {
+               AbstractForm::save();
+               
+               $this->objectAction = new UserGroupAssignmentAction(array($this->assignment), 'update', array(
+                       'data' => array(
+                               'groupID' => $this->groupID,
+                               'isDisabled' => $this->isDisabled,
+                               'title' => $this->title
+                       )
+               ));
+               $returnValues = $this->objectAction->executeAction();
+               
+               ConditionHandler::getInstance()->updateConditions($this->assignment->assignmentID, $this->assignment->getConditions(), $this->conditions);
+               
+               $this->saved();
+               
+               WCF::getTPL()->assign('success', true);
+       }
+}
diff --git a/wcfsetup/install/files/lib/acp/page/UserGroupAssignmentListPage.class.php b/wcfsetup/install/files/lib/acp/page/UserGroupAssignmentListPage.class.php
new file mode 100644 (file)
index 0000000..929efb5
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+namespace wcf\acp\page;
+use wcf\page\MultipleLinkPage;
+
+/**
+ * Lists the available automatic user group assignments.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage acp.page
+ * @category   Community Framework
+ */
+class UserGroupAssignmentListPage extends MultipleLinkPage {
+       /**
+        * @see \wcf\page\AbstractPage::$activeMenuItem
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.group.assignment';
+       
+       /**
+        * @see \wcf\page\AbstractPage::$neededPermissions
+        */
+       public $neededPermissions = array('admin.user.canManageGroupAssignment');
+       
+       /**
+        * @see \wcf\page\MultipleLinkPage::$objectListClassName
+        */
+       public $objectListClassName = 'wcf\data\user\group\assignment\UserGroupAssignmentList';
+}
diff --git a/wcfsetup/install/files/lib/data/condition/Condition.class.php b/wcfsetup/install/files/lib/data/condition/Condition.class.php
new file mode 100644 (file)
index 0000000..d7c48cc
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+namespace wcf\data\condition;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\DatabaseObject;
+
+/**
+ * Represents a condition.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.condition
+ * @category   Community Framework
+ */
+class Condition extends DatabaseObject {
+       /**
+        * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+        */
+       protected static $databaseTableIndexName = 'conditionID';
+       
+       /**
+        * @see \wcf\data\DatabaseObject::$databaseTableName
+        */
+       protected static $databaseTableName = 'condition';
+       
+       /**
+        * @see \wcf\data\IStorableObject::__get()
+        */
+       public function __get($name) {
+               $value = parent::__get($name);
+               
+               // treat condition data as data variables if it is an array
+               if ($value === null && is_array($this->data['conditionData']) && isset($this->data['conditionData'][$name])) {
+                       $value = $this->data['conditionData'][$name];
+               }
+               
+               return $value;
+       }
+       
+       /**
+        * Returns the condition object type of the condition.
+        * 
+        * @return      \wcf\data\object\type\ObjectType
+        */
+       public function getObjectType() {
+               return ObjectTypeCache::getInstance()->getObjectType($this->objectTypeID);
+       }
+       
+       /**
+        * @see \wcf\data\DatabaseObject::handleData()
+        */
+       protected function handleData($data) {
+               parent::handleData($data);
+               
+               // handle condition data
+               $this->data['conditionData'] = @unserialize($data['conditionData']);
+               if (!is_array($this->data['conditionData'])) {
+                       $this->data['conditionData'] = array();
+               }
+       }
+       
+       /**
+        * @see \wcf\data\IStorableObject::getDatabaseTableAlias()
+        */
+       public static function getDatabaseTableAlias() {
+               return 'condition_table';
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/condition/ConditionAction.class.php b/wcfsetup/install/files/lib/data/condition/ConditionAction.class.php
new file mode 100644 (file)
index 0000000..128f2ea
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+namespace wcf\data\condition;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes condition-related actions.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.condition
+ * @category   Community Framework
+ */
+class ConditionAction extends AbstractDatabaseObjectAction { }
diff --git a/wcfsetup/install/files/lib/data/condition/ConditionEditor.class.php b/wcfsetup/install/files/lib/data/condition/ConditionEditor.class.php
new file mode 100644 (file)
index 0000000..6effdca
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+namespace wcf\data\condition;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\ConditionCacheBuilder;
+
+/**
+ * Executes condition-related actions.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.condition
+ * @category   Community Framework
+ */
+class ConditionEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+       /**
+        * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+        */
+       protected static $baseClass = 'wcf\data\condition\Condition';
+       
+       /**
+        * @see \wcf\data\IEditableCachedObject::resetCache()
+        */
+       public static function resetCache() {
+               ConditionCacheBuilder::getInstance()->reset();
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/condition/ConditionList.class.php b/wcfsetup/install/files/lib/data/condition/ConditionList.class.php
new file mode 100644 (file)
index 0000000..c8e9b81
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+namespace wcf\data\condition;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of conditions.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.condition
+ * @category   Community Framework
+ */
+class ConditionList extends DatabaseObjectList { }
index c2be42b3ac1340e9bc9261a4e9e6fd8777799c7b..1df130f6905d3f276452bfd34c782159c9aebbae 100644 (file)
@@ -510,7 +510,6 @@ class UserProfile extends DatabaseObjectDecorator implements IBreadcrumbProvider
                                if ($birthdayYear) {
                                        return $year - $birthdayYear;
                                }
-                               
                        }
                        
                        return 0;
@@ -524,14 +523,14 @@ class UserProfile extends DatabaseObjectDecorator implements IBreadcrumbProvider
                                        $this->__age = 0;
                                }
                        }
-               
+                       
                        return $this->__age;
                }
        }
        
        /**
         * Returns the formatted birthday of this user.
-        *
+        * 
         * @param       integer         $year
         * @return      string
         */
index aea95cd60b01a38cd9c48726b5e5afb3478c2810..4a9465f26d50ac7b8988d3e0fd8d4c8843e33a24 100644 (file)
@@ -7,6 +7,7 @@ use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\option\user\UserOptionHandler;
+use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
 use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
 use wcf\util\StringUtil;
@@ -272,6 +273,10 @@ class UserProfileAction extends UserAction {
                        $userAction = new UserAction(array($this->userProfile->userID), 'update', $data);
                        $userAction->executeAction();
                        
+                       // check if the user will be automatically added to new
+                       // user groups because of the changed user options
+                       UserGroupAssignmentHandler::getInstance()->checkUsers(array($this->userProfile->userID));
+                       
                        // return parsed template
                        $user = new User($this->userProfile->userID);
                        
diff --git a/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignment.class.php b/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignment.class.php
new file mode 100644 (file)
index 0000000..51dc17f
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+namespace wcf\data\user\group\assignment;
+use wcf\data\user\group\UserGroup;
+use wcf\data\DatabaseObject;
+use wcf\system\condition\ConditionHandler;
+use wcf\system\request\IRouteController;
+
+/**
+ * Represents an automatic assignement to a user group.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.user.group.assignment
+ * @category   Community Framework
+ */
+class UserGroupAssignment extends DatabaseObject implements IRouteController {
+       /**
+        * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+        */
+       protected static $databaseTableIndexName = 'assignmentID';
+       
+       /**
+        * @see \wcf\data\DatabaseObject::$databaseTableName
+        */
+       protected static $databaseTableName = 'user_group_assignment';
+       
+       /**
+        * Returns the conditions of the automatic assignement to a user group.
+        * 
+        * @return      array<\wcf\data\condition\Condition>
+        */
+       public function getConditions() {
+               return ConditionHandler::getInstance()->getConditions('com.woltlab.wcf.condition.userGroupAssignment', $this->assignmentID);
+       }
+       
+       /**
+        * @see \wcf\data\ITitledObject::getTitle()
+        */
+       public function getTitle() {
+               return $this->title;
+       }
+       
+       /**
+        * Returns the user group the automatic assignement belongs to.
+        * 
+        * @return      \wcf\data\user\group\UserGroup
+        */
+       public function getUserGroup() {
+               return UserGroup::getGroupByID($this->groupID);
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentAction.class.php b/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentAction.class.php
new file mode 100644 (file)
index 0000000..4b9ba1b
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+namespace wcf\data\user\group\assignment;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\IToggleAction;
+
+/**
+ * Executes user group assignment-related actions.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.user.group.assignment
+ * @category   Community Framework
+ */
+class UserGroupAssignmentAction extends AbstractDatabaseObjectAction implements IToggleAction {
+       /**
+        * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+        */
+       protected $permissionsDelete = array('admin.user.canManageGroupAssignment');
+       
+       /**
+        * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+        */
+       protected $permissionsUpdate = array('admin.user.canManageGroupAssignment');
+       
+       /**
+        * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
+        */
+       protected $requireACP = array('create', 'delete', 'toggle', 'update');
+       
+       /**
+        * @see \wcf\data\IToggleAction::toggle()
+        */
+       public function toggle() {
+               foreach ($this->objects as $assignment) {
+                       $assignment->update(array(
+                               'isDisabled' => $assignment->isDisabled ? 0 : 1
+                       ));
+               }
+       }
+       
+       /**
+        * @see \wcf\data\IToggleAction::validateToggle()
+        */
+       public function validateToggle() {
+               parent::validateUpdate();
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentEditor.class.php b/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentEditor.class.php
new file mode 100644 (file)
index 0000000..1ae5a6b
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+namespace wcf\data\user\group\assignment;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\UserGroupAssignmentCacheBuilder;
+use wcf\system\cache\builder\ConditionCacheBuilder;
+
+/**
+ * Executes user group assignment-related actions.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.user.group.assignment
+ * @category   Community Framework
+ */
+class UserGroupAssignmentEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+       /**
+        * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+        */
+       protected static $baseClass = 'wcf\data\user\group\assignment\UserGroupAssignment';
+       
+       /**
+        * @see \wcf\data\IEditableCachedObject::resetCache()
+        */
+       public static function resetCache() {
+               UserGroupAssignmentCacheBuilder::getInstance()->reset();
+               ConditionCacheBuilder::getInstance()->reset(array(
+                       'definitionID' => ObjectTypeCache::getInstance()->getDefinitionByName('com.woltlab.wcf.condition.userGroupAssignment')->definitionID
+               ));
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentList.class.php b/wcfsetup/install/files/lib/data/user/group/assignment/UserGroupAssignmentList.class.php
new file mode 100644 (file)
index 0000000..293b52f
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+namespace wcf\data\user\group\assignment;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of user group assignments.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage data.user.group.assignment
+ * @category   Community Framework
+ */
+class UserGroupAssignmentList extends DatabaseObjectList { }
index 7ff7cf32e583618ab91ca6c6d2075607397b45c8..415775f56bbc0c53926d87b5800a4e5ff4e26709 100644 (file)
@@ -6,6 +6,7 @@ use wcf\data\user\UserAction;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\menu\user\UserMenu;
+use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
 use wcf\system\WCF;
 
 /**
@@ -121,6 +122,10 @@ class AvatarEditForm extends AbstractForm {
                ));
                $this->objectAction->executeAction();
                
+               // check if the user will be automatically added to new user groups
+               // because of the changed avatar
+               UserGroupAssignmentHandler::getInstance()->checkUsers(array(WCF::getUser()->userID));
+               
                // reset gravatar cache
                if ($this->avatarType == 'gravatar') {
                        $pattern = WCF_DIR . sprintf(Gravatar::GRAVATAR_CACHE_LOCATION, md5(mb_strtolower(WCF::getUser()->email)), '*');
diff --git a/wcfsetup/install/files/lib/system/cache/builder/ConditionCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/ConditionCacheBuilder.class.php
new file mode 100644 (file)
index 0000000..d2b2741
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\condition\ConditionList;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\exception\SystemException;
+
+/**
+ * Caches the conditions for a certain condition object type definition.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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 ConditionCacheBuilder extends AbstractCacheBuilder {
+       /**
+        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+        */
+       public function rebuild(array $parameters) {
+               if (!isset($parameters['definitionID'])) {
+                       throw new SystemException("Missing 'definitionID' parameter");
+               }
+               
+               $definition = ObjectTypeCache::getInstance()->getDefinition($parameters['definitionID']);
+               if ($definition === null) {
+                       throw new SystemException("Unknown object type definition with id '".$parameters['definitionID']."'");
+               }
+               
+               $objectTypes = ObjectTypeCache::getInstance()->getObjectTypes($definition->definitionName);
+               if (empty($objectTypes)) {
+                       return array();
+               }
+               
+               $objectTypeIDs = array();
+               foreach ($objectTypes as $objectType) {
+                       $objectTypeIDs[] = $objectType->objectTypeID;
+               }
+               
+               $conditionList = new ConditionList();
+               $conditionList->getConditionBuilder()->add('condition_table.objectTypeID IN (?)', array($objectTypeIDs));
+               $conditionList->readObjects();
+               
+               $groupedConditions = array();
+               foreach ($conditionList as $condition) {
+                       if (!isset($groupedConditions[$condition->objectID])) {
+                               $groupedConditions[$condition->objectID] = array();
+                       }
+                       
+                       $groupedConditions[$condition->objectID][$condition->conditionID] = $condition;
+               }
+               
+               return $groupedConditions;
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/cache/builder/UserGroupAssignmentCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/UserGroupAssignmentCacheBuilder.class.php
new file mode 100644 (file)
index 0000000..26a2ebc
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\user\group\assignment\UserGroupAssignmentList;
+
+/**
+ * Caches the enabled automatic user group assignments.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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 UserGroupAssignmentCacheBuilder extends AbstractCacheBuilder {
+       /**
+        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+        */
+       protected function rebuild(array $parameters) {
+               $assignmentList = new UserGroupAssignmentList();
+               $assignmentList->getConditionBuilder()->add('isDisabled = ?', array(0));
+               $assignmentList->readObjects();
+               
+               return $assignmentList->getObjects();
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractCondition.class.php
new file mode 100644 (file)
index 0000000..7324b53
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\object\type\AbstractObjectTypeProcessor;
+
+/**
+ * Abstract implementation of a condition.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+abstract class AbstractCondition extends AbstractObjectTypeProcessor implements ICondition {
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               // does nothing
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               // does nothing
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractIntegerCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractIntegerCondition.class.php
new file mode 100644 (file)
index 0000000..7320c1c
--- /dev/null
@@ -0,0 +1,270 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Abstract implementation of a condition for an integer value.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+abstract class AbstractIntegerCondition extends AbstractMultipleFieldsCondition {
+       /**
+        * property value has to be greater than the given value
+        * @var integer
+        */
+       protected $greaterThan = null;
+       
+       /**
+        * identifier used for the input fields
+        * @var string
+        */
+       protected $identifier = '';
+       
+       /**
+        * prefix used for error message language items
+        * @var string
+        */
+       protected $languageItemPrefix = '';
+       
+       /**
+        * property value has to be less than the given value
+        * @var integer
+        */
+       protected $lessThan = null;
+       
+       /**
+        * maximum value the property can have
+        * @var integer
+        */
+       protected $maxValue = null;
+       
+       /**
+        * language item with the global maximum value error message
+        * @var string
+        */
+       protected $maxValueErrorMessage = null;
+       
+       /**
+        * minimum value the property can have
+        * @var integer
+        */
+       protected $minValue = null;
+       
+       /**
+        * language item with the global minimum value error message
+        * @var string
+        */
+       protected $minValueErrorMessage = null;
+       
+       /**
+        * name of the integer user property
+        * @var string
+        */
+       protected $propertyName = '';
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               $data = array();
+               
+               if ($this->lessThan !== null) {
+                       $data['lessThan'] = $this->lessThan;
+               }
+               if ($this->greaterThan !== null) {
+                       $data['greaterThan'] = $this->greaterThan;
+               }
+               
+               if (!empty($data)) {
+                       return $data;
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractMultipleFieldsCondition::getData()
+        */
+       protected function getErrorMessageElement($identifier) {
+               if (isset($this->errorMessages[$identifier])) {
+                       $errorMessage = '';
+                       switch ($this->errorMessages[$identifier]) {
+                               case $this->languageItemPrefix.'.greaterThan.error.maxValue':
+                               case $this->languageItemPrefix.'.lessThan.error.maxValue':
+                                       $errorMessage = WCF::getLanguage()->getDynamicVariable($this->maxValueErrorMessage ? $this->maxValueErrorMessage : $this->errorMessages[$identifier], array(
+                                               'maxValue' => $this->maxValue
+                                       ));
+                               break;
+                               
+                               case $this->languageItemPrefix.'.greaterThan.error.minValue':
+                               case $this->languageItemPrefix.'.lessThan.error.minValue':
+                                       $errorMessage = WCF::getLanguage()->getDynamicVariable($this->minValueErrorMessage ? $this->minValueErrorMessage : $this->errorMessages[$identifier], array(
+                                               'minValue' => $this->minValue
+                                       ));
+                               break;
+                               
+                               default:
+                                       $errorMessage = WCF::getLanguage()->get($this->errorMessages[$identifier]);
+                               break;
+                       }
+                       return '<small class="innerError">'.$errorMessage.'</small>';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getHTML()
+        */
+       public function getHTML() {
+               return <<<HTML
+<dl>
+       <dt>{$this->getLabel('lessThan')}</dt>
+       <dd>
+               <input type="number" name="lessThan_{$this->getIdentifier()}" value="{$this->lessThan}"{$this->getMinMaxAttributes('lessThan')} />
+               {$this->getDescriptionElement('lessThan')}
+               {$this->getErrorMessageElement('lessThan')}
+       </dd>
+</dl>
+
+<dl>
+       <dt>{$this->getLabel('greaterThan')}</dt>
+       <dd>
+               <input type="number" name="greaterThan_{$this->getIdentifier()}" value="{$this->greaterThan}"{$this->getMinMaxAttributes('greaterThan')} />
+               {$this->getDescriptionElement('greaterThan')}
+               {$this->getErrorMessageElement('greaterThan')}
+       </dd>
+</dl>
+HTML;
+       }
+       
+       /**
+        * Returns the identifier used for the input fields.
+        * 
+        * @return      string
+        */
+       protected function getIdentifier() {
+               return $this->identifier;
+       }
+       
+       /**
+        * Returns the maximum value the property can have or null if there is no
+        * such maximum.
+        * 
+        * @return      integer
+        */
+       protected function getMaxValue() {
+               if ($this->getDecoratedObject()->maxvalue !== null) {
+                       return $this->getDecoratedObject()->maxvalue;
+               }
+               
+               if ($this->maxValue !== null) {
+                       return $this->maxValue;
+               }
+               
+               return null;
+       }
+       
+       /**
+        * Returns the min and max attributes for the input elements.
+        * 
+        * @param       string          $type
+        * @return      string
+        */
+       protected function getMinMaxAttributes($type) {
+               $attributes = '';
+               if ($this->getMinValue() !== null) {
+                       $attributes .= ' min="'.($this->getMinValue() + ($type == 'lessThan' ? 1 : 0)).'"';
+               }
+               if ($this->getMaxValue() !== null) {
+                       $attributes .= ' max="'.($this->getMaxValue() - ($type == 'lessThan' ? 1 : 0)).'"';
+               }
+               
+               return $attributes;
+       }
+       
+       /**
+        * Returns the minimum value the property can have or null if there is no
+        * such minimum.
+        * 
+        * @return      integer
+        */
+       protected function getMinValue() {
+               if ($this->getDecoratedObject()->minvalue !== null) {
+                       return $this->getDecoratedObject()->minvalue;
+               }
+               
+               if ($this->minValue !== null) {
+                       return $this->minValue;
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST['lessThan_'.$this->getIdentifier()]) && strlen($_POST['lessThan_'.$this->getIdentifier()])) $this->lessThan = intval($_POST['lessThan_'.$this->getIdentifier()]);
+               if (isset($_POST['greaterThan_'.$this->getIdentifier()]) && strlen($_POST['greaterThan_'.$this->getIdentifier()])) $this->greaterThan = intval($_POST['greaterThan_'.$this->getIdentifier()]);
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->lessThan = null;
+               $this->greaterThan = null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               $this->lessThan = $condition->lessThan;
+               $this->greaterThan = $condition->greaterThan;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               if ($this->lessThan !== null) {
+                       if ($this->getMinValue() !== null && $this->lessThan <= $this->getMinValue()) {
+                               $this->errorMessages['lessThan'] = $this->languageItemPrefix.'.lessThan.error.minValue';
+                               
+                               throw new UserInputException('lessThan', 'minValue');
+                       }
+                       else if ($this->getMaxValue() !== null && $this->lessThan > $this->getMaxValue()) {
+                               $this->errorMessages['lessThan'] = $this->languageItemPrefix.'.lessThan.error.maxValue';
+                               
+                               throw new UserInputException('lessThan', 'maxValue');
+                       }
+               }
+               if ($this->greaterThan !== null) {
+                       if ($this->getMinValue() !== null && $this->greaterThan < $this->getMinValue()) {
+                               $this->errorMessages['greaterThan'] = $this->languageItemPrefix.'.greaterThan.error.minValue';
+                               
+                               throw new UserInputException('greaterThan', 'minValue');
+                       }
+                       else if ($this->getMaxValue() !== null && $this->greaterThan >= $this->getMaxValue()) {
+                               $this->errorMessages['greaterThan'] = $this->languageItemPrefix.'.greaterThan.error.maxValue';
+                               
+                               throw new UserInputException('greaterThan', 'maxValue');
+                       }
+               }
+               
+               if ($this->lessThan !== null && $this->greaterThan !== null && $this->greaterThan + 1 >= $this->lessThan) {
+                       $this->errorMessages['greaterThan'] = $this->languageItemPrefix.'.greaterThan.error.lessThan';
+                       
+                       throw new UserInputException('greaterThan', 'lessThan');
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractMultipleFieldsCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractMultipleFieldsCondition.class.php
new file mode 100644 (file)
index 0000000..99f08df
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\object\type\AbstractObjectTypeProcessor;
+use wcf\system\WCF;
+
+/**
+ * Abstract implementation of a condition for multiple fields.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+abstract class AbstractMultipleFieldsCondition extends AbstractCondition {
+       /**
+        * language items of the input element descriptions
+        * @var string
+        */
+       protected $descriptions = array();
+       
+       /**
+        * error messages if the validation failed
+        * @var string
+        */
+       protected $errorMessages = array();
+       
+       /**
+        * language items of the input element labels
+        * @var string
+        */
+       protected $labels = array();
+       
+       /**
+        * Returns the description element for the HTML output.
+        * 
+        * @param       string          $identifier
+        * @return      string
+        */
+       protected function getDescriptionElement($identifier) {
+               if (isset($this->descriptions[$identifier])) {
+                       return '<small>'.WCF::getLanguage()->get($this->descriptions[$identifier]).'</small>';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the error class for the definition list element.
+        * 
+        * @param       string          $identifier
+        * @return      string
+        */
+       public function getErrorClass($identifier) {
+               if (isset($this->errorMessages[$identifier])) {
+                       return ' class="formError"';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the error message element for the HTML output.
+        * 
+        * @param       string          $identifier
+        * @return      string
+        */
+       protected function getErrorMessageElement($identifier) {
+               if (isset($this->errorMessages[$identifier])) {
+                       return '<small class="innerError">'.WCF::getLanguage()->get($this->errorMessages[$identifier]).'</small>';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the label of the input element.
+        * 
+        * @param       string          $identifier
+        * @return      string
+        */
+       protected function getLabel($identifier) {
+               if (isset($this->labels[$identifier])) {
+                       return '<label for="'.$identifier.'">'.WCF::getLanguage()->get($this->labels[$identifier]).'</label>';
+               }
+               
+               return '';
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractSelectCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractSelectCondition.class.php
new file mode 100644 (file)
index 0000000..e236f1b
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Abstract implementation of a condition with select options.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+abstract class AbstractSelectCondition extends AbstractSingleFieldCondition {
+       /**
+        * name of the field
+        * @var string
+        */
+       protected $fieldName = '';
+       
+       /**
+        * value of the selected option
+        * @var string
+        */
+       protected $fieldValue = self::NO_SELECTION_VALUE;
+       
+       /**
+        * value of the "no selection" option
+        * @var string
+        */
+       const NO_SELECTION_VALUE = -1;
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               if ($this->fieldValue != self::NO_SELECTION_VALUE) {
+                       return array(
+                               $this->fieldName => $this->fieldValue
+                       );
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::getFieldElement()
+        */
+       protected function getFieldElement() {
+               $options = $this->getOptions();
+               
+               $fieldElement = '<select name="'.$this->fieldName.'">';
+               foreach ($options as $value => $label) {
+                       $fieldElement .= '<option value="'.$value.'"'.($this->fieldValue == $value ? ' selected="selected"' : '').'>'.WCF::getLanguage()->get($label).'</option>';
+               }
+               $fieldElement .= "</select>";
+               
+               return $fieldElement;
+       }
+       
+       /**
+        * Returns the selectable options.
+        */
+       abstract protected function getOptions();
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST[$this->fieldName])) $this->fieldValue = intval($_POST[$this->fieldName]);
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->fieldValue = self::NO_SELECTION_VALUE;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               $this->fieldValue = $condition->conditionData[$this->fieldName];
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               if ($this->fieldValue != self::NO_SELECTION_VALUE) {
+                       $options = $this->getOptions();
+                       
+                       if (!isset($options[$this->fieldValue])) {
+                               $this->errorMessage = 'wcf.global.form.error.noValidSelection';
+                               
+                               throw new UserInputException($this->fieldName, 'noValidSelection');
+                       }
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractSingleFieldCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractSingleFieldCondition.class.php
new file mode 100644 (file)
index 0000000..e3f0a2c
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+namespace wcf\system\condition;
+use wcf\system\WCF;
+
+/**
+ * Abstract implementation of a condition for a single field.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+abstract class AbstractSingleFieldCondition extends AbstractCondition implements ICondition {
+       /**
+        * language item of the input element description
+        * @var string
+        */
+       protected $description = '';
+       
+       /**
+        * error message if the validation failed
+        * @var string
+        */
+       protected $errorMessage = '';
+       
+       /**
+        * language item of the input element label
+        * @var string
+        */
+       protected $label = '';
+       
+       /**
+        * Returns the description element for the HTML output.
+        * 
+        * @return      string
+        */
+       protected function getDescriptionElement() {
+               if ($this->description) {
+                       return '<small>'.WCF::getLanguage()->get($this->description).'</small>';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the error class for the definition list element.
+        * 
+        * @return      string
+        */
+       public function getErrorClass() {
+               if ($this->errorMessage) {
+                       return ' class="formError"';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the error message element for the HTML output.
+        * 
+        * @return      string
+        */
+       protected function getErrorMessageElement() {
+               if ($this->errorMessage) {
+                       return '<small class="innerError">'.WCF::getLanguage()->get($this->errorMessage).'</small>';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the output of the field element.
+        * 
+        * @return      string
+        */
+       abstract protected function getFieldElement();
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getHTML()
+        */
+       public function getHTML() {
+               return <<<HTML
+<dl>
+       <dt>{$this->getLabel()}</dt>
+       <dd>
+               {$this->getFieldElement()}
+               {$this->getDescriptionElement()}
+               {$this->getErrorMessageElement()}
+       </dd>
+</dl>
+HTML;
+       }
+       
+       /**
+        * Returns the label of the input element.
+        * 
+        * @return      string
+        */
+       protected function getLabel() {
+               return WCF::getLanguage()->get($this->label);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/AbstractTextCondition.class.php b/wcfsetup/install/files/lib/system/condition/AbstractTextCondition.class.php
new file mode 100644 (file)
index 0000000..f38afa7
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\util\StringUtil;
+
+/**
+ * Abstract implementation of a text condition.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+abstract class AbstractTextCondition extends AbstractSingleFieldCondition {
+       /**
+        * name of the field
+        * @var string
+        */
+       protected $fieldName = '';
+       
+       /**
+        * entered condition field value
+        * @var string
+        */
+       protected $fieldValue = '';
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               if (mb_strlen($this->fieldValue)) {
+                       return array(
+                               $this->fieldName => $this->fieldValue
+                       );
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::getFieldElement()
+        */
+       protected function getFieldElement() {
+               return '<input type="text" name="'.$this->fieldName.'" value="'.StringUtil::encodeHTML($this->fieldValue).'" class="long" />';
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST[$this->fieldName])) $this->fieldValue = StringUtil::trim($_POST[$this->fieldName]);
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->fieldValue = '';
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               $this->fieldValue = $condition->conditionData[$this->fieldName];
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/ConditionHandler.class.php b/wcfsetup/install/files/lib/system/condition/ConditionHandler.class.php
new file mode 100644 (file)
index 0000000..2651641
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\ConditionAction;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\cache\builder\ConditionCacheBuilder;
+use wcf\system\exception\SystemException;
+use wcf\system\SingletonFactory;
+
+/**
+ * Handles general condition-related matters.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class ConditionHandler extends SingletonFactory {
+       /**
+        * list of available conditions grouped by the id of the related condition
+        * object type definition
+        * @var array
+        */
+       protected $conditions = array();
+       
+       /**
+        * Creates condition objects for the object with the given id and based
+        * on the given condition object types.
+        * 
+        * @param       integer         $objectID
+        * @param       array           $conditionObjectTypes
+        */
+       public function createConditions($objectID, array $conditionObjectTypes) {
+               foreach ($conditionObjectTypes as $objectTypes) {
+                       foreach ($objectTypes as $objectType) {
+                               $conditionData = $objectType->getProcessor()->getData();
+                               if ($conditionData !== null) {
+                                       $conditionAction = new ConditionAction(array(), 'create', array(
+                                               'data' => array(
+                                                       'conditionData' => serialize($conditionData),
+                                                       'objectID' => $objectID,
+                                                       'objectTypeID' => $objectType->objectTypeID
+                                               )
+                                       ));
+                                       $conditionAction->executeAction();
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * Returns the conditions for the conditioned object with the given condition
+        * object type definition and object id.
+        * 
+        * @param       string          $definitionName
+        * @param       integer         $objectID
+        * @return      array<\wcf\data\condition\Condition>
+        */
+       public function getConditions($definitionName, $objectID) {
+               // validate definition
+               $definition = ObjectTypeCache::getInstance()->getDefinitionByName($definitionName);
+               if ($definition === null) {
+                       throw new SystemException("Unknown object type definition with name '".$definitionName."'");
+               }
+               
+               if (!isset($this->conditions[$definition->definitionID])) {
+                       $this->conditions[$definition->definitionID] = ConditionCacheBuilder::getInstance()->getData(array(
+                               'definitionID' => $definition->definitionID
+                       ));
+               }
+               
+               if (isset($this->conditions[$definition->definitionID][$objectID])) {
+                       return $this->conditions[$definition->definitionID][$objectID];
+               }
+               
+               return null;
+       }
+       
+       /**
+        * Updates the conditions for the object with the given object id.
+        * 
+        * @param       integer                                         $objectID
+        * @param       array<\wcf\data\condition\Condition>            $oldConditions
+        * @param       array                                           $conditionObjectTypes
+        */
+       public function updateConditions($objectID, array $oldConditions, array $conditionObjectTypes) {
+               // delete old conditions first
+               $conditionAction = new ConditionAction($oldConditions, 'delete');
+               $conditionAction->executeAction();
+               
+               // create new conditions
+               $this->createConditions($objectID, $conditionObjectTypes);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/ICondition.class.php b/wcfsetup/install/files/lib/system/condition/ICondition.class.php
new file mode 100644 (file)
index 0000000..b9d9eac
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\IDatabaseObjectProcessor;
+
+/**
+ * Every concrete condition implementation needs to implement this interface.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+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
+        */
+       public function getData();
+       
+       /**
+        * Returns the output for setting up the condition.
+        * 
+        * @return      string
+        */
+       public function getHTML();
+       
+       /**
+        * Reads the form parameters of the condition.
+        */
+       public function readFormParameters();
+       
+       /**
+        * Resets the internally stored condition data.
+        */
+       public function reset();
+       
+       /**
+        * Extracts all needed data from the given condition to pre-fill the output
+        * for editing the given condition.
+        * 
+        * @param       \wcf\data\condition\Condition   $condition
+        */
+       public function setData(Condition $condition);
+       
+       /**
+        * Validates the given data.
+        * 
+        * @param       string          $value
+        */
+       public function validate();
+}
diff --git a/wcfsetup/install/files/lib/system/condition/IUserCondition.class.php b/wcfsetup/install/files/lib/system/condition/IUserCondition.class.php
new file mode 100644 (file)
index 0000000..edf9dc5
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+
+/**
+ * Every implementation for user conditions needs to implements this interface.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+interface IUserCondition extends ICondition {
+       /**
+        * Adds the condition to the given user list to fetch the users which fulfill
+        * the given condition.
+        * 
+        * @param       \wcf\data\condition\Condition   $condition
+        * @param       \wcf\data\user\UserList         $userList
+        */
+       public function addUserCondition(Condition $condition, UserList $userList);
+       
+       /**
+        * Returns true if the given user fulfills the given condition.
+        * 
+        * @param       \wcf\data\condition\Condition   $condition
+        * @param       \wcf\data\user\User             $user
+        * @return      boolean
+        */
+       public function checkUser(Condition $condition, User $user);
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserAvatarCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserAvatarCondition.class.php
new file mode 100644 (file)
index 0000000..26eb249
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Condition implementation for the avatar of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserAvatarCondition extends AbstractSelectCondition implements IUserCondition {
+       /**
+        * @see wcf\system\condition\AbstractSelectCondition::$fieldName
+        */
+       protected $fieldName = 'userAvatar';
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::$label
+        */
+       protected $label = 'wcf.user.condition.avatar';
+       
+       /**
+        * value of the "user has no avatar" option
+        * @var integer
+        */
+       const NO_AVATAR = 0;
+       
+       /**
+        * value of the "user has a custom avatar" option
+        * @var integer
+        */
+       const AVATAR = 1;
+       
+       /**
+        * value of the "user has a gravatar" option
+        * @var integer
+        */
+       const GRAVATAR = 2;
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               switch ($condition->userAvatar) {
+                       case self::NO_AVATAR:
+                               $userList->getConditionBuilder()->add('user_table.avatarID IS NULL');
+                       break;
+                       
+                       case self::AVATAR:
+                               $userList->getConditionBuilder()->add('user_table.avatarID IS NOT NULL');
+                       break;
+                       
+                       case self::GRAVATAR:
+                               $userList->getConditionBuilder()->add('user_table.enableGravatar = ?', array(1));
+                       break;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               switch ($condition->userAvatar) {
+                       case self::NO_AVATAR:
+                               return !$user->avatar;
+                       break;
+                       
+                       case self::AVATAR:
+                               return $user->avatar != 0;
+                       break;
+                       
+                       case self::GRAVATAR:
+                               return $user->enableGravatar;
+                       break;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractSelectCondition::getOptions()
+        */
+       protected function getOptions() {
+               return array(
+                       self::NO_SELECTION_VALUE => 'wcf.global.noSelection',
+                       self::NO_AVATAR => 'wcf.user.condition.avatar.noAvatar',
+                       self::AVATAR => 'wcf.user.condition.avatar.avatar',
+                       self::GRAVATAR => 'wcf.user.condition.avatar.gravatar'
+               );
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserEmailCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserEmailCondition.class.php
new file mode 100644 (file)
index 0000000..f6bf199
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+
+/**
+ * Condition implementation for the email address of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserEmailCondition extends AbstractTextCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractTextCondition::$fieldName
+        */
+       protected $fieldName = 'email';
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::$label
+        */
+       protected $label = 'wcf.user.email';
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               $userList->getConditionBuilder()->add('user_table.email LIKE ?', array('%'.addcslashes($condition->email, '_%').'%'));
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               return mb_strpos($user->email, $condition->email) !== false;
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserGroupCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserGroupCondition.class.php
new file mode 100644 (file)
index 0000000..f8b1832
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\group\UserGroup;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\system\exception\UserInputException;
+use wcf\util\ArrayUtil;
+
+/**
+ * Condition implementation for the user group a user is a member or no member of.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserGroupCondition extends AbstractMultipleFieldsCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractMultipleFieldsCondition::$descriptions
+        */
+       protected $descriptions = array(
+               'groupIDs' => 'wcf.user.condition.groupIDs.description',
+               'notGroupIDs' => 'wcf.user.condition.notGroupIDs.description'
+       );
+       
+       /**
+        * ids of the selected user groups the user has to be member of
+        * @var array<integer>
+        */
+       protected $groupIDs = array();
+       
+       /**
+        * @see \wcf\system\condition\AbstractMultipleFieldsCondition::$labels
+        */
+       protected $labels = array(
+               'groupIDs' => 'wcf.user.condition.groupIDs',
+               'notGroupIDs' => 'wcf.user.condition.notGroupIDs'
+       );
+       
+       /**
+        * ids of the selected user groups the user may not be member of
+        * @var array<integer>
+        */
+       protected $notGroupIDs = array();
+       
+       /**
+        * selectable user groups
+        * @var array<\wcf\data\user\group\UserGroup>
+        */
+       protected $userGroups = null;
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               if ($condition->groupIDs !== null) {
+                       $userList->getConditionBuilder()->add('user_table.userID IN (SELECT userID FROM wcf'.WCF_N.'_user_to_group WHERE groupID IN (?) GROUP BY userID HAVING COUNT(userID) = ?)', array($condition->groupIDs, count($condition->groupIDs)));
+               }
+               if ($condition->notGroupIDs !== null) {
+                       $userList->getConditionBuilder()->add('user_table.userID NOT IN (SELECT userID FROM wcf'.WCF_N.'_user_to_group WHERE groupID IN (?))', array($condition->notGroupIDs));
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               $groupIDs = $user->getGroupIDs();
+               if (!empty($condition->groupIDs) && count(array_diff($condition->groupIDs, $groupIDs))) {
+                       return false;
+               }
+               
+               if (!empty($condition->notGroupIDs) && count(array_intersect($condition->notGroupIDs, $groupIDs))) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               $data = array();
+
+               if (!empty($this->groupIDs)) {
+                       $data['groupIDs'] = $this->groupIDs;
+               }
+               if (!empty($this->notGroupIDs)) {
+                       $data['notGroupIDs'] = $this->notGroupIDs;
+               }
+               
+               if (!empty($data)) {
+                       return $data;
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getHTML()
+        */
+       public function getHTML() {
+               return <<<HTML
+<dl{$this->getErrorClass('groupIDs')}>
+       <dt>{$this->getLabel('groupIDs')}</dt>
+       <dd>
+               {$this->getOptionElements('groupIDs')}
+               {$this->getDescriptionElement('groupIDs')}
+               {$this->getErrorMessageElement('groupIDs')}
+       </dd>
+</dl>
+<dl{$this->getErrorClass('notGroupIDs')}>
+       <dt>{$this->getLabel('notGroupIDs')}</dt>
+       <dd>
+               {$this->getOptionElements('notGroupIDs')}
+               {$this->getDescriptionElement('notGroupIDs')}
+               {$this->getErrorMessageElement('notGroupIDs')}
+       </dd>
+</dl>
+HTML;
+       }
+       
+       /**
+        * Returns the option elements for the user group selection.
+        * 
+        * @param       string          $identifier
+        * @return      string
+        */
+       protected function getOptionElements($identifier) {
+               $userGroups = $this->getUserGroups();
+               
+               $returnValue = "";
+               foreach ($userGroups as $userGroup) {
+                       $returnValue .= "<label><input type=\"checkbox\" name=\"".$identifier."[]\" value=\"".$userGroup->groupID."\"".(in_array($userGroup->groupID, $this->$identifier) ? ' checked="checked"' : "")." /> ".$userGroup->getName()."</label>";
+               }
+               
+               return $returnValue;
+       }
+       
+       /**
+        * Returns the selectable user groups.
+        * 
+        * @return      array<\wcf\data\user\group\UserGroup>
+        */
+       protected function getUserGroups() {
+               if ($this->userGroups == null) {
+                       $this->userGroups = UserGroup::getGroupsByType(array(UserGroup::OTHER));
+                       foreach ($this->userGroups as $key => $userGroup) {
+                               if (!$userGroup->isAccessible()) {
+                                       unset($this->userGroups[$key]);
+                               }
+                       }
+                       
+                       uasort($this->userGroups, function(UserGroup $groupA, UserGroup $groupB) {
+                               return strcmp($groupA->getName(), $groupB->getName());
+                       });
+               }
+               
+               return $this->userGroups;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST['groupIDs'])) $this->groupIDs = ArrayUtil::toIntegerArray($_POST['groupIDs']);
+               if (isset($_POST['notGroupIDs'])) $this->notGroupIDs = ArrayUtil::toIntegerArray($_POST['notGroupIDs']);
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->groupIDs = array();
+               $this->notGroupIDs = array();
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               if ($condition->groupIDs !== null) {
+                       $this->groupIDs = $condition->groupIDs;
+               }
+               if ($condition->notGroupIDs !== null) {
+                       $this->notGroupIDs = $condition->notGroupIDs;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               $userGroups = $this->getUserGroups();
+               foreach ($this->groupIDs as $groupID) {
+                       if (!isset($userGroups[$groupID])) {
+                               $this->errorMessages['groupIDs'] = 'wcf.global.form.error.noValidSelection';
+                               
+                               throw new UserInputException('groupIDs', 'noValidSelection');
+                       }
+               }
+               foreach ($this->notGroupIDs as $groupID) {
+                       if (!isset($userGroups[$groupID])) {
+                               $this->errorMessages['notGroupIDs'] = 'wcf.global.form.error.noValidSelection';
+                               
+                               throw new UserInputException('notGroupIDs', 'noValidSelection');
+                       }
+               }
+               
+               if (count(array_intersect($this->notGroupIDs, $this->groupIDs))) {
+                       $this->errorMessages['notGroupIDs'] = 'wcf.user.condition.notGroupIDs.error.groupIDsIntersection';
+                       
+                       throw new UserInputException('notGroupIDs', 'groupIDsIntersection');
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserIntegerPropertyCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserIntegerPropertyCondition.class.php
new file mode 100644 (file)
index 0000000..0b34358
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\data\DatabaseObject;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Condition implementation for an integer property of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserIntegerPropertyCondition extends AbstractIntegerCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::$maxValueErrorMessage
+        */
+       protected $maxValueErrorMessage = 'wcf.user.condition.integerProperty.error.maxValue';
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::$minValueErrorMessage
+        */
+       protected $minValueErrorMessage = 'wcf.user.condition.integerProperty.error.minValue';
+       
+       /**
+        * @see \wcf\data\DatabaseObjectDecorator::__construct()
+        */
+       public function __construct(DatabaseObject $object) {
+               parent::__construct($object);
+               
+               $this->languageItemPrefix = 'wcf.user.condition.'.$this->getDecoratedObject()->propertyname;
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               if ($condition->greaterThan !== null) {
+                       $userList->getConditionBuilder()->add('user_table.'.$this->getDecoratedObject()->propertyname.' > ?', array($condition->greaterThan));
+               }
+               if ($condition->lessThan !== null) {
+                       $userList->getConditionBuilder()->add('user_table.'.$this->getDecoratedObject()->propertyname.' < ?', array($condition->lessThan));
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               if ($condition->greaterThan !== null && $user->{$this->getDecoratedObject()->propertyname} <= $condition->greaterThan) {
+                       return false;
+               }
+               if ($condition->lessThan !== null && $user->{$this->getDecoratedObject()->propertyname} >= $condition->lessThan) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::getIdentifier()
+        */
+       protected function getIdentifier() {
+               return 'user_'.$this->getDecoratedObject()->propertyname;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractMultipleFieldsCondition::getLabel()
+        */
+       protected function getLabel($identifier) {
+               return WCF::getLanguage()->get('wcf.user.condition.'.$this->getDecoratedObject()->propertyname.'.'.$identifier);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserLanguageCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserLanguageCondition.class.php
new file mode 100644 (file)
index 0000000..ba9e0f5
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\LanguageFactory;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+
+/**
+ * Condition implementation for the languages of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserLanguageCondition extends AbstractSingleFieldCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::$label
+        */
+       protected $label = 'wcf.user.condition.languages';
+       
+       /**
+        * ids of the selected languages
+        * @var array<integer>
+        */
+       protected $languageIDs = array();
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               $userList->getConditionBuilder()->add('user_table.languageID IN (?)', array($condition->conditionData['languageIDs']));
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               if (!empty($condition->languageIDs) && !in_array($user->languageID, $condition->languageIDs)) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               if (!empty($this->languageIDs)) {
+                       return array(
+                               'languageIDs' => $this->languageIDs
+                       );
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::getFieldElement()
+        */
+       protected function getFieldElement() {
+               $returnValue = "";
+               foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
+                       $returnValue .= "<label><input type=\"checkbox\" name=\"languageIDs[]\" value=\"".$language->languageID."\"".(in_array($language->languageID, $this->languageIDs) ? ' checked="checked"' : "")." /> ".$language->languageName."</label>";
+               }
+               
+               return $returnValue;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST['languageIDs']) && is_array($_POST['languageIDs'])) $this->languageIDs = ArrayUtil::toIntegerArray($_POST['languageIDs']);
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->languageIDs = array();
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               if (!empty($condition->conditionData['languageIDs'])) {
+                       $this->languageIDs = $condition->conditionData['languageIDs'];
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               foreach ($this->languageIDs as $languageID) {
+                       if (LanguageFactory::getInstance()->getLanguage($languageID) === null) {
+                               $this->errorMessage = 'wcf.global.form.error.noValidSelection';
+                               
+                               throw new UserInputException('languageIDs', 'noValidSelection');
+                       }
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserOptionsCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserOptionsCondition.class.php
new file mode 100644 (file)
index 0000000..812f1e5
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\data\DatabaseObject;
+use wcf\system\option\user\UserOptionHandler;
+use wcf\system\WCF;
+
+/**
+ * Condition implementation for the options of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserOptionsCondition extends AbstractMultipleFieldsCondition implements IUserCondition {
+       /**
+        * user option handler object
+        * @var \wcf\system\option\user\UserOptionHandler
+        */
+       protected $optionHandler = null;
+       
+       /**
+        * @see \wcf\data\DatabaseObjectDecorator::__construct()
+        */
+       public function __construct(DatabaseObject $object) {
+               parent::__construct($object);
+               
+               $this->optionHandler = new UserOptionHandler(false);
+               $this->optionHandler->enableConditionMode();
+               $this->optionHandler->init();
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               $optionValues = $condition->optionValues;
+               
+               foreach ($this->optionHandler->getCategoryOptions('profile') as $option) {
+                       $option = $option['object'];
+                       
+                       if (isset($optionValues[$option->optionName])) {
+                               $this->optionHandler->getTypeObject($option->optionType)->addCondition($userList, $option, $optionValues[$option->optionName]);
+                       }
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               $optionValues = $condition->optionValues;
+               
+               $checkSuccess = true;
+               foreach ($this->optionHandler->getCategoryOptions('profile') as $option) {
+                       $option = $option['object'];
+                       
+                       if (isset($optionValues[$option->optionName])) {
+                               if (!$this->optionHandler->getTypeObject($option->optionType)->checkUser($user, $option, $optionValues[$option->optionName])) {
+                                       $checkSuccess = false;
+                                       break;
+                               }
+                       }
+               }
+               
+               return $checkSuccess;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               $optionValues = $this->optionHandler->getOptionValues();
+               
+               $data = array();
+               foreach ($this->optionHandler->getCategoryOptions('profile') as $option) {
+                       $option = $option['object'];
+                       
+                       if (isset($optionValues[$option->optionName])) {
+                               $conditionData = $this->optionHandler->getTypeObject($option->optionType)->getConditionData($option, $optionValues[$option->optionName]);
+                               if ($conditionData !== null) {
+                                       $data[$option->optionName] = $conditionData;
+                               }
+                       }
+               }
+               
+               if (!empty($data)) {
+                       return array(
+                               'optionValues' => $data
+                       );
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getHTML()
+        */
+       public function getHTML() {
+               return WCF::getTPL()->fetch('userOptionsCondition', 'wcf', array(
+                       'optionTree' => $this->optionHandler->getOptionTree('profile')
+               ));
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               $this->optionHandler->readUserInput($_POST);
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->optionHandler->setOptionValues(array());
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               $this->optionHandler->setOptionValues($condition->conditionData['optionValues']);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserRegistrationDateCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserRegistrationDateCondition.class.php
new file mode 100644 (file)
index 0000000..37a4e59
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Condition implementation for the registration date of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserRegistrationDateCondition extends AbstractSingleFieldCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::$label
+        */
+       protected $label = 'wcf.user.condition.registrationDate';
+       
+       /**
+        * registration start date
+        * @var string
+        */
+       protected $registrationDateEnd = '';
+       
+       /**
+        * registration start date
+        * @var string
+        */
+       protected $registrationDateStart = '';
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               if ($condition->registrationDateEnd !== null) {
+                       $userList->getConditionBuilder()->add('user_table.registrationDate <= ?', array(strtotime($condition->registrationDateEnd) + 24 * 3600 - 1));
+               }
+               if ($condition->registrationDateStart !== null) {
+                       $userList->getConditionBuilder()->add('user_table.registrationDate >= ?', array(strtotime($condition->registrationDateStart)));
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               if ($condition->registrationDateStart !== null && $user->registrationDate < $condition->registrationDateStart) {
+                       return false;
+               }
+               if ($condition->registrationDateEnd !== null && $user->registrationDate > $condition->registrationDateEnd) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               $data = array();
+               
+               if (strlen($this->registrationDateStart)) {
+                       $data['registrationDateStart'] = $this->registrationDateStart;
+               }
+               if (strlen($this->registrationDateEnd)) {
+                       $data['registrationDateEnd'] = $this->registrationDateEnd;
+               }
+               
+               if (!empty($data)) {
+                       return $data;
+               }
+               
+               return null;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::getFieldElement()
+        */
+       protected function getFieldElement() {
+               $start = WCF::getLanguage()->get('wcf.date.period.start');
+               $end = WCF::getLanguage()->get('wcf.date.period.end');
+               
+               return <<<HTML
+<input type="date" id="registrationDateStart" name="registrationDateStart" value="{$this->registrationDateStart}" placeholder="{$start}" />
+<input type="date" id="registrationDateEnd" name="registrationDateEnd" value="{$this->registrationDateEnd}" placeholder="{$end}" />
+HTML;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST['registrationDateEnd'])) $this->registrationDateEnd = $_POST['registrationDateEnd'];
+               if (isset($_POST['registrationDateStart'])) $this->registrationDateStart = $_POST['registrationDateStart'];
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->registrationDateEnd = '';
+               $this->registrationDateStart = '';
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               if ($condition->registrationDateEnd) {
+                       $this->registrationDateEnd = $condition->registrationDateEnd;
+               }
+               if ($condition->registrationDateStart) {
+                       $this->registrationDateStart = $condition->registrationDateStart;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               $registrationDateEnd = $registrationDateStart = null;
+               if (strlen($this->registrationDateStart)) {
+                       $registrationDateStart = @strtotime($this->registrationDateStart);
+                       if ($registrationDateStart === false) {
+                               $this->errorMessage = 'wcf.user.condition.registrationDate.error.startNotValid';
+                               
+                               throw new UserInputException($this->fieldName, 'startNotValid');
+                       }
+               }
+               if (strlen($this->registrationDateEnd)) {
+                       $registrationDateEnd = @strtotime($this->registrationDateEnd);
+                       if ($registrationDateEnd === false) {
+                               $this->errorMessage = 'wcf.user.condition.registrationDate.error.endNotValid';
+                               
+                               throw new UserInputException($this->fieldName, 'endNotValid');
+                       }
+               }
+               
+               if ($registrationDateEnd !== null && $registrationDateStart !== null && $registrationDateEnd < $registrationDateStart) {
+                       $this->errorMessage = 'wcf.user.condition.registrationDate.error.endBeforeStart';
+                       
+                       throw new UserInputException($this->fieldName, 'endBeforeStart');
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserRegistrationDateIntervalCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserRegistrationDateIntervalCondition.class.php
new file mode 100644 (file)
index 0000000..4b989ea
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\data\DatabaseObject;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Condition implementation for a relative interval for the registration date of
+ * a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserRegistrationDateIntervalCondition extends AbstractIntegerCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractMultipleFieldsCondition::$languageItemPrefix
+        */
+       protected $labels = array(
+               'greaterThan' => 'wcf.user.condition.registrationDateInterval.greaterThan',
+               'lessThan' => 'wcf.user.condition.registrationDateInterval.lessThan'
+       );
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::$languageItemPrefix
+        */
+       protected $languageItemPrefix = 'wcf.user.condition.registrationDateInterval';
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::$maxValueErrorMessage
+        */
+       protected $maxValueErrorMessage = 'wcf.user.condition.integerProperty.error.maxValue';
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::$minValue
+        */
+       protected $minValue = 0;
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::$minValueErrorMessage
+        */
+       protected $minValueErrorMessage = 'wcf.user.condition.integerProperty.error.minValue';
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               if ($condition->greaterThan !== null) {
+                       $userList->getConditionBuilder()->add('user_table.registrationDate < ?', array(TIME_NOW - $condition->greaterThan * 86400));
+               }
+               if ($condition->lessThan !== null) {
+                       $userList->getConditionBuilder()->add('user_table.registrationDate > ?', array(TIME_NOW - $condition->lessThan * 86400));
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               if ($condition->greaterThan !== null && $user->registrationDate >= TIME_NOW - $condition->greaterThan * 86400) {
+                       return false;
+               }
+               if ($condition->lessThan !== null && $user->registrationDate <= TIME_NOW - $condition->lessThan * 86400) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractIntegerCondition::getIdentifier()
+        */
+       protected function getIdentifier() {
+               return 'user_registrationDateInterval';
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractMultipleFieldsCondition::getLabel()
+        */
+       protected function getLabel($identifier) {
+               return WCF::getLanguage()->get('wcf.user.condition.registrationDateInterval.'.$identifier);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserStateCondition.class.php
new file mode 100644 (file)
index 0000000..919a444
--- /dev/null
@@ -0,0 +1,194 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Condition implementation for the state (banned, enabled) of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserStateCondition extends AbstractSingleFieldCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::$label
+        */
+       protected $label = 'wcf.user.condition.state';
+       
+       /**
+        * true if the the user has to be banned
+        * @var integer
+        */
+       protected $userIsBanned = 0;
+       
+       /**
+        * true if the user has to be disabled
+        * @var integer
+        */
+       protected $userIsDisabled = 0;
+       
+       /**
+        * true if the user has to be enabled
+        * @var integer
+        */
+       protected $userIsEnabled = 0;
+       
+       /**
+        * true if the the user may not be banned
+        * @var integer
+        */
+       protected $userIsNotBanned = 0;
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               if ($condition->userIsBanned !== null) {
+                       $userList->getConditionBuilder()->add('user_table.banned = ?', array($condition->userIsBanned));
+               }
+               
+               if ($condition->userIsEnabled !== null) {
+                       if ($condition->userIsEnabled) {
+                               $userList->getConditionBuilder()->add('user_table.activationCode = ?', array(0));
+                       }
+                       else {
+                               $userList->getConditionBuilder()->add('user_table.activationCode <> ?', array(0));
+                       }
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               if ($condition->userIsBanned !== null && $user->banned != $condition->userIsBanned) {
+                       return false;
+               }
+               
+               if ($condition->userIsEnabled !== null) {
+                       if ($condition->userIsEnabled && $user->activationCode) {
+                               return false;
+                       }
+                       else if (!$condition->userIsEnabled && !$user->activationCode) {
+                               return false;
+                       }
+               }
+               
+               return true;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::getData()
+        */
+       public function getData() {
+               $data = array();
+               
+               if ($this->userIsBanned) {
+                       $data['userIsBanned'] = 1;
+               }
+               else if ($this->userIsNotBanned) {
+                       $data['userIsBanned'] = 0;
+               }
+               if ($this->userIsEnabled) {
+                       $data['userIsEnabled'] = 1;
+               }
+               else if ($this->userIsDisabled) {
+                       $data['userIsEnabled'] = 0;
+               }
+               
+               if (!empty($data)) {
+                       return $data;
+               }
+               
+               return null;
+       }
+       
+       /**
+        * Returns the "checked" attribute for an input element.
+        * 
+        * @param       string          $propertyName
+        * @return      string
+        */
+       protected function getCheckedAttribute($propertyName) {
+               if ($this->$propertyName) {
+                       return ' checked="checked"';
+               }
+               
+               return '';
+       }
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::getFieldElement()
+        */
+       protected function getFieldElement() {
+               $userIsNotBanned = WCF::getLanguage()->get('wcf.user.condition.state.isNotBanned');
+               $userIsBanned = WCF::getLanguage()->get('wcf.user.condition.state.isBanned');
+               $userIsDisabled = WCF::getLanguage()->get('wcf.user.condition.state.isDisabled');
+               $userIsEnabled = WCF::getLanguage()->get('wcf.user.condition.state.isEnabled');
+               
+               return <<<HTML
+<label><input type="checkbox" name="userIsBanned" value="1"{$this->getCheckedAttribute('userIsBanned')} /> {$userIsBanned}</label>
+<label><input type="checkbox" name="userIsNotBanned" value="1"{$this->getCheckedAttribute('userIsNotBanned')} /> {$userIsNotBanned}</label>
+<label><input type="checkbox" name="userIsEnabled" value="1"{$this->getCheckedAttribute('userIsEnabled')} /> {$userIsEnabled}</label>
+<label><input type="checkbox" name="userIsDisabled" value="1"{$this->getCheckedAttribute('userIsDisabled')} /> {$userIsDisabled}</label>
+HTML;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::readFormParameters()
+        */
+       public function readFormParameters() {
+               if (isset($_POST['userIsBanned'])) $this->userIsBanned = 1;
+               if (isset($_POST['userIsDisabled'])) $this->userIsDisabled = 1;
+               if (isset($_POST['userIsEnabled'])) $this->userIsEnabled = 1;
+               if (isset($_POST['userIsNotBanned'])) $this->userIsNotBanned = 1;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::reset()
+        */
+       public function reset() {
+               $this->userIsBanned = 0;
+               $this->userIsDisabled = 0;
+               $this->userIsEnabled = 0;
+               $this->userIsNotBanned = 0;
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::setData()
+        */
+       public function setData(Condition $condition) {
+               if ($condition->userIsBanned !== null) {
+                       $this->userIsBanned = $condition->userIsBanned;
+                       $this->userIsNotBanned = !$condition->userIsBanned;
+               }
+               if ($condition->userIsEnabled !== null) {
+                       $this->userIsEnabled = $condition->userIsEnabled;
+                       $this->userIsDisabled = !$condition->userIsEnabled;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\condition\ICondition::validate()
+        */
+       public function validate() {
+               if ($this->userIsBanned && $this->userIsNotBanned) {
+                       $this->errorMessage = 'wcf.user.condition.state.isBanned.error.conflict';
+                       
+                       throw new UserInputException('userIsBanned', 'conflict');
+               }
+               
+               if ($this->userIsDisabled && $this->userIsEnabled) {
+                       $this->errorMessage = 'wcf.user.condition.state.isEnabled.error.conflict';
+                       
+                       throw new UserInputException('userisEnabled', 'conflict');
+               }
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/condition/UserUsernameCondition.class.php b/wcfsetup/install/files/lib/system/condition/UserUsernameCondition.class.php
new file mode 100644 (file)
index 0000000..61ea094
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+namespace wcf\system\condition;
+use wcf\data\condition\Condition;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Condition implementation for the username of a user.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 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
+ */
+class UserUsernameCondition extends AbstractTextCondition implements IUserCondition {
+       /**
+        * @see \wcf\system\condition\AbstractTextCondition::$fieldName
+        */
+       protected $fieldName = 'username';
+       
+       /**
+        * @see \wcf\system\condition\AbstractSingleFieldCondition::$label
+        */
+       protected $label = 'wcf.user.username';
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::addUserCondition()
+        */
+       public function addUserCondition(Condition $condition, UserList $userList) {
+               $userList->getConditionBuilder()->add('user_table.username LIKE ?', array('%'.addcslashes($condition->username, '_%').'%'));
+       }
+       
+       /**
+        * @see \wcf\system\condition\IUserCondition::checkUser()
+        */
+       public function checkUser(Condition $condition, User $user) {
+               return mb_strpos($user->username, $condition->username) !== false;
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/cronjob/UserGroupAssignmentCronjob.class.php b/wcfsetup/install/files/lib/system/cronjob/UserGroupAssignmentCronjob.class.php
new file mode 100644 (file)
index 0000000..8feea8d
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+namespace wcf\system\cronjob;
+use wcf\data\cronjob\Cronjob;
+use wcf\data\user\UserAction;
+use wcf\system\cache\builder\UserGroupAssignmentCacheBuilder;
+use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
+
+/**
+ * Executes automatic user group assignments.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.cronjob
+ * @category   Community Framework
+ */
+class UserGroupAssignmentCronjob extends AbstractCronjob {
+       /**
+        * @see \wcf\system\cronjob\ICronjob::execute()
+        */
+       public function execute(Cronjob $cronjob) {
+               parent::execute($cronjob);
+               
+               $assignments = UserGroupAssignmentCacheBuilder::getInstance()->getData();
+               $usersToGroup = array();
+               foreach ($assignments as $assignment) {
+                       if (!isset($usersToGroup[$assignment->groupID])) {
+                               $usersToGroup[$assignment->groupID] = array();
+                       }
+                       
+                       $usersToGroup[$assignment->groupID] = array_merge($usersToGroup[$assignment->groupID], UserGroupAssignmentHandler::getInstance()->getUsers($assignment));
+               }
+               
+               foreach ($usersToGroup as $groupID => $users) {
+                       $userAction = new UserAction(array_unique($users), 'addToGroups', array(
+                               'addDefaultGroups' => false,
+                               'deleteOldGroups' => false,
+                               'groups' => array($groupID)
+                       ));
+                       $userAction->executeAction();
+               }
+       }
+}
index ebb982a1ee255a255a5bafdbfe793651e23f7818..7ad2d321a19ecff9aeff7633aba7e47d376f7aa8 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\system\option;
 use wcf\data\option\Option;
 use wcf\data\user\User;
+use wcf\data\user\UserList;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
@@ -37,6 +38,13 @@ class BirthdayOptionType extends DateOptionType {
                }
        }
        
+       /**
+        * @see \wcf\system\option\IOptionType::getData()
+        */
+       public function getData(Option $option, $newValue) {
+               return $newValue;
+       }
+       
        /**
         * @see \wcf\system\option\IOptionType::getFormElement()
         */
@@ -90,4 +98,60 @@ class BirthdayOptionType extends DateOptionType {
                
                return true;
        }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::addCondition()
+        */
+       public function addCondition(UserList $userList, Option $option, $value) {
+               $ageFrom = intval($value['ageFrom']);
+               $ageTo = intval($value['ageTo']);
+               
+               if ($ageFrom < 0 || $ageFrom > 120 || $ageTo < 0 || $ageTo > 120) return false;
+               
+               $dateFrom = DateUtil::getDateTimeByTimestamp(TIME_NOW)->sub(new \DateInterval('P'.($ageTo + 1).'Y'))->add(new \DateInterval('P1D'));
+               $dateTo = DateUtil::getDateTimeByTimestamp(TIME_NOW)->sub(new \DateInterval('P'.$ageFrom.'Y'));
+               
+               $userList->getConditionBuilder()->add('user_option_value.userOption'.User::getUserOptionID('birthdayShowYear').' = ?', array(1));
+               
+               if ($ageFrom && $ageTo) {
+                       $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' BETWEEN DATE(?) AND DATE(?)', array($dateFrom->format('Y-m-d'), $dateTo->format('Y-m-d')));
+               }
+               else if ($ageFrom) {
+                       $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' BETWEEN DATE(?) AND DATE(?)', array('1893-01-01', $dateTo->format('Y-m-d')));
+               }
+               else {
+                       $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' BETWEEN DATE(?) AND DATE(?)', array($dateFrom->format('Y-m-d'), DateUtil::getDateTimeByTimestamp(TIME_NOW)->add(new \DateInterval('P1D'))->format('Y-m-d')));
+               }
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::checkUser()
+        */
+       public function checkUser(User $user, Option $option, $value) {
+               if (!$user->birthdayShowYear || !$user->birthday) return false;
+               
+               $ageFrom = intval($value['ageFrom']);
+               $ageTo = intval($value['ageTo']);
+               
+               $userAge = DateUtil::getAge($user->birthday);
+               
+               if ($ageFrom && $ageTo) {
+                       return $userAge >= $ageFrom && $userAge <= $ageTo;
+               }
+               else if ($ageFrom) {
+                       return $userAge >= $ageFrom;
+               }
+               else {
+                       return $userAge <= $ageTo;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::getConditionData()
+        */
+       public function getConditionData(Option $option, $newValue) {
+               if (!$newValue['ageFrom'] && !$newValue['ageTo']) return null;
+               
+               return $newValue;
+       }
 }
index 614c961a94f83f42727c6263d17efbb977623331..12a288cad1580f89faa0c53373af08eb05b0d771 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\system\option;
 use wcf\data\option\Option;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\WCF;
 
@@ -62,4 +64,32 @@ class BooleanOptionType extends AbstractOptionType implements ISearchableUserOpt
                $conditions->add("option_value.userOption".$option->optionID." = ?", array(1));
                return true;
        }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::addCondition()
+        */
+       public function addCondition(UserList $userList, Option $option, $value) {
+               $value = intval($value);
+               if (!$value) return;
+               
+               if ($ageFrom < 0 || $ageFrom > 120 || $ageTo < 0 || $ageTo > 120) return false;
+               
+               $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' = ?', array(1));
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::checkUser()
+        */
+       public function checkUser(User $user, Option $option, $value) {
+               if (!$value) return false;
+               
+               return $user->getUserOption($option->optionName);
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::getConditionData()
+        */
+       public function getConditionData(Option $option, $newValue) {
+               return $newValue;
+       }
 }
diff --git a/wcfsetup/install/files/lib/system/option/ISearchableConditionUserOption.class.php b/wcfsetup/install/files/lib/system/option/ISearchableConditionUserOption.class.php
new file mode 100644 (file)
index 0000000..746d9f9
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+namespace wcf\system\option;
+use wcf\data\option\Option;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
+
+/**
+ * Searchable user option types available for conditions have to implement this interface.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.option
+ * @category   Community Framework
+ */
+interface ISearchableConditionUserOption extends ISearchableUserOption {
+       /**
+        * Adds the condition to the given user list to fetch the users which have
+        * the given value for the given option.
+        * 
+        * @param       \wcf\data\user\UserList         $userList
+        * @param       \wcf\data\option\Option         $option
+        * @param       mixed                           $value
+        */
+       public function addCondition(UserList $userList, Option $option, $value);
+       
+       /**
+        * Returns true if given the user option of the given user matches a certain
+        * value.
+        * 
+        * @param       \wcf\data\user\User             $user
+        * @param       \wcf\data\option\Option         $option
+        * @param       mixed                           $value
+        * @return      boolean
+        */
+       public function checkUser(User $user, Option $option, $value);
+       
+       /**
+        * Returns the data of the condition or null if the option should be igored.
+        * 
+        * @return      mixed
+        */
+       public function getConditionData(Option $option, $newValue);
+}
index 4a3c2e9633e11653025e4c804f0320123dd6540d..4a752d0dc67db5694d49e99c2f9391384e3e026b 100644 (file)
@@ -26,9 +26,9 @@ interface ISearchableUserOption {
        /**
         * Returns a condition for search sql query.
         * 
-        * @param       \wcf\system\database\condition\PreparedStatementConditionBuilder                $conditions
+        * @param       \wcf\system\database\condition\PreparedStatementConditionBuilder        $conditions
         * @param       \wcf\data\option\Option                                                 $option
-        * @param       string                                                                  $value
+        * @param       mixed                                                                   $value
         * @return      boolean
         */
        public function getCondition(PreparedStatementConditionBuilder &$conditions, Option $option, $value);
index 99d749c8ef6f7ceee12d8aa97b471399e28d299f..a5e9a23015d2199ae0b94d2c2046630bdbed26c5 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\system\option;
 use wcf\data\option\Option;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
@@ -35,7 +37,7 @@ class MultiSelectOptionType extends SelectOptionType {
        public function getSearchFormElement(Option $option, $value) {
                WCF::getTPL()->assign(array(
                        'option' => $option,
-                       'searchOption' => isset($_POST['searchOptions'][$option->optionName]),
+                       'searchOption' => $value !== $option->defaultValue || isset($_POST['searchOptions'][$option->optionName]),
                        'selectOptions' => $option->parseSelectOptions(),
                        'value' => (!is_array($value) ? explode("\n", $value) : $value)
                ));
@@ -75,4 +77,32 @@ class MultiSelectOptionType extends SelectOptionType {
                $conditions->add("option_value.userOption".$option->optionID." REGEXP '".'(^|\n)'.implode('\n([^\n]*\n)*', array_map('escapeString', $value)).'($|\n)'."'");
                return true;
        }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::addCondition()
+        */
+       public function addCondition(UserList $userList, Option $option, $value) {
+               if (!is_array($value) || empty($value)) return false;
+               $value = ArrayUtil::trim($value);
+               
+               $userList->getConditionBuilder()->add("user_option_value.userOption".$option->optionID." REGEXP '".'(^|\n)'.implode('\n([^\n]*\n)*', array_map('escapeString', $value)).'($|\n)'."'");
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::checkUser()
+        */
+       public function checkUser(User $user, Option $option, $value) {
+               if (!is_array($value) || empty($value)) return false;
+               
+               $optionValues = explode('\n', $user->getUserOption($option->optionName));
+               
+               return count(array_diff($optionValues, $value)) > 0;
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::getConditionData()
+        */
+       public function getConditionData(Option $option, $newValue) {
+               return $newValue;
+       }
 }
index 0bfed198736321a31261100fb0ba38a1a6179b11..151c31d1cb3d33cb119bfe7d59bd85909f6987fc 100644 (file)
@@ -406,7 +406,7 @@ class OptionHandler implements IOptionHandler {
                if (!$this->didInit) {
                        // get active options
                        $this->loadActiveOptions($this->categoryName);
-                               
+                       
                        // mark options as initialized
                        $this->didInit = true;
                }
index aaaec7edad15adee1deaf7a89f70aad382751c0e..fc73717e54b5581f472cd22b0151cd7ce718139c 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\system\option;
 use wcf\data\option\Option;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 
 /**
@@ -25,4 +27,25 @@ class PasswordOptionType extends TextOptionType {
        public function getCondition(PreparedStatementConditionBuilder &$conditions, Option $option, $value) {
                return false;
        }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::addCondition()
+        */
+       public function addCondition(UserList $userList, Option $option, $value) {
+               // does nothing
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::checkUser()
+        */
+       public function checkUser(User $user, Option $option, $value) {
+               return false;
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::getConditionData()
+        */
+       public function getConditionData(Option $option, $newValue) {
+               return $newValue;
+       }
 }
index 149689b24854084ea6e91fec7b72795a4e4d2079..4179b0971f9d1478f2f7b4f3d06618548bcae74b 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\system\option;
 use wcf\data\option\Option;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
@@ -16,7 +18,7 @@ use wcf\util\StringUtil;
  * @subpackage system.option
  * @category   Community Framework
  */
-class RadioButtonOptionType extends AbstractOptionType implements ISearchableUserOption {
+class RadioButtonOptionType extends AbstractOptionType implements ISearchableConditionUserOption {
        /**
         * name of the template that contains the form element of this option type
         * @var string
@@ -70,7 +72,7 @@ class RadioButtonOptionType extends AbstractOptionType implements ISearchableUse
         */
        public function getSearchFormElement(Option $option, $value) {
                $this->templateName = 'radioButtonSearchableOptionType';
-               WCF::getTPL()->assign('searchOption', empty($_POST) || isset($_POST['searchOptions'][$option->optionName]));
+               WCF::getTPL()->assign('searchOption', $value !== $option->defaultValue || isset($_POST['searchOptions'][$option->optionName]));
                
                return $this->getFormElement($option, $value);
        }
@@ -84,4 +86,25 @@ class RadioButtonOptionType extends AbstractOptionType implements ISearchableUse
                $conditions->add("option_value.userOption".$option->optionID." = ?", array(StringUtil::trim($value)));
                return true;
        }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::addCondition()
+        */
+       public function addCondition(UserList $userList, Option $option, $value) {
+               $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' = ?', array(StringUtil::trim($value)));
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::checkUser()
+        */
+       public function checkUser(User $user, Option $option, $value) {
+               return mb_strtolower($user->getUserOption($option->optionName)) == mb_strtolower(StringUtil::trim($value));
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::getConditionData()
+        */
+       public function getConditionData(Option $option, $newValue) {
+               return $newValue;
+       }
 }
index 4bc814079725d71976437f089791f85199d6ed4c..e95ce3b6ab62a09a949c785dd9ba9337c8df7007 100644 (file)
@@ -43,7 +43,7 @@ class SelectOptionType extends RadioButtonOptionType {
                        'disableOptions' => $options['disableOptions'],
                        'enableOptions' => $options['enableOptions'],
                        'option' => $option,
-                       'searchOption' => isset($_POST['searchOptions'][$option->optionName]),
+                       'searchOption' => ($value !== null && $value !== $option->defaultValue) || isset($_POST['searchOptions'][$option->optionName]),
                        'selectOptions' => $option->parseSelectOptions(),
                        'value' => $value
                ));
index 71f4d6988a9332368fae733a2cbe5ca15cedd1f0..d16c77ba67804ae7b3c53eafae9f1a1dfc555ba7 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\system\option;
 use wcf\data\option\Option;
+use wcf\data\user\User;
+use wcf\data\user\UserList;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
@@ -16,7 +18,7 @@ use wcf\util\StringUtil;
  * @subpackage system.option
  * @category   Community Framework
  */
-class TextOptionType extends AbstractOptionType implements ISearchableUserOption {
+class TextOptionType extends AbstractOptionType implements ISearchableConditionUserOption {
        /**
         * input type
         * @var string
@@ -50,7 +52,7 @@ class TextOptionType extends AbstractOptionType implements ISearchableUserOption
                        'option' => $option,
                        'inputType' => $this->inputType,
                        'inputClass' => $this->inputClass,
-                       'searchOption' => isset($_POST['searchOptions'][$option->optionName]),
+                       'searchOption' => $value !== $option->defaultValue || isset($_POST['searchOptions'][$option->optionName]),
                        'value' => $value
                ));
                return WCF::getTPL()->fetch('textSearchableOptionType');
@@ -111,4 +113,37 @@ class TextOptionType extends AbstractOptionType implements ISearchableUserOption
                
                return $newValue;
        }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::addCondition()
+        */
+       public function addCondition(UserList $userList, Option $option, $value) {
+               $value = StringUtil::trim($value);
+               if ($value == '') {
+                       $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' = ?', array(''));
+               }
+               else {
+                       $userList->getConditionBuilder()->add('user_option_value.userOption'.$option->optionID.' LIKE ?', array('%'.addcslashes($value, '_%').'%'));
+               }
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::checkUser()
+        */
+       public function checkUser(User $user, Option $option, $value) {
+               $value = StringUtil::trim($value);
+               if ($value == '') {
+                       return $user->getUserOption($option->optionName) == '';
+               }
+               else {
+                       return mb_stripos($user->getUserOption($option->optionName), $value) !== false;
+               }
+       }
+       
+       /**
+        * @see \wcf\system\option\ISearchableConditionUserOption::getConditionData()
+        */
+       public function getConditionData(Option $option, $newValue) {
+               return $newValue;
+       }
 }
index 93170da86622d536d9f03a795ac5141982b282a6..26cf6231ab682cf7891f17f068f19e4522d872b5 100644 (file)
@@ -32,7 +32,7 @@ class TextareaOptionType extends TextOptionType {
        public function getSearchFormElement(Option $option, $value) {
                WCF::getTPL()->assign(array(
                        'option' => $option,
-                       'searchOption' => isset($_POST['searchOptions'][$option->optionName]),
+                       'searchOption' => $value !== $option->defaultValue || isset($_POST['searchOptions'][$option->optionName]),
                        'value' => $value
                ));
                return WCF::getTPL()->fetch('textareaSearchableOptionType');
index 1dce1efc254c5a21be2e66d7da6b32bda15556cb..3f7290a4c6e1e90e55ef7cf6d9df5b9ff0720d40 100644 (file)
@@ -12,5 +12,4 @@ use wcf\system\option\user\group\UserGroupsUserGroupOptionType;
  * @subpackage system.option
  * @category   Community Framework
  */
-class UserGroupOptionType extends UserGroupsUserGroupOptionType {
-}
+class UserGroupOptionType extends UserGroupsUserGroupOptionType { }
index 4ff326c029ff4c7f661bd988f8d130e1363de541..6c2d28e1d85799f64b86a1742296a4ea25f2d439 100644 (file)
@@ -5,6 +5,7 @@ use wcf\data\option\Option;
 use wcf\data\user\option\ViewableUserOption;
 use wcf\data\user\User;
 use wcf\system\exception\UserInputException;
+use wcf\system\option\ISearchableConditionUserOption;
 use wcf\system\option\OptionHandler;
 
 /**
@@ -24,25 +25,25 @@ class UserOptionHandler extends OptionHandler {
        protected $cacheClass = 'wcf\system\cache\builder\UserOptionCacheBuilder';
        
        /**
-        * true, if within registration process
+        * true if within registration process
         * @var boolean
         */
        public $inRegistration = false;
        
        /**
-        * true, if within edit mode
+        * true if within edit mode
         * @var boolean
         */
        public $editMode = true;
        
        /**
-        * true, if within search mode
+        * true if within search mode
         * @var boolean
         */
        public $searchMode = false;
        
        /**
-        * true, if empty options should be removed
+        * true if empty options should be removed
         * @var boolean
         */
        public $removeEmptyOptions = false;
@@ -53,6 +54,12 @@ class UserOptionHandler extends OptionHandler {
         */
        public $user = null;
        
+       /**
+        * true if the condition mode during search mode is enabled
+        * @var boolean
+        */
+       public $conditionMode = false;
+       
        /**
         * Shows empty options.
         */
@@ -112,6 +119,37 @@ class UserOptionHandler extends OptionHandler {
                $this->optionValues = array();
        }
        
+       /**
+        * Enables the condition mode.
+        * 
+        * @param       boolean         $enable
+        */
+       public function enableConditionMode($enable = true) {
+               if (!$this->searchMode) {
+                       $this->enableSearchMode();
+               }
+               
+               $this->conditionMode = $enable;
+       }
+       
+       /**
+        * Returns the option values.
+        * 
+        * @return      array
+        */
+       public function getOptionValues() {
+               return $this->optionValues;
+       }
+       
+       /**
+        * Sets the option values.
+        * 
+        * @param       array           $values
+        */
+       public function setOptionValues(array $values) {
+               $this->optionValues = $values;
+       }
+       
        /**
         * @see \wcf\system\option\OptionHandler::getOption()
         */
@@ -181,6 +219,10 @@ class UserOptionHandler extends OptionHandler {
                        return false;
                }
                
+               if ($this->conditionMode && (!$this->getTypeObject($option->optionType) instanceof ISearchableConditionUserOption)) {
+                       return false;
+               }
+               
                if ($this->user !== null) {
                        $option->setUser($this->user);
                }
index 4351f18828aa33e592770764ce6434e3d24f4311..bb0bd712b7d651a96022296594334cfbeb0ede72 100644 (file)
@@ -4,6 +4,7 @@ use wcf\data\object\type\ObjectTypeCache;
 use wcf\data\user\UserProfileAction;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\SystemException;
+use wcf\system\user\group\assignment\UserGroupAssignmentHandler;
 use wcf\system\SingletonFactory;
 use wcf\system\WCF;
 
@@ -83,6 +84,10 @@ class UserActivityPointHandler extends SingletonFactory {
                
                // update user ranks
                $this->updateUserRanks(array($userID));
+               
+               // check if the user will be automatically added to new user groups
+               // because of the new activity points
+               UserGroupAssignmentHandler::getInstance()->checkUsers(array($userID));
        }
        
        /**
@@ -133,6 +138,10 @@ class UserActivityPointHandler extends SingletonFactory {
                // update activity points for given user ids
                if ($updateUsers) {
                        $this->updateUsers($userIDs);
+                       
+                       // check if one of the user will be automatically added
+                       // to new user groups because of the new activity points
+                       UserGroupAssignmentHandler::getInstance()->checkUsers($userIDs);
                }
        }
        
diff --git a/wcfsetup/install/files/lib/system/user/group/assignment/UserGroupAssignmentHandler.class.php b/wcfsetup/install/files/lib/system/user/group/assignment/UserGroupAssignmentHandler.class.php
new file mode 100644 (file)
index 0000000..2827063
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+namespace wcf\system\user\group\assignment;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\user\group\assignment\UserGroupAssignment;
+use wcf\data\user\User;
+use wcf\data\user\UserAction;
+use wcf\data\user\UserList;
+use wcf\system\cache\builder\UserGroupAssignmentCacheBuilder;
+use wcf\system\SingletonFactory;
+
+/**
+ * Handles user group assignment-related matters.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.user.group.assignment
+ * @category   Community Framework
+ */
+class UserGroupAssignmentHandler extends SingletonFactory {
+       /**
+        * list of grouped user group assignment condition object types
+        * @var array
+        */
+       protected $groupedObjectTypes = array();
+       
+       /**
+        * Checks if the users with the given ids should be assigned to new user
+        * groups.
+        * 
+        * Note: This method uses the user ids as a parameter instead of user objects
+        * on purpose to make sure the latest data of the users are fetched.
+        * 
+        * @param       array<integer>          $userIDs
+        */
+       public function checkUsers(array $userIDs) {
+               if (empty($userIDs)) return;
+               
+               $userList = new UserList();
+               $userList->getConditionBuilder()->add('user_table.userID IN (?)', array($userIDs));
+               $userList->readObjects();
+               
+               $assignments = UserGroupAssignmentCacheBuilder::getInstance()->getData();
+               foreach ($userList as $user) {
+                       $groupIDs = $user->getGroupIDs();
+                       $newGroupIDs = array();
+                       
+                       foreach ($assignments as $assignment) {
+                               if (in_array($assignment->groupID, $groupIDs) || in_array($assignment->groupID, $newGroupIDs)) {
+                                       continue;
+                               }
+                               
+                               $checkFailed = false;
+                               $conditions = $assignment->getConditions();
+                               foreach ($conditions as $condition) {
+                                       if (!$condition->getObjectType()->getProcessor()->checkUser($condition, $user)) {
+                                               $checkFailed = true;
+                                               break;
+                                       }
+                               }
+                               
+                               if (!$checkFailed) {
+                                       $newGroupIDs[] = $assignment->groupID;
+                               }
+                       }
+                       
+                       $userAction = new UserAction(array($user), 'addToGroups', array(
+                               'addDefaultGroups' => false,
+                               'deleteOldGroups' => false,
+                               'groups' => $newGroupIDs
+                       ));
+                       $userAction->executeAction();
+               }
+       }
+       
+       /**
+        * Returns the list of grouped user group assignment condition object types.
+        * 
+        * @return      array
+        */
+       public function getGroupedObjectTypes() {
+               return $this->groupedObjectTypes;
+       }
+       
+       /**
+        * Returns the users who fullfil all conditions of the given user group
+        * assignment.
+        * 
+        * @param       \wcf\data\user\group\assignment\UserGroupAssignment     $assignment
+        * @return      array<\wcf\data\user\User>
+        */
+       public function getUsers(UserGroupAssignment $assignment) {
+               $userList = new UserList();
+               $userList->getConditionBuilder()->add('user_table.userID NOT IN (SELECT userID FROM wcf'.WCF_N.'_user_to_group WHERE groupID = ?)', array(
+                       $assignment->groupID
+               ));
+               
+               $conditions = $assignment->getConditions();
+               foreach ($conditions as $condition) {
+                       $condition->getObjectType()->getProcessor()->addUserCondition($condition, $userList);
+               }
+               $userList->readObjects();
+               
+               return $userList->getObjects();
+       }
+       
+       /**
+        * @see \wcf\system\SingletonFactory::init()
+        */
+       protected function init() {
+               $objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.condition.userGroupAssignment');
+               foreach ($objectTypes as $objectType) {
+                       if (!$objectType->conditiongroup) continue;
+                       
+                       if (!isset($this->groupedObjectTypes[$objectType->conditiongroup])) {
+                               $this->groupedObjectTypes[$objectType->conditiongroup] = array();
+                       }
+                       
+                       $this->groupedObjectTypes[$objectType->conditiongroup][$objectType->objectTypeID] = $objectType;
+               }
+       }
+}
index 7f8b6930cc8f9038926eccca223c7b954aef916c..f7cd7514a59193c707f430984ebe87d31c3962f3 100644 (file)
@@ -339,7 +339,7 @@ final class DateUtil {
                if (isset($value[0])) $year = intval($value[0]);
                if (isset($value[1])) $month = intval($value[1]);
                if (isset($value[2])) $day = intval($value[2]);
-                       
+               
                // calc
                if ($year) {
                        $age = self::format(null, 'Y') - $year;
index c9ac8639a691464c5757782bdc487bc1894e5398..7c8d0dd5bf26b4083f0fca1603f0a2e4110a1da7 100644 (file)
                <item name="wcf.acp.group.option.error.validationFailed"><![CDATA[Sie haben einen ungültigen Inhalt eingegeben.]]></item>
                <item name="wcf.acp.group.option.admin.user.canDisableAvatar"><![CDATA[Kann Avatar sperren]]></item>
                <item name="wcf.acp.group.option.admin.user.canDisableSignature"><![CDATA[Kann Signatur sperren]]></item>
+               <item name="wcf.acp.group.option.admin.user.canManageGroupAssignment"><![CDATA[Kann automatische Gruppenzuordnungen verwalten]]></item>
+               <item name="wcf.acp.group.assignment.add"><![CDATA[Automatische Benutzergruppen-Zuordnung hinzufügen]]></item>
+               <item name="wcf.acp.group.assignment.button.add"><![CDATA[Automatische Zuordnung hinzufügen]]></item>
+               <item name="wcf.acp.group.assignment.button.list"><![CDATA[Automatische Zuordnungen auflisten]]></item>
+               <item name="wcf.acp.group.assignment.conditions"><![CDATA[Bedingungen]]></item>
+               <item name="wcf.acp.group.assignment.conditions.description"><![CDATA[Benutzer müssen die folgenden Bedingungen erfüllen um automatisch der Benutzergruppe hinzugefügt zu werden]]></item>
+               <item name="wcf.acp.group.assignment.delete.confirmMessage"><![CDATA[Wollen Sie diese automatische Benutzergruppen-Zuordnung wirklich löschen?]]></item>
+               <item name="wcf.acp.group.assignment.edit"><![CDATA[Automatische Benutzergruppen-Zuordnung bearbeiten]]></item>
+               <item name="wcf.acp.group.assignment.error.noConditions"><![CDATA[Sie haben keine Bedingungen ausgewählt.]]></item>
+               <item name="wcf.acp.group.assignment.list"><![CDATA[Automatische Benutzergruppen-Zuordnungen]]></item>
+               <item name="wcf.acp.group.assignment.userGroup"><![CDATA[Benutzergruppe]]></item>
+               <item name="wcf.acp.group.assignment.isDisabled"><![CDATA[Automatische Zuordnung deaktivieren]]></item>
        </category>
        
        <category name="wcf.acp.index">
                <item name="wcf.acp.menu.link.maintenance.import"><![CDATA[Datenimport]]></item>
                <item name="wcf.acp.menu.link.maintenance.rebuildData"><![CDATA[Anzeigen aktualisieren]]></item>
                <item name="wcf.acp.menu.link.log.stat"><![CDATA[Statistiken]]></item>
+               <item name="wcf.acp.menu.link.group.assignment"><![CDATA[Automatische Zuordnungen]]></item>
        </category>
        
        <category name="wcf.acp.option">
@@ -1868,6 +1881,7 @@ Fehler sind beispielsweise:
                <item name="wcf.global.form.error.lessThan"><![CDATA[Der eingebene Wert muss kleiner sein als {#$lessThan}.]]></item>
                <item name="wcf.global.form.error.lessThan.javaScript"><![CDATA[{literal}Der eingebene Wert muss kleiner sein als {#$lessThan}.{/literal}]]></item>
                <item name="wcf.global.form.error.multilingual"><![CDATA[Bitte füllen Sie dieses Eingabefeld für jede Sprache aus.]]></item>
+               <item name="wcf.global.form.error.noValidSelection"><![CDATA[Wählen Sie eine der angebotenen Optionen aus.]]></item>
                <item name="wcf.global.form.error.securityToken"><![CDATA[Ihre Sitzung ist abgelaufen, bitte senden Sie das Formular erneut ab.]]></item>
        </category>
        
@@ -2478,6 +2492,44 @@ Sollten Sie sich nicht auf der Website: {@PAGE_TITLE|language} angemeldet haben,
                <item name="wcf.user.avatar.upload.success"><![CDATA[Der neue Avatar wurde erfolgreich gespeichert.]]></item>
        </category>
        
+       <category name="wcf.user.condition">
+               <item name="wcf.user.condition.activityPoints.greaterThan"><![CDATA[Punkte mehr als]]></item>
+               <item name="wcf.user.condition.activityPoints.greaterThan.error.lessThan"><![CDATA[Die Werte in „Punkte weniger als“ und „Punkte mehr als“ sind widersprüchlich.]]></item>
+               <item name="wcf.user.condition.activityPoints.lessThan"><![CDATA[Punkte weniger als]]></item>
+               <item name="wcf.user.condition.avatar"><![CDATA[Avatar]]></item>
+               <item name="wcf.user.condition.avatar.avatar"><![CDATA[Eigener Avatar]]></item>
+               <item name="wcf.user.condition.avatar.gravatar"><![CDATA[Gravatar]]></item>
+               <item name="wcf.user.condition.avatar.noAvatar"><![CDATA[Kein Avatar]]></item>
+               <item name="wcf.user.condition.conditionGroup.contents"><![CDATA[Inhalte]]></item>
+               <item name="wcf.user.condition.conditionGroup.general"><![CDATA[Allgemeine Daten]]></item>
+               <item name="wcf.user.condition.conditionGroup.userOptions"><![CDATA[Persönliche Daten]]></item>
+               <item name="wcf.user.condition.groupIDs"><![CDATA[in Benutzergruppen]]></item>
+               <item name="wcf.user.condition.groupIDs.description"><![CDATA[Benutzer müssen in den ausgewählten Benutzergruppen Mitglied sein.]]></item>
+               <item name="wcf.user.condition.integerProperty.error.maxValue"><![CDATA[Der Wert darf nicht kleiner sein {#$minValue}.]]></item>
+               <item name="wcf.user.condition.integerProperty.error.minValue"><![CDATA[Der Wert muss größer sein als {#$minValue}.]]></item>
+               <item name="wcf.user.condition.languages"><![CDATA[Sprachen]]></item>
+               <item name="wcf.user.condition.likesReceived.greaterThan"><![CDATA[Erhaltene Likes mehr als]]></item>
+               <item name="wcf.user.condition.likesReceived.greaterThan.error.lessThan"><![CDATA[Die Werte in „Erhaltene Likes weniger als“ und „Erhaltene Likes mehr als“ sind widersprüchlich.]]></item>
+               <item name="wcf.user.condition.likesReceived.lessThan"><![CDATA[Erhaltene Likes weniger als]]></item>
+               <item name="wcf.user.condition.notGroupIDs"><![CDATA[nicht in Benutzergruppen]]></item>
+               <item name="wcf.user.condition.notGroupIDs.description"><![CDATA[Benutzer dürfen in den ausgewählten Benutzergruppen nicht Mitglied sein.]]></item>
+               <item name="wcf.user.condition.notGroupIDs.error.groupIDsIntersection"><![CDATA[Die ausgewählten Benutzergruppen in „in Benutzergruppen“ und „nicht in Benutzergruppen“ sind widersprüchlich.]]></item>
+               <item name="wcf.user.condition.registrationDate"><![CDATA[Registrierungsdatum]]></item>
+               <item name="wcf.user.condition.registrationDate.error.endBeforeStart"><![CDATA[Das Enddatum ist vor dem Startdatum.]]></item>
+               <item name="wcf.user.condition.registrationDate.error.endNotValid"><![CDATA[Das Startdatum ist ungültig.]]></item>
+               <item name="wcf.user.condition.registrationDate.error.startNotValid"><![CDATA[Das Enddatum ist ungültig.]]></item>
+               <item name="wcf.user.condition.registrationDateInterval.greaterThan"><![CDATA[mehr als X Tage registiert]]></item>
+               <item name="wcf.user.condition.registrationDateInterval.greaterThan.error.lessThan"><![CDATA[Die Werte in „weniger als X Tage registiert“ und „mehr als X Tage registiert“ sind widersprüchlich.]]></item>
+               <item name="wcf.user.condition.registrationDateInterval.lessThan"><![CDATA[weniger als X Tage registiert]]></item>
+               <item name="wcf.user.condition.state"><![CDATA[Zustand]]></item>
+               <item name="wcf.user.condition.state.isBanned"><![CDATA[Gesperrt]]></item>
+               <item name="wcf.user.condition.state.isBanned.error.conflict"><![CDATA[„Gesperrt“ und „Nicht gesperrt“ können nicht gleichzeitig ausgewählt werden.]]></item>
+               <item name="wcf.user.condition.state.isDisabled"><![CDATA[Nicht aktiviert]]></item>
+               <item name="wcf.user.condition.state.isEnabled"><![CDATA[Aktiviert]]></item>
+               <item name="wcf.user.condition.state.isEnabled.error.conflict"><![CDATA[„Aktiviert“ und „Nicht aktiviert“ können nicht gleichzeitig ausgewählt werden.]]></item>
+               <item name="wcf.user.condition.state.isNotBanned"><![CDATA[Nicht gesperrt]]></item>
+       </category>
+       
        <category name="wcf.user.notification">
                <item name="wcf.user.notification.button.confirmed"><![CDATA[OK]]></item>
                <item name="wcf.user.notification.count"><![CDATA[if (data.returnValues.count == 0) { "Keine Benachrichtigungen" } else if (data.returnValues.count == 1) { "Eine Benachrichtigung" } else { data.returnValues.count + " Benachrichtigungen" }]]></item>
index b79060a6086fcaf64e6bc3a22281217cced18144..335fd2413fb4e4064f6c23798cbc4ba12a85acc1 100644 (file)
@@ -373,6 +373,18 @@ Examples for medium ID detection:
                <item name="wcf.acp.group.option.error.validationFailed"><![CDATA[You have entered an invalid value.]]></item>
                <item name="wcf.acp.group.option.admin.user.canDisableAvatar"><![CDATA[Can block avatar]]></item>
                <item name="wcf.acp.group.option.admin.user.canDisableSignature"><![CDATA[Can block signature]]></item>
+               <item name="wcf.acp.group.option.admin.user.canManageGroupAssignment"><![CDATA[Can manage automatic user group assignments]]></item>
+               <item name="wcf.acp.group.assignment.add"><![CDATA[Add User Group Automatic Assignment]]></item>
+               <item name="wcf.acp.group.assignment.button.add"><![CDATA[Add Automatic Assignment]]></item>
+               <item name="wcf.acp.group.assignment.button.list"><![CDATA[Automatic Assignments]]></item>
+               <item name="wcf.acp.group.assignment.conditions"><![CDATA[Conditions]]></item>
+               <item name="wcf.acp.group.assignment.conditions.description"><![CDATA[Users need to fulfill the following conditions to be automatically added to the user group]]></item>
+               <item name="wcf.acp.group.assignment.delete.confirmMessage"><![CDATA[Do you really want to delete this automatic user group assignment]]></item>
+               <item name="wcf.acp.group.assignment.edit"><![CDATA[Edit User Group Automatic Assignment]]></item>
+               <item name="wcf.acp.group.assignment.error.noConditions"><![CDATA[You have not chosen any condition.]]></item>
+               <item name="wcf.acp.group.assignment.list"><![CDATA[Automatic User Group Assignments]]></item>
+               <item name="wcf.acp.group.assignment.userGroup"><![CDATA[User Group]]></item>
+               <item name="wcf.acp.group.assignment.isDisabled"><![CDATA[Disable Automatic Assignment]]></item>
        </category>
        
        <category name="wcf.acp.index">
@@ -564,6 +576,7 @@ Examples for medium ID detection:
                <item name="wcf.acp.menu.link.maintenance.import"><![CDATA[Data Import]]></item>
                <item name="wcf.acp.menu.link.maintenance.rebuildData"><![CDATA[Rebuild Data]]></item>
                <item name="wcf.acp.menu.link.log.stat"><![CDATA[Statistics]]></item>
+               <item name="wcf.acp.menu.link.group.assignment"><![CDATA[Automatic Assignments]]></item>
        </category>
        
        <category name="wcf.acp.option">
@@ -1838,6 +1851,7 @@ Errors are:
                <item name="wcf.global.form.error.lessThan"><![CDATA[The entered value has to be less than {#$lessThan}.]]></item>
                <item name="wcf.global.form.error.lessThan.javaScript"><![CDATA[{literal}The entered value has to be less than {#$lessThan}.{/literal}]]></item>
                <item name="wcf.global.form.error.multilingual"><![CDATA[Please fill in this field for all languages.]]></item>
+               <item name="wcf.global.form.error.noValidSelection"><![CDATA[Choose one of the available options.]]></item>
                <item name="wcf.global.form.error.securityToken"><![CDATA[Your session has expired, please submit the form again.]]></item>
        </category>
        
@@ -2447,6 +2461,44 @@ You can safely ignore this email if you did not register with the website: {@PAG
                <item name="wcf.user.avatar.upload.success"><![CDATA[Avatar has been saved.]]></item>
        </category>
        
+       <category name="wcf.user.condition">
+               <item name="wcf.user.condition.activityPoints.greaterThan"><![CDATA[Points Greater Than]]></item>
+               <item name="wcf.user.condition.activityPoints.greaterThan.error.lessThan"><![CDATA[The values in “Points Less Than“ and “Points Greater Than“ are conflicting.]]></item>
+               <item name="wcf.user.condition.activityPoints.lessThan"><![CDATA[Points Less Than]]></item>
+               <item name="wcf.user.condition.avatar"><![CDATA[Avatar]]></item>
+               <item name="wcf.user.condition.avatar.avatar"><![CDATA[Own Avatar]]></item>
+               <item name="wcf.user.condition.avatar.gravatar"><![CDATA[Gravatar]]></item>
+               <item name="wcf.user.condition.avatar.noAvatar"><![CDATA[No Avatar]]></item>
+               <item name="wcf.user.condition.conditionGroup.contents"><![CDATA[Contents]]></item>
+               <item name="wcf.user.condition.conditionGroup.general"><![CDATA[General Data]]></item>
+               <item name="wcf.user.condition.conditionGroup.userOptions"><![CDATA[Personal Data]]></item>
+               <item name="wcf.user.condition.groupIDs"><![CDATA[in User Groups]]></item>
+               <item name="wcf.user.condition.groupIDs.description"><![CDATA[Users have to be a member of the selected user groups.]]></item>
+               <item name="wcf.user.condition.integerProperty.error.maxValue"><![CDATA[The value may not be less than {#$minValue}.]]></item>
+               <item name="wcf.user.condition.integerProperty.error.minValue"><![CDATA[The value has to be greater than {#$minValue}.]]></item>
+               <item name="wcf.user.condition.languages"><![CDATA[Languages]]></item>
+               <item name="wcf.user.condition.likesReceived.greaterThan"><![CDATA[Likes Received Greater Than]]></item>
+               <item name="wcf.user.condition.likesReceived.greaterThan.error.lessThan"><![CDATA[The values in “Likes Received Less Than“ and “Likes Received Greater Than“ are conflicting.]]></item>
+               <item name="wcf.user.condition.likesReceived.lessThan"><![CDATA[Likes Received Less Than]]></item>
+               <item name="wcf.user.condition.notGroupIDs"><![CDATA[not in User Groups]]></item>
+               <item name="wcf.user.condition.notGroupIDs.description"><![CDATA[Users may not be a member of the selected user groups.]]></item>
+               <item name="wcf.user.condition.notGroupIDs.error.groupIDsIntersection"><![CDATA[The selected user groups in “in User Groups“ and “not in User Groups“ are conflicting.]]></item>
+               <item name="wcf.user.condition.registrationDate"><![CDATA[Registration Date]]></item>
+               <item name="wcf.user.condition.registrationDate.error.endBeforeStart"><![CDATA[The end date is prior to the start date.]]></item>
+               <item name="wcf.user.condition.registrationDate.error.endNotValid"><![CDATA[The start date is not valid.]]></item>
+               <item name="wcf.user.condition.registrationDate.error.startNotValid"><![CDATA[The end date is not valid.]]></item>
+               <item name="wcf.user.condition.registrationDateInterval.greaterThan"><![CDATA[registered less than X days ago]]></item>
+               <item name="wcf.user.condition.registrationDateInterval.greaterThan.error.lessThan"><![CDATA[The values in “registered less than X days ago“ and “registered more than X days ago“ are conflicting.]]></item>
+               <item name="wcf.user.condition.registrationDateInterval.lessThan"><![CDATA[registered more than X days ago]]></item>
+               <item name="wcf.user.condition.state"><![CDATA[State]]></item>
+               <item name="wcf.user.condition.state.isBanned"><![CDATA[Banned]]></item>
+               <item name="wcf.user.condition.state.isBanned.error.conflict"><![CDATA[You may not simultaneously select “Banned“ and “Not Banned“.]]></item>
+               <item name="wcf.user.condition.state.isDisabled"><![CDATA[Awaiting Approval]]></item>
+               <item name="wcf.user.condition.state.isEnabled"><![CDATA[Approved]]></item>
+               <item name="wcf.user.condition.state.isEnabled.error.conflict"><![CDATA[You may not simultaneously select “Approved“ and “Awaiting Approval“.]]></item>
+               <item name="wcf.user.condition.state.isNotBanned"><![CDATA[Not Banned]]></item>
+       </category>
+       
        <category name="wcf.user.notification">
                <item name="wcf.user.notification.button.confirmed"><![CDATA[OK]]></item>
                <item name="wcf.user.notification.count"><![CDATA[if (data.returnValues.count == 0) { "No Notifications" } else if (data.returnValues.count == 1) { "1 Notification" } else { data.returnValues.count + " Notifications" }]]></item>
index 47e554c8caa23d5f01d4636ffd5cc2a6fa7a71c2..3a52154bc9c626188faa5a4bb45d1d8ada1fce6a 100644 (file)
@@ -268,6 +268,14 @@ CREATE TABLE wcf1_comment_response (
        KEY lastResponseTime (userID, time)
 );
 
+DROP TABLE IF EXISTS wcf1_condition;
+CREATE TABLE wcf1_condition (
+       conditionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       objectTypeID INT(10) NOT NULL,
+       objectID INT(10) NOT NULL,
+       conditionData MEDIUMTEXT
+);
+
 DROP TABLE IF EXISTS wcf1_core_object;
 CREATE TABLE wcf1_core_object (
        objectID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
@@ -1088,6 +1096,14 @@ CREATE TABLE wcf1_user_group (
        showOnTeamPage TINYINT(1) NOT NULL DEFAULT 0
 );
 
+DROP TABLE IF EXISTS wcf1_user_group_assignment;
+CREATE TABLE wcf1_user_group_assignment (
+       assignmentID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       groupID INT(10) NOT NULL,
+       title VARCHAR(255) NOT NULL,
+       isDisabled TINYINT(1) NOT NULL DEFAULT 0
+);
+
 DROP TABLE IF EXISTS wcf1_user_group_option;
 CREATE TABLE wcf1_user_group_option (
        optionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
@@ -1354,6 +1370,8 @@ ALTER TABLE wcf1_clipboard_item ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_o
 ALTER TABLE wcf1_clipboard_page ADD FOREIGN KEY (actionID) REFERENCES wcf1_clipboard_action (actionID) ON DELETE CASCADE;
 ALTER TABLE wcf1_clipboard_page ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
 
+ALTER TABLE wcf1_condition ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
+
 ALTER TABLE wcf1_core_object ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
 
 ALTER TABLE wcf1_cronjob ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
@@ -1441,6 +1459,8 @@ ALTER TABLE wcf1_template_listener ADD FOREIGN KEY (packageID) REFERENCES wcf1_p
 ALTER TABLE wcf1_user_collapsible_content ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
 ALTER TABLE wcf1_user_collapsible_content ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
 
+ALTER TABLE wcf1_user_group_assignment ADD FOREIGN KEY (groupID) REFERENCES wcf1_user_group (groupID) ON DELETE CASCADE;
+
 ALTER TABLE wcf1_user_group_option ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
 
 ALTER TABLE wcf1_user_group_option_category ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;