Add special trophies
authorJoshua Rüsweg <josh@bastelstu.be>
Sat, 29 Jul 2017 14:08:24 +0000 (16:08 +0200)
committerJoshua Rüsweg <josh@bastelstu.be>
Sat, 29 Jul 2017 14:08:46 +0000 (16:08 +0200)
See #2315

15 files changed:
com.woltlab.wcf/templates/messageSidebar.tpl
com.woltlab.wcf/templates/settings.tpl
com.woltlab.wcf/templates/user.tpl
com.woltlab.wcf/templates/userProfilePreview.tpl
com.woltlab.wcf/userGroupOption.xml
wcfsetup/install/files/lib/acp/form/TrophyEditForm.class.php
wcfsetup/install/files/lib/data/trophy/TrophyAction.class.php
wcfsetup/install/files/lib/data/user/UserProfile.class.php
wcfsetup/install/files/lib/data/user/UserProfileAction.class.php
wcfsetup/install/files/lib/data/user/trophy/UserTrophyAction.class.php
wcfsetup/install/files/lib/form/SettingsForm.class.php
wcfsetup/install/files/style/ui/trophy.scss
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index 004553e22c9353cee5b93bdb613d86a404343ef1..ded7ae5bafc5af1377385bf91d2132b5228c37ad 100644 (file)
                                        <div class="userRank">{@$userProfile->getRank()->getImage()}</div>
                                {/if}
                        {/if}
+
+                       {if MODULE_TROPHY && $__wcf->session->getPermission('user.profile.trophy.canSeeTrophies') && ($userProfile->isAccessible('canViewTrophies') || $userProfile->userID == $__wcf->session->userID) && $userProfile->getSpecialTrophies()|count}
+                               <div class="specialTrophyContainer">
+                                       <ul>
+                                               {foreach from=$userProfile->getSpecialTrophies() item=trophy}
+                                                       <li><a href="{@$trophy->getLink()}">{@$trophy->renderTrophy(32)}</a></li>
+                                               {/foreach}
+                                       </ul>
+                               </div>
+                       {/if}
                {else}
                        <div class="messageAuthorContainer">
                                {if $userProfile->username}
index c807aaa2928f829c796760f665b5881a954438ba..b8ee04b883ed725b590ebc99c4ad11c25376e5c9 100644 (file)
                                {event name='styleFields'}
                        </section>
                {/if}
+               
+               {if MODULE_TROPHY && $__wcf->getSession()->getPermission('user.profile.trophy.maxUserSpecialTrophies') > 0 && $availableTrophies|count}
+                       <section class="section">
+                               <h2 class="sectionTitle">{lang}wcf.user.trophy.trophies{/lang}</h2>
+                               <dl{if $errorField == 'specialTrophies'} class="formError"{/if}>
+                                       <dt>{lang}wcf.user.trophy.specialTrophies{/lang}</dt>
+                                       <dd>
+                                               <ul class="specialTrophiesUl">
+                                                       {if $__wcf->getSession()->getPermission('user.profile.trophy.maxUserSpecialTrophies') == 1}
+                                                               {foreach from=$availableTrophies item=trophy}
+                                                                       <li><label><input type="radio" name="specialTrophies[]" value="{$trophy->getObjectID()}"{if $trophy->getObjectID()|in_array:$specialTrophies} checked{/if}> {@$trophy->renderTrophy(32)} <span>{$trophy->getTitle()}</span></label></li>
+                                                               {/foreach}
+                                                       {else}
+                                                               {foreach from=$availableTrophies item=trophy}
+                                                                       <li><label><input type="checkbox" name="specialTrophies[]" value="{$trophy->getObjectID()}"{if $trophy->getObjectID()|in_array:$specialTrophies} checked{/if}> {@$trophy->renderTrophy(32)} <span>{$trophy->getTitle()}</span></label></li>
+                                                               {/foreach}
+                                                       {/if}
+                                               </ul>
+                                               {if $errorField == 'specialTrophies'}
+                                                       <small class="innerError">
+                                                               {lang}wcf.user.trophy.specialTrophies.error.{$errorType}{/lang}
+                                                       </small>
+                                               {/if}
+                                               <small>{lang}wcf.user.trophy.specialTrophies.description{/lang}</small>
+                                       </dd>
+                               </dl>
+
+                               {event name='trophyFields'}
+                       </section>
+               {/if}
        {/if}
        
        {if !$optionTree|empty}
