Add showOrder function for trophies
authorJoshua Rüsweg <josh@bastelstu.be>
Mon, 6 Aug 2018 09:30:11 +0000 (11:30 +0200)
committerJoshua Rüsweg <josh@bastelstu.be>
Mon, 6 Aug 2018 09:30:27 +0000 (11:30 +0200)
See #2641

15 files changed:
wcfsetup/install/files/acp/templates/trophyAdd.tpl
wcfsetup/install/files/acp/templates/trophyList.tpl
wcfsetup/install/files/lib/acp/form/TrophyAddForm.class.php
wcfsetup/install/files/lib/acp/form/TrophyEditForm.class.php
wcfsetup/install/files/lib/acp/page/TrophyListPage.class.php
wcfsetup/install/files/lib/data/trophy/Trophy.class.php
wcfsetup/install/files/lib/data/trophy/TrophyAction.class.php
wcfsetup/install/files/lib/data/trophy/TrophyEditor.class.php
wcfsetup/install/files/lib/data/user/UserProfile.class.php
wcfsetup/install/files/lib/form/SettingsForm.class.php
wcfsetup/install/files/lib/page/TrophyListPage.class.php
wcfsetup/install/files/lib/system/cache/builder/TrophyCacheBuilder.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index 4279ced2bb5a894e0651307c383db0bb7a955436..8908e038079e8d2838d2205deef72231df9ae07c 100644 (file)
                                </dd>
                        </dl>
                        
+                       <dl>
+                               <dt><label for="showOrder">{lang}wcf.global.showOrder{/lang}</label></dt>
+                               <dd>
+                                       <input type="number" id="showOrder" name="showOrder" value="{$showOrder}" class="tiny" min="0">
+                                       <small>{lang}wcf.acp.trophy.showOrder.description{/lang}</small>
+                               </dd>
+                       </dl>
+                       
                        <dl>
                                <dt></dt>
                                <dd>
index 6e7b5b751ed21aedd8feef3c96efa827c4077a4e..0aa506c31558cbc7a2207ef168470264ae09a1cf 100644 (file)
@@ -1,12 +1,18 @@
 {include file='header' pageTitle='wcf.acp.menu.link.trophy.list'}
 
 <script data-relocate="true">
-       //<![CDATA[
+       require(['WoltLabSuite/Core/Ui/Sortable/List'], function (UiSortableList) {
+               new UiSortableList({
+                       containerId: 'trophyList',
+                       className: 'wcf\\data\\trophy\\TrophyAction',
+                       offset: {@$startIndex}
+               });
+       });
+       
        $(function() {
                new WCF.Action.Delete('wcf\\data\\trophy\\TrophyAction', '.trophyRow');
                new WCF.Action.Toggle('wcf\\data\\trophy\\TrophyAction', '.trophyRow');
        });
-       //]]>
 </script>
 
 <header class="contentHeader">
 {hascontent}
        <div class="paginationTop">
                {content}
