</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>
{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">
*/
public $conditions = [];
+ /**
+ * the showOrder value of the trophy
+ * @var int
+ */
+ public $showOrder = 0;
+
/**
* @inheritDoc
*/
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);
'type' => $this->type,
'isDisabled' => $this->isDisabled,
'awardAutomatically' => $this->awardAutomatically,
- 'trophyUseHtml' => $this->trophyUseHtml
+ 'trophyUseHtml' => $this->trophyUseHtml,
+ 'showOrder' => $this->showOrder
]),
'tmpHash' => $this->tmpHash
]);
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)';
'availableTypes' => $this->availableTypes,
'tmpHash' => $this->tmpHash,
'uploadedImageURL' => $this->uploadedImageURL,
- 'trophyUseHtml' => $this->trophyUseHtml
+ 'trophyUseHtml' => $this->trophyUseHtml,
+ 'showOrder' => $this->showOrder
]);
}
}
$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) {
'type' => $this->type,
'isDisabled' => $this->isDisabled,
'awardAutomatically' => $this->awardAutomatically,
- 'trophyUseHtml' => $this->trophyUseHtml
+ 'trophyUseHtml' => $this->trophyUseHtml,
+ 'showOrder' => $this->showOrder
])]);
$this->objectAction->executeAction();
<?php
namespace wcf\acp\page;
use wcf\data\trophy\TrophyList;
-use wcf\page\SortablePage;
+use wcf\page\MultipleLinkPage;
/**
* Trophy list page.
* @package WoltLabSuite\Core\Acp\Page
* @since 3.1
*/
-class TrophyListPage extends SortablePage {
+class TrophyListPage extends MultipleLinkPage {
/**
* @inheritDoc
*/
/**
* @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'];
}
* @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 {
/**
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;
* @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
*/
* @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();
$this->updateTrophyImage($trophy);
}
- return $trophy;
+ $trophyEditor = new TrophyEditor($trophy);
+ $trophyEditor->setShowOrder($showOrder);
+
+ return new Trophy($trophy->trophyID);
}
/**
}
}
}
+
+ 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']);
+ }
}
/**
}
}
}
+
+ /**
+ * @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();
+ }
}
namespace wcf\data\trophy;
use wcf\data\DatabaseObjectEditor;
use wcf\data\IEditableCachedObject;
+use wcf\system\WCF;
/**
* A trophy editor.
*/
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
*/
UserStorageHandler::getInstance()->update($this->userID, 'specialTrophies', serialize($specialTrophies));
}
+ $trophies = TrophyCache::getInstance()->getTrophiesByID($specialTrophies);
+ Trophy::sort($trophies, 'showOrder');
- return TrophyCache::getInstance()->getTrophiesByID($specialTrophies);
+ return $trophies;
}
/**
}, UserTrophyList::getUserTrophies([WCF::getUser()->userID])[WCF::getUser()->userID]));
$this->availableTrophies = TrophyCache::getInstance()->getTrophiesByID($trophyIDs);
+
+ Trophy::sort($this->availableTrophies, 'showOrder');
}
}
* selected sort field
* @var string
*/
- public $sortField = 'trophyID';
+ public $sortField = 'trophy.showOrder';
/**
* selected sort order
$trophyList->getConditionBuilder()->add('isDisabled = ?', [0]);
}
+ $trophyList->sqlOrderBy = 'trophy.showOrder ASC';
$trophyList->readObjects();
return $trophyList->getObjects();
}
<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">
<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">
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)
);