index 40b2f50785de0db18ff7d2cde1f9fd69690cc79b..2dac794e66cb8cb2f3bdd02597f19cf3ca909c07 100644 (file)
                        </h1>
                        
                        <div class="contentHeaderDescription">
+                               {if MODULE_TROPHY && $__wcf->session->getPermission('user.profile.trophy.canSeeTrophies') && ($user->isAccessible('canViewTrophies') || $user->userID == $__wcf->session->userID) && $user->getSpecialTrophies()|count}
+                                       <div class="specialTrophyUserContainer">
+                                               <ul>
+                                                       {foreach from=$user->getSpecialTrophies() item=trophy}
+                                                               <li><a href="{@$trophy->getLink()}">{@$trophy->renderTrophy(32)}</a></li>
+                                                       {/foreach}
+                                               </ul>
+                                       </div>
+                               {/if}
                                <ul class="inlineList commaSeparated">
                                        {if !$user->isProtected()}
                                                {if $user->isVisibleOption('gender') && $user->gender}<li>{lang}wcf.user.gender.{if $user->gender == 1}male{else}female{/if}{/lang}</li>{/if}
index 9366018be707e8e5f75e3d78d3262fbd5e1e0192..a115d092218f534ef547349d0e8451321cdd5cbc 100644 (file)
                
                <div class="userInformation">
                        {include file='userInformation'}
+
+                       {if MODULE_TROPHY && $__wcf->session->getPermission('user.profile.trophy.canSeeTrophies') && ($user->isAccessible('canViewTrophies') || $user->userID == $__wcf->session->userID) && $user->getSpecialTrophies()|count}
+                               <div class="specialTrophyUserContainer">
+                                       <ul>
+                                               {foreach from=$user->getSpecialTrophies() item=trophy}
+                                                       <li><a href="{@$trophy->getLink()}">{@$trophy->renderTrophy(32)}</a></li>
+                                               {/foreach}
+                                       </ul>
+                               </div>
+                       {/if}
                        
                        {if $user->canViewOnlineStatus() && $user->getLastActivityTime()}
                                <dl class="plain inlineDataList">
index b67b60dd9ecf8b12c11c2dbdce41ec8e7a02512a..c24c47a51427760ec3a89181e27a4d1e872b6138 100644 (file)
@@ -740,6 +740,15 @@ png</defaultvalue>
                                <defaultvalue>1</defaultvalue>
                                <options>module_trophy</options>
                        </option>
+                       <option name="user.profile.trophy.maxUserSpecialTrophies">
+                               <categoryname>user.profile.trophy</categoryname>
+                               <optiontype>integer</optiontype>
+                               <minvalue>0</minvalue>
+                               <maxvalue>5</maxvalue>
+                               <defaultvalue>3</defaultvalue>
+                               <usersonly>1</usersonly>
+                               <options>module_trophy</options>
+                       </option>
                        <!-- /user.profile -->
                        
                        <option name="user.page.canAddComment">
index 18e36b4bda3461039ef4587cc0ea6f745fc8a430..b6bf4e2277be16293b7a5c490fede39791d09524 100644 (file)
@@ -185,6 +185,12 @@ class TrophyEditForm extends TrophyAddForm {
                        ConditionHandler::getInstance()->deleteConditions(TrophyConditionHandler::CONDITION_DEFINITION_NAME, [$this->trophy->trophyID]);
                }
                
+               // reset special trophies, if trophy is disabled 
+               if ($this->isDisabled) {
+                       WCF::getDB()->prepareStatement("DELETE FROM wcf". WCF_N ."_user_special_trophy WHERE trophyID = ?")->execute([$this->trophyID]);
+                       UserStorageHandler::getInstance()->resetAll('specialTrophies');
+               }
+               
                $this->saved();
                
                // show success message
index 3531ce4534854e53342dacc2bc1c4a1ec4f49a50..be7ab5ed4eda120fb1ce3c0827bd403024bf232f 100644 (file)
@@ -8,6 +8,7 @@ use wcf\system\exception\UserInputException;
 use wcf\system\image\ImageHandler;
 use wcf\system\upload\TrophyImageUploadFileValidationStrategy;
 use wcf\system\upload\UploadFile;