-                       {pages print=true assign=pagesLinks controller='TrophyList' link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+                       {pages print=true assign=pagesLinks controller='TrophyList' link="pageNo=%d"}
                {/content}
        </div>
 {/hascontent}
 
 {if $objects|count}
-       <div class="section tabularBox">
-
-               <table class="table">
-                       <thead>
-                       <tr>
-                               <th class="columnID columnTrophyID{if $sortField == 'trophyID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='TrophyList'}pageNo={@$pageNo}&sortField=trophyID&sortOrder={if $sortField == 'trophyID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
-                               <th class="columnTitle{if $sortField == 'title'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='TrophyList'}pageNo={@$pageNo}&sortField=title&sortOrder={if $sortField == 'title' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.title{/lang}</a></th>
-                               <th class="columnText columnCategory{if $sortField == 'categoryID'} active {@$sortOrder}{/if}"><a href="{link controller='TrophyList'}pageNo={@$pageNo}&sortField=categoryID&sortOrder={if $sortField == 'categoryID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.trophy.category{/lang}</a></th>
-
-                               {event name='columnHeads'}
-                       </tr>
-                       </thead>
-
-                       <tbody>
-                       {foreach from=$objects item=trophy}
-                               <tr class="trophyRow">
-                                       <td class="columnIcon">
-                                               <span class="icon icon16 fa-{if !$trophy->isDisabled}check-{/if}square-o jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if !$trophy->isDisabled}disable{else}enable{/if}{/lang}" data-object-id="{@$trophy->getObjectID()}"></span>
-                                               <a href="{link controller='TrophyEdit' id=$trophy->getObjectID()}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 fa-pencil"></span></a>
-                                               <span class="icon icon16 fa-times pointer jsDeleteButton jsTooltip" data-confirm-message-html="{lang __encode=true}wcf.acp.trophy.delete.confirmMessage{/lang}" data-object-id="{@$trophy->getObjectID()}" title="{lang}wcf.global.button.delete{/lang}"></span>
-                                       </td>
-                                       <td class="columnID columnTrophyID">{@$trophy->trophyID}</td>
-                                       <td class="columnIcon">{@$trophy->renderTrophy(32)}</td>
-                                       <td class="columnTitle columnTrophyTitle"><a href="{link controller='TrophyEdit' id=$trophy->getObjectID()}{/link}" title="{lang}wcf.global.button.edit{/lang}">{$trophy->getTitle()}</a></td>
-                                       <td class="columnText columnCategory">{$trophy->getCategory()->getTitle()}</td>
-                                       
-                                       {event name='columns'}
-                               </tr>
+       <div class="section sortableListContainer" id="trophyList">
+               <ol class="sortableList" data-object-id="0" start="{@($pageNo - 1) * $itemsPerPage + 1}">
+                       {foreach from=$objects item='trophy'}
+                               <li class="sortableNode sortableNoNesting trophyRow" data-object-id="{@$trophy->trophyID}">
+                                       <span class="sortableNodeLabel">
+                                               <a href="{link controller='TrophyEdit' object=$trophy}{/link}">{$trophy->getTitle()}</a>
+                                               
+                                               <span class="statusDisplay sortableButtonContainer">
+                                                       <span class="icon icon16 fa-arrows sortableNodeHandle"></span>
+                                                       <span class="icon icon16 fa-{if !$trophy->isDisabled}check-{/if}square-o jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if $trophy->isDisabled}enable{else}disable{/if}{/lang}" data-object-id="{@$trophy->trophyID}"></span>
+                                                       <a href="{link controller='TrophyEdit' object=$trophy}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 fa-pencil"></span></a>
+                                                       <span class="icon icon16 fa-times pointer jsDeleteButton jsTooltip" data-confirm-message-html="{lang __encode=true}wcf.acp.trophy.delete.confirmMessage{/lang}" data-object-id="{@$trophy->getObjectID()}" title="{lang}wcf.global.button.delete{/lang}"></span>
+                                                       
+                                                       {event name='itemButtons'}
+                                               </span>
+                                       </span>
+                               </li>
                        {/foreach}
-                       </tbody>
-               </table>
-
+               </ol>
+               
+               <div class="formSubmit">
+                       <button class="button" data-type="submit">{lang}wcf.global.button.saveSorting{/lang}</button>
+               </div>
        </div>
        
        <footer class="contentFooter">
index 3fd9ff7753e832f779c38f965da3ad4c7395d246..04c1a46ee4741730c5957d7e4d236ee123ae0380 100644 (file)
@@ -131,6 +131,12 @@ class TrophyAddForm extends AbstractAcpForm {
         */
        public $conditions = [];
        
+       /**
+        * the showOrder value of the trophy
+        * @var int
+        */
+       public $showOrder = 0;
+       
        /**
         * @inheritDoc
         */
@@ -178,6 +184,7 @@ class TrophyAddForm extends AbstractAcpForm {
                if (isset($_POST['badgeColor'])) $this->badgeColor = $_POST['badgeColor'];
                if (isset($_POST['awardAutomatically'])) $this->awardAutomatically = 1;
                if (isset($_POST['trophyUseHtml'])) $this->trophyUseHtml = 1;
+               if (isset($_POST['showOrder'])) $this->showOrder = intval($_POST['showOrder']);
                
                // read file upload 
                $fileExtension = WCF::getSession()->getVar('trophyImage-'.$this->tmpHash);
@@ -288,7 +295,8 @@ class TrophyAddForm extends AbstractAcpForm {
                                'type' => $this->type,
                                'isDisabled' => $this->isDisabled,
                                'awardAutomatically' => $this->awardAutomatically,
-                               'trophyUseHtml' => $this->trophyUseHtml
+                               'trophyUseHtml' => $this->trophyUseHtml,
+                               'showOrder' => $this->showOrder
                        ]),
                        'tmpHash' => $this->tmpHash
                ]);