+use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
 
 /**
@@ -18,6 +19,9 @@ use wcf\system\WCF;
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\Data\Trophy
  * @since      3.1
+ *
+ * @method     TrophyEditor[]          getObjects()
+ * @method     TrophyEditor            getSingleObject()
  */
 class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction, IUploadAction {
        /**
@@ -43,6 +47,17 @@ class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction
                return $trophy;
        }
        
+       /**
+        * @inheritDoc
+        */
+       public function delete() {
+               $returnValues = parent::delete();
+               
+               UserStorageHandler::getInstance()->resetAll('specialTrophies');
+               
+               return $returnValues;
+       }
+       
        /**
         * @inheritDoc
         */
@@ -63,9 +78,14 @@ class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction
         */
        public function toggle() {
                foreach ($this->getObjects() as $trophy) {
-                       /** @var TrophyEditor $trophy */
                        $trophy->update(['isDisabled' => $trophy->isDisabled ? 0 : 1]);
+                       
+                       if (!$trophy->isDisabled) {
+                               WCF::getDB()->prepareStatement("DELETE FROM wcf". WCF_N ."_user_special_trophy WHERE trophyID = ?")->execute([$trophy->trophyID]);
+                       }
                }
+               
+               UserStorageHandler::getInstance()->resetAll('specialTrophies');
        }
        
        /**
index 502f78a3824488ed6a5eef5231c3ed79e8dcf309..497ec97487ad176c7be3582c99cf3c5836dfe478 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 namespace wcf\data\user;
+use wcf\data\trophy\Trophy;
+use wcf\data\trophy\TrophyCache;
 use wcf\data\user\avatar\DefaultAvatar;
 use wcf\data\user\avatar\Gravatar;
 use wcf\data\user\avatar\IUserAvatar;
@@ -12,6 +14,7 @@ use wcf\data\DatabaseObjectDecorator;
 use wcf\data\ITitledLinkObject;
 use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
 use wcf\system\cache\runtime\UserProfileRuntimeCache;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\event\EventHandler;
 use wcf\system\user\signature\SignatureCache;
 use wcf\system\user\storage\UserStorageHandler;
@@ -316,6 +319,52 @@ class UserProfile extends DatabaseObjectDecorator implements ITitledLinkObject {
                return $this->currentLocation;
        }
        
+       /**
+        * Returns the special trophies for the user. 
+        *
+        * @return      Trophy[]
+        */
+       public function getSpecialTrophies() {
+               $specialTrophies = UserStorageHandler::getInstance()->getField('specialTrophies', $this->userID);
+               
+               if ($specialTrophies === null) {
+                       // load special trophies for the user 
+                       $specialTrophies = [];
+                       
+                       $statement = WCF::getDB()->prepareStatement("SELECT trophyID FROM wcf".WCF_N."_user_special_trophy WHERE userID = ?");
+                       $statement->execute([$this->userID]); 
+                       
+                       while ($trophyID = $statement->fetchColumn()) {
+                               $specialTrophies[] = $trophyID; 
+                       }
+                       
+                       UserStorageHandler::getInstance()->update($this->userID, 'specialTrophies', serialize($specialTrophies));
+               }
+               else {
+                       $specialTrophies = unserialize($specialTrophies);
+               }
+               
+               // check if the user has the permission to store these number of trophies,
+               // otherwise, delete the last trophies
+               if (count($specialTrophies) > $this->getPermission('user.profile.trophy.maxUserSpecialTrophies')) {
+                       $trophyDeleteIDs = [];
+                       while (count($specialTrophies) > $this->getPermission('user.profile.trophy.maxUserSpecialTrophies')) {
+                               $trophyDeleteIDs[] = array_pop($specialTrophies);
+                       }
+                       
+                       $conditionBuilder = new PreparedStatementConditionBuilder();
+                       $conditionBuilder->add('userID = ?', [$this->userID]);
+                       $conditionBuilder->add('trophyID IN (?)', [$trophyDeleteIDs]);
+                       
+                       // reset some special trophies 
+                       WCF::getDB()->prepareStatement("DELETE FROM wcf".WCF_N."_user_special_trophy ".$conditionBuilder)->execute($conditionBuilder->getParameters());
+                       
+                       UserStorageHandler::getInstance()->update($this->userID, 'specialTrophies', serialize($specialTrophies));
+               }
+               
+               return TrophyCache::getInstance()->getTrophiesByID($specialTrophies); 
+       }
+       
        /**
         * Returns the last activity time.
         * 
index 16b7ae320e0e08e98db91664fc7060bcd18d902d..ee680ab97745aed1104a33352e3e0521afea0e54 100644 (file)
@@ -423,6 +423,36 @@ class UserProfileAction extends UserAction {
                }
        }
        
+       /**
+        * Updates the special trophies.
+        */
+       public function updateSpecialTrophies() {
+               if (empty($this->objects)) {
+                       $this->readObjects();
+               }
+               
+               foreach ($this->getObjects() as $user) {
+                       WCF::getDB()->beginTransaction();
+                       
+                       WCF::getDB()->prepareStatement("DELETE FROM wcf".WCF_N."_user_special_trophy WHERE userID = ?")->execute([$user->userID]);
+                       
+                       if (!empty($this->parameters['trophyIDs'])) {
+                               $statement = WCF::getDB()->prepareStatement("INSERT INTO wcf".WCF_N."_user_special_trophy (userID, trophyID) VALUES (?, ?)");
+                               
+                               foreach ($this->parameters['trophyIDs'] as $trophyID) {
+                                       $statement->execute([
+                                               $user->userID, 
+                                               $trophyID
+                                       ]);
+                               }
+                       }
+                       
+                       WCF::getDB()->commitTransaction();
+                       
+                       UserStorageHandler::getInstance()->reset([$user->userID], 'specialTrophies');
+               }
+       }
+       
        /**
         * Returns the user option handler object.
         * 
index 28f7db6c81097dd62faff8e125a62234d3042bed..2d901014999f5304ee8063ff478a047174c3a6df 100644 (file)
@@ -3,10 +3,12 @@ namespace wcf\data\user\trophy;
 use wcf\data\user\UserAction;
 use wcf\data\AbstractDatabaseObjectAction;
 use wcf\system\cache\runtime\UserProfileRuntimeCache;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\user\notification\object\UserTrophyNotificationObject;
 use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
 
 /**
@@ -17,6 +19,9 @@ use wcf\system\WCF;
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\Data\User\Trophy
  * @since      3.1
+ *
+ * @method     UserTrophyEditor[]              getObjects()
+ * @method     UserTrophyEditor                getSingleObject()
  */
 class UserTrophyAction extends AbstractDatabaseObjectAction {
        /**
@@ -66,12 +71,33 @@ class UserTrophyAction extends AbstractDatabaseObjectAction {
         * @inheritDoc
         */
        public function delete() {
+               $trophyIDs = $userIDs = []; 
+               foreach ($this->getObjects() as $object) {
+                       $trophyIDs[] = $object->trophyID; 
+                       $userIDs[] = $object->userID; 
+               }
+               
                $returnValues = parent::delete();
                
-               $updateUserTrophies = [];
+               // update user special trophies trophies
+               $userTrophies = UserTrophyList::getUserTrophies($userIDs);
+               
+               foreach ($userTrophies as $userID => $trophies) {
+                       $userTrophyIDs = [];
+                       foreach ($trophies as $trophy) {
+                               $userTrophyIDs[] = $trophy->trophyID;
+                       }
+                       
+                       $conditionBuilder = new PreparedStatementConditionBuilder(); 
+                       $conditionBuilder->add('trophyID NOT IN (?)', [array_unique($userTrophyIDs)]); 
+                       $conditionBuilder->add('userID = ?', [$userID]);
+                       WCF::getDB()->prepareStatement("DELETE FROM wcf". WCF_N ."_user_special_trophy ". $conditionBuilder)->execute($conditionBuilder->getParameters());
+                       
+                       UserStorageHandler::getInstance()->reset([$userID], 'specialTrophies');
+               }
                
                /** @var $object UserTrophyEditor */
-               foreach ($this->objects as $object) {
+               foreach ($this->getObjects() as $object) {
                        if (!isset($updateUserTrophies[$object->userID])) $updateUserTrophies[$object->userID] = 0; 
                        $updateUserTrophies[$object->userID]--;
                }
index 4f897bf08d0c287ad4f72d0199551c84ecff5fcf..b3d4a6e2a454ae789d69e40c15fdfb8b3310efa4 100644 (file)
@@ -2,8 +2,13 @@
 namespace wcf\form;
 use wcf\data\language\Language;
 use wcf\data\style\Style;
+use wcf\data\trophy\Trophy;
+use wcf\data\trophy\TrophyCache;
 use wcf\data\user\option\category\UserOptionCategory;
+use wcf\data\user\trophy\UserTrophyList;
 use wcf\data\user\UserAction;
+use wcf\data\user\UserProfile;
+use wcf\data\user\UserProfileAction;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\UserInputException;
 use wcf\system\language\LanguageFactory;
@@ -63,6 +68,12 @@ class SettingsForm extends AbstractForm {
         */
        public $availableStyles = [];
        
+       /**
+        * list of available trophies
+        * @var Trophy[]
+        */
+       public $availableTrophies = [];
+       
        /**
         * list of content language ids
         * @var integer[]
@@ -81,6 +92,12 @@ class SettingsForm extends AbstractForm {
         */
        public $styleID = 0;
        
+       /**
+        * special trophies
+        * @var integer[]
+        */
+       public $specialTrophies = [];
+       
        /**
         * @inheritDoc
         */
@@ -103,6 +120,13 @@ class SettingsForm extends AbstractForm {
                        $this->availableContentLanguages = LanguageFactory::getInstance()->getContentLanguages();
                        $this->availableLanguages = LanguageFactory::getInstance()->getLanguages();
                        $this->availableStyles = StyleHandler::getInstance()->getAvailableStyles();
+                       
+                       // read available trophies
+                       $trophyIDs = array_unique(array_map(function ($userTrophy) {
+                               return $userTrophy->trophyID;
+                       }, UserTrophyList::getUserTrophies([WCF::getUser()->userID])[WCF::getUser()->userID]));
+                       
+                       $this->availableTrophies = TrophyCache::getInstance()->getTrophiesByID($trophyIDs);
                }
        }
        
@@ -119,6 +143,7 @@ class SettingsForm extends AbstractForm {
                        if (isset($_POST['contentLanguageIDs']) && is_array($_POST['contentLanguageIDs'])) $this->contentLanguageIDs = ArrayUtil::toIntegerArray($_POST['contentLanguageIDs']);
                        if (isset($_POST['languageID'])) $this->languageID = intval($_POST['languageID']);
                        if (isset($_POST['styleID'])) $this->styleID = intval($_POST['styleID']);
+                       if (isset($_POST['specialTrophies'])) $this->specialTrophies = ArrayUtil::toIntegerArray($_POST['specialTrophies']);
                }
        }
        
@@ -157,6 +182,19 @@ class SettingsForm extends AbstractForm {
                        if (!isset($this->availableStyles[$this->styleID])) {
                                $this->styleID = 0;
                        }
+                       
+                       // validate special trophies
+                       if (count($this->specialTrophies) > WCF::getSession()->getPermission('user.profile.trophy.maxUserSpecialTrophies')) {
+                               throw new UserInputException('specialTrophies', 'tooMuch');
+                       }
+                       
+                       foreach ($this->specialTrophies as $trophyID) {
+                               if (!in_array($trophyID, array_map(function ($trophy) {
+                                       return $trophy->trophyID; 
+                               }, $this->availableTrophies))) {
+                                       throw new UserInputException('specialTrophies' , 'invalid');
+                               }
+                       }
                }
        }
        
@@ -175,6 +213,10 @@ class SettingsForm extends AbstractForm {
                                        $this->languageID = WCF::getUser()->languageID;
                                }
                                $this->styleID = WCF::getUser()->styleID;
+                               
+                               $this->specialTrophies = array_unique(array_map(function ($trophy) {
+                                       return $trophy->trophyID;
+                               }, (new UserProfile(WCF::getUser()))->getSpecialTrophies()));
                        }
                }
        }
@@ -203,6 +245,11 @@ class SettingsForm extends AbstractForm {
                if ($this->category == 'general') {
                        // reset user language ids cache
                        UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'languageIDs');
+                       
+                       $userProfileAction = new UserProfileAction([WCF::getUser()->userID], 'updateSpecialTrophies', [
+                               'trophyIDs' => $this->specialTrophies
+                       ]);
+                       $userProfileAction->executeAction();
                }
                $this->saved();
                
@@ -225,9 +272,11 @@ class SettingsForm extends AbstractForm {
                                'availableContentLanguages' => $this->availableContentLanguages,
                                'availableLanguages' => $this->availableLanguages,
                                'availableStyles' => $this->availableStyles,
+                               'availableTrophies' => $this->availableTrophies,
                                'contentLanguageIDs' => $this->contentLanguageIDs,
                                'languageID' => $this->languageID,
-                               'styleID' => $this->styleID
+                               'styleID' => $this->styleID,
+                               'specialTrophies' => $this->specialTrophies
                        ]);
                }
        }
index 6458ff70451d5dcedc186f669e53071fefd5ef08..7f4f382e0252fc6ec723dbf1c8bbac4efbbc154f 100644 (file)
                font-size: 81px;
        }
 }
+
+.specialTrophiesUl {
+       display: flex;
+       flex-wrap: wrap;
+       
+       > li {
+               width: 300px; 
+               padding-bottom: 5px;
+               
+               > label {
+                       display: flex; 
+                       align-items: center;
+                       
+                       > span:last-child {
+                               margin-left: 5px;
+                       }
+               }
+       }
+}
+
+.specialTrophyContainer > ul {
+       display: flex;
+       margin-top: 5px;
+       justify-content: center;
+       
+       > li:not(:last-child) {
+               margin-right: 5px;
+       }
+}
+
+
+@include screen-sm-down {
+       .specialTrophyContainer {
+               display: none;
+       }
+}
+
+.specialTrophyUserContainer > ul {
+       display: flex;
+       margin-top: -15px;
+       margin-bottom: 5px;
+       
+       > li:not(:last-child) {
+               margin-right: 5px;
+       }
+}
+
+
+.userProfilePreview .specialTrophyUserContainer > ul {
+       margin-top: 5px;
+       margin-bottom: 5px;
+}
index a89327bd4f5186552001c0bb5ef738cb18290244..b8b727c227a91844be58e1728a454238c590bcfc 100644 (file)
                <item name="wcf.acp.group.option.admin.trophy.canAwardTrophy"><![CDATA[Kann Trophäen verleihen]]></item>
                <item name="wcf.acp.group.option.category.user.profile.trophy"><![CDATA[Trophäen]]></item>
                <item name="wcf.acp.group.option.user.profile.trophy.canSeeTrophies"><![CDATA[Kann Trophäen sehen]]></item>