@@ -320,7 +328,7 @@ class TrophyAddForm extends AbstractAcpForm {
        public function reset() {
                parent::reset();
                
-               $this->isDisabled = $this->awardAutomatically = $this->categoryID = $this->trophyUseHtml = 0;
+               $this->isDisabled = $this->awardAutomatically = $this->categoryID = $this->trophyUseHtml = $this->showOrder = 0;
                $this->type = Trophy::TYPE_BADGE;
                $this->iconName = $this->uploadedImageURL = '';
                $this->iconColor = 'rgba(255, 255, 255, 1)';
@@ -354,7 +362,8 @@ class TrophyAddForm extends AbstractAcpForm {
                        'availableTypes' => $this->availableTypes, 
                        'tmpHash' => $this->tmpHash,
                        'uploadedImageURL' => $this->uploadedImageURL,
-                       'trophyUseHtml' => $this->trophyUseHtml
+                       'trophyUseHtml' => $this->trophyUseHtml,
+                       'showOrder' => $this->showOrder
                ]);
        }
 }
index e562bf69acf4e4aebc82d41473300f67b6425359..69bb1d0e6e292954c3d704c6dbd56b8cef7ea948 100644 (file)
@@ -75,6 +75,7 @@ class TrophyEditForm extends TrophyAddForm {
                        $this->badgeColor = $this->trophy->badgeColor;
                        $this->awardAutomatically = $this->trophy->awardAutomatically;
                        $this->trophyUseHtml = $this->trophy->trophyUseHtml;
+                       $this->showOrder = $this->trophy->showOrder;
                        
                        // reset badge values for non badge trophies
                        if ($this->trophy->type != Trophy::TYPE_BADGE) {
@@ -166,7 +167,8 @@ class TrophyEditForm extends TrophyAddForm {
                        'type' => $this->type,
                        'isDisabled' => $this->isDisabled,
                        'awardAutomatically' => $this->awardAutomatically,
-                       'trophyUseHtml' => $this->trophyUseHtml
+                       'trophyUseHtml' => $this->trophyUseHtml,
+                       'showOrder' => $this->showOrder
                ])]);
                $this->objectAction->executeAction();
                
index 17bc43e8c83a2ce17f64025074017c8d101be008..5d21c0539cc353f3285c5057555028993370075f 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 namespace wcf\acp\page;
 use wcf\data\trophy\TrophyList;
-use wcf\page\SortablePage;
+use wcf\page\MultipleLinkPage;
 
 /**
  * Trophy list page.
@@ -12,7 +12,7 @@ use wcf\page\SortablePage;
  * @package    WoltLabSuite\Core\Acp\Page
  * @since      3.1
  */
-class TrophyListPage extends SortablePage {
+class TrophyListPage extends MultipleLinkPage {
        /**
         * @inheritDoc
         */
@@ -31,20 +31,15 @@ class TrophyListPage extends SortablePage {
        /**
         * @inheritDoc
         */
-       public $defaultSortField = 'trophyID';
+       public $sortField = 'trophy.showOrder';
        
        /**
         * @inheritDoc
         */
-       public $defaultSortOrder = 'DESC';
+       public $sortOrder = 'ASC';
        
        /**
         * @inheritDoc
         */
        public $objectListClassName = TrophyList::class;
-       
-       /**
-        * @inheritDoc
-        */
-       public $validSortFields = ['trophyID', 'title', 'categoryID'];
 }
index 3287b3f53f9c90ecc992a1682c1431631a25d766..5d5310fe6b41153bd097cf641ba16bce6f6f3321 100644 (file)
@@ -33,6 +33,7 @@ use wcf\util\StringUtil;
  * @property-read      integer         $isDisabled                     `1` if the trophy is disabled
  * @property-read      integer         $awardAutomatically             `1` if the trophy is awarded automatically
  * @property-read      integer         $trophyUseHtml                  `1`, if the trophy use a html description
+ * @property-read      integer         $showOrder                      position of the trophy in relation to the other trophies at the same location
  */
 class Trophy extends DatabaseObject implements ITitledLinkObject, IRouteController {
        /**
index d14135fc99e2d8502b16acb7a0d5d70b8371995b..9921ade6544f9b1d195bc416372a78132349a88a 100644 (file)
@@ -4,6 +4,7 @@ use wcf\data\user\trophy\UserTrophyAction;
 use wcf\data\user\trophy\UserTrophyList;
 use wcf\data\user\UserAction;
 use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\ISortableAction;
 use wcf\data\IToggleAction;
 use wcf\data\IUploadAction;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
@@ -27,12 +28,17 @@ use wcf\system\WCF;
  * @method     TrophyEditor[]          getObjects()
  * @method     TrophyEditor            getSingleObject()
  */
-class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction, IUploadAction {
+class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction, IUploadAction, ISortableAction {
        /**
         * @inheritDoc
         */
        protected $permissionsDelete = ['admin.trophy.canManageTrophy'];
        
+       /**
+        * @inheritDoc
+        */
+       protected $permissionsUpdate = ['admin.trophy.canManageTrophy'];
+       
        /**
         * @inheritDoc
         */
@@ -43,6 +49,12 @@ class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction
         * @return      Trophy
         */
        public function create() {
+               $showOrder = 0;
+               if (isset($this->parameters['data']['showOrder'])) {
+                       $showOrder = $this->parameters['data']['showOrder'];
+                       unset($this->parameters['data']['showOrder']);
+               }
+               
                /** @var Trophy $trophy */
                $trophy = parent::create();
                
@@ -50,7 +62,10 @@ class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction
                        $this->updateTrophyImage($trophy);
                }
                
-               return $trophy;
+               $trophyEditor = new TrophyEditor($trophy);
+               $trophyEditor->setShowOrder($showOrder);
+               
+               return new Trophy($trophy->trophyID);
        }
        
        /**
@@ -91,6 +106,10 @@ class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction
                                }
                        }
                }
+               
+               if (count($this->objects) == 1 && isset($this->parameters['data']['showOrder']) && $this->parameters['data']['showOrder'] != reset($this->objects)->showOrder) {
+                       reset($this->objects)->setShowOrder($this->parameters['data']['showOrder']);
+               }
        }
        
        /**
@@ -280,4 +299,43 @@ class TrophyAction extends AbstractDatabaseObjectAction implements IToggleAction
                        }
                }
        }
+       
+       /**
+        * @inheritDoc
+        */
+       public function validateUpdatePosition() {
+               WCF::getSession()->checkPermissions($this->permissionsUpdate);
+               
+               if (!isset($this->parameters['data']['structure']) || !is_array($this->parameters['data']['structure'])) {
+                       throw new UserInputException('structure');
+               }
+               
+               $adList = new TrophyList();
+               $adList->setObjectIDs($this->parameters['data']['structure'][0]);
+               if ($adList->countObjects() != count($this->parameters['data']['structure'][0])) {
+                       throw new UserInputException('structure');
+               }
+               
+               $this->readInteger('offset', true, 'data');
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function updatePosition() {
+               $sql = "UPDATE  wcf".WCF_N."_trophy
+                       SET     showOrder = ?
+                       WHERE   trophyID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               
+               $showOrder = $this->parameters['data']['offset'];
+               WCF::getDB()->beginTransaction();
+               foreach ($this->parameters['data']['structure'][0] as $trophyID) {
+                       $statement->execute([
+                               $showOrder++,
+                               $trophyID
+                       ]);
+               }
+               WCF::getDB()->commitTransaction();
+       }
 }
index 5a4857a3c33a3ba06e1dab8ab66f6f9bb8144eed..e8aff9a7ba90d8102e3c22702dd29ab84604e5ff 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\data\trophy;
 use wcf\data\DatabaseObjectEditor;
 use wcf\data\IEditableCachedObject;
+use wcf\system\WCF;
 
 /**
  * A trophy editor.
@@ -20,6 +21,40 @@ class TrophyEditor extends DatabaseObjectEditor implements IEditableCachedObject
         */
        protected static $baseClass = Trophy::class;
        
+       /**
+        * Sets the show order of the trophy.
+        *
+        * @param       integer         $showOrder
+        */
+       public function setShowOrder($showOrder = 0) {
+               $sql = "SELECT  MAX(showOrder)
+                       FROM    wcf".WCF_N."_trophy";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute();
+               $maxShowOrder = $statement->fetchSingleColumn();
+               if (!$maxShowOrder) $maxShowOrder = 0;
+               
+               if (!$showOrder || $showOrder > $maxShowOrder) {
+                       $newShowOrder = $maxShowOrder + 1;
+               }
+               else {
+                       // shift other trophies
+                       $sql = "UPDATE  wcf".WCF_N."_trophy
+                               SET     showOrder = showOrder + 1
+                               WHERE   showOrder >= ?";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([
+                               $showOrder
+                       ]);
+                       
+                       $newShowOrder = $showOrder;
+               }
+               
+               $this->update([
+                       'showOrder' => $newShowOrder
+               ]);
+       }
+       
        /**
         * @inheritDoc
         */
index e1ccf77a014509c114ec73a366526a20295d145e..b08c1c84de3ab9e6b48ddde9297f4459054f0d29 100644 (file)
@@ -465,8 +465,10 @@ class UserProfile extends DatabaseObjectDecorator implements ITitledLinkObject {
                        
                        UserStorageHandler::getInstance()->update($this->userID, 'specialTrophies', serialize($specialTrophies));
                }
+               $trophies = TrophyCache::getInstance()->getTrophiesByID($specialTrophies);
+               Trophy::sort($trophies, 'showOrder'); 
                
-               return TrophyCache::getInstance()->getTrophiesByID($specialTrophies); 
+               return $trophies;
        }
        
        /**
index 0a16559b9caebfa9470c28a9c887deba066760e8..16e9c0039673610cc94ec83ff094ebf287f8eb57 100644 (file)
@@ -127,6 +127,8 @@ class SettingsForm extends AbstractForm {
                        }, UserTrophyList::getUserTrophies([WCF::getUser()->userID])[WCF::getUser()->userID]));
                        
                        $this->availableTrophies = TrophyCache::getInstance()->getTrophiesByID($trophyIDs);
+                       
+                       Trophy::sort($this->availableTrophies, 'showOrder');
                }
        }
        
index 0c86378a4da58d2fc9066f8fd453641281f3c171..8a900128216271c96fef5bba2ad473fbd0feffb4 100644 (file)
@@ -42,7 +42,7 @@ class TrophyListPage extends MultipleLinkPage {
         * selected sort field
         * @var string
         */
-       public $sortField = 'trophyID';
+       public $sortField = 'trophy.showOrder';
        
        /**
         * selected sort order
index db543fcf299560b068914d606f19ef825d46874d..d81741cacde1218888c69c3a0ab8c2d012a68f9d 100644 (file)
@@ -22,6 +22,7 @@ class TrophyCacheBuilder extends AbstractCacheBuilder {
                        $trophyList->getConditionBuilder()->add('isDisabled = ?', [0]);
                }
                
+               $trophyList->sqlOrderBy = 'trophy.showOrder ASC';
                $trophyList->readObjects();
                return $trophyList->getObjects();
        }
index 5d6b334291cda565f25fde074388b3df070d53cc..cd0854353e3c949db1dde2bc9cd94330dd3d83b8 100644 (file)
@@ -3881,6 +3881,7 @@ Die E-Mail-Adresse des neuen Benutzers lautet: {@$user->email}
                <item name="wcf.acp.trophy.error.noCategories"><![CDATA[Bevor {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} eine Trophäe hinzufügen {if LANGUAGE_USE_INFORMAL_VARIANT}kannst, musst du{else}können, müssen Sie{/if} eine <a href="{link controller='TrophyCategoryAdd'}{/link}">Kategorie hinzufügen</a>.]]></item>
                <item name="wcf.acp.trophy.error.noSuitableTrophies"><![CDATA[Bevor {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} eine Trophäe vergeben {if LANGUAGE_USE_INFORMAL_VARIANT}kannst, musst du{else}können, müssen Sie{/if} eine <a href="{link controller='TrophyAdd'}{/link}">Trophäe hinzufügen</a>, welche nicht automatisch durch das System vergeben wird.]]></item>
                <item name="wcf.acp.trophy.trophyUseHtml"><![CDATA[HTML in der Beschreibung verwenden]]></item>
+               <item name="wcf.acp.trophy.showOrder.description"><![CDATA[Legt die Reihenfolge fest, in der die Trophäen angezeigt werden.]]></item>
        </category>
        
        <category name="wcf.user.usersOnline">
index c08f27a999ca94ca6d8ece6e72c971ce98b27d00..a850c5b920282894188202572d748744a03f6901 100644 (file)
@@ -3874,6 +3874,7 @@ Open the link below to access the user profile:
                <item name="wcf.acp.trophy.error.noCategories"><![CDATA[Please <a href="{link controller='TrophyCategoryAdd'}{/link}">add a category</a> before creating trophies.]]></item>
                <item name="wcf.acp.trophy.error.noSuitableTrophies"><![CDATA[Please <a href="{link controller='TrophyAdd'}{/link}">add a trophy</a> that is not automatically awarded before you award trophies.]]></item>
                <item name="wcf.acp.trophy.trophyUseHtml"><![CDATA[Enable HTML code in the description]]></item>
+               <item name="wcf.acp.trophy.showOrder.description"><![CDATA[Choose display order of trophies.]]></item>
        </category>
        
        <category name="wcf.user.usersOnline">
index 44ed4edd89830d88ee1c8ada93353b7137408870..572e29fb2644a3f3bbbf84672628f382b7d4f4ab 100644 (file)
@@ -1383,6 +1383,7 @@ CREATE TABLE wcf1_trophy(
        isDisabled TINYINT(1) NOT NULL DEFAULT 0,
        awardAutomatically TINYINT(1) NOT NULL DEFAULT 0,
        trophyUseHtml TINYINT(1) NOT NULL DEFAULT 0,
+       showOrder INT(10) NOT NULL DEFAULT 0,
        KEY(categoryID)
 );