+               <item name="wcf.acp.group.option.user.profile.trophy.maxUserSpecialTrophies"><![CDATA[Maximale Anzahl an besonderen Trophäen]]></item> <!-- @TODO: find a better name? -->
+               <item name="wcf.acp.group.option.user.profile.trophy.maxUserSpecialTrophies.description"><![CDATA[Besondere Trophäen können vom Benutzer individuell ausgewählt werden und werden in der Message-Sidebar und im Benutzerprofil angezeigt.]]></item>
        </category>
        
        <category name="wcf.acp.index">
@@ -3663,6 +3665,11 @@ Die E-Mail-Adresse des neuen Benutzers lautet: {@$user->email}
                <item name="wcf.user.trophy.showTrophies"><![CDATA[Trophäen von {$user->username} anzeigen]]></item>
                <item name="wcf.user.trophy.noTrophies"><![CDATA[Der Benutzer hat noch keine Trophäen]]></item>
                <item name="wcf.user.trophy.dialogTitle"><![CDATA[Trophäen von {$username}]]></item>
+               <item name="wcf.user.trophy.trophies"><![CDATA[Trophäen]]></item>
+               <item name="wcf.user.trophy.specialTrophies"><![CDATA[Besondere Trophäen]]></item>
+               <item name="wcf.user.trophy.specialTrophies.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Wähle{else}Wählen Sie{/if} hier ihre besonderen Trophäen aus, welche im Profil und in der Nachrichten-Seitenleiste angezeigt werden.]]></item>
+               <item name="wcf.user.trophy.specialTrophies.error.tooMuch"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst{else}Sie können{/if} maximal {#$__wcf->session->getPermission('user.profile.trophy.maxUserSpecialTrophies')} Trophäen auswählen.]]></item>
+               <item name="wcf.user.trophy.specialTrophies.error.invalid"><![CDATA[Die angegebenen Trophäen sind invalid.]]></item>
        </category>
        
        <category name="wcf.acp.trophy">
index d076dd5a6c68dc75b9d26b8afd25e992486060ad..bfc9913dbee9a08a5852e790c6b963cb9e1f56c6 100644 (file)
                <item name="wcf.acp.group.option.admin.trophy.canAwardTrophy"><![CDATA[Can award trophies]]></item>
                <item name="wcf.acp.group.option.category.user.profile.trophy"><![CDATA[Trophies]]></item>
                <item name="wcf.acp.group.option.user.profile.trophy.canSeeTrophies"><![CDATA[Can see trophies]]></item>
+               <item name="wcf.acp.group.option.user.profile.trophy.maxUserSpecialTrophies"><![CDATA[Maximum number of special trophies]]></item> <!-- @TODO: find a better name? -->
+               <item name="wcf.acp.group.option.user.profile.trophy.maxUserSpecialTrophies.description"><![CDATA[Special trophies can be individually selected by the user and displayed in the message sidebar and the user profile.]]></item>
        </category>
        
        <category name="wcf.acp.index">
@@ -3654,6 +3656,11 @@ Open the link below to access the user profile:
                <item name="wcf.user.trophy.showTrophies"><![CDATA[Display Trophies of {$user->username}]]></item>
                <item name="wcf.user.trophy.noTrophies"><![CDATA[The user has no trophies]]></item>
                <item name="wcf.user.trophy.dialogTitle"><![CDATA[Trophies of {$username}]]></item>
+               <item name="wcf.user.trophy.trophies"><![CDATA[Trophies]]></item>
+               <item name="wcf.user.trophy.specialTrophies"><![CDATA[Special Trophies]]></item>
+               <item name="wcf.user.trophy.specialTrophies.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Wähle{else}Wählen Sie{/if} hier ihre besonderen Trophäen aus, welche im Profil und in der Nachrichten-Seitenleiste angezeigt werden.]]></item>
+               <item name="wcf.user.trophy.specialTrophies.error.tooMuch"><![CDATA[You can choose a maximum of {#$__wcf->session->getPermission('user.profile.trophy.maxUserSpecialTrophies')} trophies.]]></item>
+               <item name="wcf.user.trophy.specialTrophies.error.invalid"><![CDATA[The selected trophies are invalid.]]></item>
        </category>
        
        <category name="wcf.acp.trophy">
index 89d305fff5bcbfd19374f5484ab7cb3218df19d8..33485d48b5e1eeb3871a1e7992a89b6c22ec6674 100644 (file)
@@ -1545,6 +1545,13 @@ CREATE TABLE wcf1_user_ignore (
        UNIQUE KEY (userID, ignoreUserID)
 );
 
+DROP TABLE IF EXISTS wcf1_user_special_trophy;
+CREATE TABLE wcf1_user_special_trophy(
+       trophyID INT(10) NOT NULL,
+       userID INT(10) NOT NULL,
+       UNIQUE KEY (trophyID, userID)
+);
+
 DROP TABLE IF EXISTS wcf1_user_trophy;
 CREATE TABLE wcf1_user_trophy(
        userTrophyID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
@@ -2048,6 +2055,9 @@ ALTER TABLE wcf1_user_profile_visitor ADD FOREIGN KEY (userID) REFERENCES wcf1_u
 ALTER TABLE wcf1_user_object_watch ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
 ALTER TABLE wcf1_user_object_watch ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
 
+ALTER TABLE wcf1_user_special_trophy ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_special_trophy ADD FOREIGN KEY (trophyID) REFERENCES wcf1_trophy (trophyID) ON DELETE CASCADE;
+
 ALTER TABLE wcf1_message_embedded_object ADD FOREIGN KEY (messageObjectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
 ALTER TABLE wcf1_message_embedded_object ADD FOREIGN KEY (embeddedObjectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;