<name>com.woltlab.wcf.article.content</name>
<definitionname>com.woltlab.wcf.message</definitionname>
</type>
+ <type>
+ <name>com.woltlab.wcf.article</name>
+ <definitionname>com.woltlab.wcf.label.object</definitionname>
+ <classname>wcf\system\label\object\ArticleLabelObjectHandler</classname>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.article.category</name>
+ <definitionname>com.woltlab.wcf.label.objectType</definitionname>
+ <classname>wcf\system\label\object\type\ArticleCategoryLabelObjectTypeHandler</classname>
+ </type>
<!-- /articles -->
<type>
<div class="contentHeaderTitle">
<h1 class="contentTitle" itemprop="name headline">{$articleContent->title}</h1>
<ul class="inlineList contentHeaderMetaData articleMetaData">
+ {if $article->hasLabels()}
+ <li>
+ <span class="icon icon16 fa-tags"></span>
+ <ul class="labelList">
+ {foreach from=$article->getLabels() item=label}
+ <li><span class="label badge{if $label->getClassNames()} {$label->getClassNames()}{/if}">{lang}{$label->label}{/lang}</span></li>
+ {/foreach}
+ </ul>
+ </li>
+ {/if}
+
<li itemprop="author" itemscope itemtype="http://schema.org/Person">
<span class="icon icon16 fa-user"></span>
{if $article->userID}
<div class="containerHeadline">
<h3 class="articleListTitle">{$article->getTitle()}</h3>
<ul class="inlineList articleListMetaData">
+ {if $article->hasLabels()}
+ <li>
+ <span class="icon icon16 fa-tags"></span>
+ <ul class="labelList">
+ {foreach from=$article->getLabels() item=label}
+ <li><span class="label badge{if $label->getClassNames()} {$label->getClassNames()}{/if}">{lang}{$label->label}{/lang}</span></li>
+ {/foreach}
+ </ul>
+ </li>
+ {/if}
+
<li>
<span class="icon icon16 fa-clock-o"></span>
{@$article->time|time}
{if $lastVersion}<p class="info">{lang}wcf.acp.article.lastVersion{/lang}</p>{/if}
{/if}
-<form method="post" action="{if $action == 'add'}{link controller='ArticleAdd'}{/link}{else}{link controller='ArticleEdit' id=$articleID}{/link}{/if}">
+<form class="articleAddForm" method="post" action="{if $action == 'add'}{link controller='ArticleAdd'}{/link}{else}{link controller='ArticleEdit' id=$articleID}{/link}{/if}">
<div class="section">
<dl{if $errorField == 'categoryID'} class="formError"{/if}>
<dt><label for="categoryID">{lang}wcf.acp.article.category{/lang}</label></dt>
</dd>
</dl>
+ {if $labelGroups|count}
+ {foreach from=$labelGroups item=labelGroup}
+ {if $labelGroup|count}
+ <dl{if $errorField == 'label' && $errorType[$labelGroup->groupID]|isset} class="formError"{/if}>
+ <dt><label>{$labelGroup->getTitle()}</label></dt>
+ <dd>
+ <ul class="labelList jsOnly" data-object-id="{@$labelGroup->groupID}">
+ <li class="dropdown labelChooser" id="labelGroup{@$labelGroup->groupID}" data-group-id="{@$labelGroup->groupID}" data-force-selection="{if $labelGroup->forceSelection}true{else}false{/if}">
+ <div class="dropdownToggle" data-toggle="labelGroup{@$labelGroup->groupID}"><span class="badge label">{lang}wcf.label.none{/lang}</span></div>
+ <div class="dropdownMenu">
+ <ul class="scrollableDropdownMenu">
+ {foreach from=$labelGroup item=label}
+ <li data-label-id="{@$label->labelID}"><span><span class="badge label{if $label->getClassNames()} {@$label->getClassNames()}{/if}">{lang}{$label->label}{/lang}</span></span></li>
+ {/foreach}
+ </ul>
+ </div>
+ </li>
+ </ul>
+ <noscript>
+ <select name="labelIDs[{@$labelGroup->groupID}]">
+ {foreach from=$labelGroup item=label}
+ <option value="{@$label->labelID}">{lang}{$label->label}{/lang}</option>
+ {/foreach}
+ </select>
+ </noscript>
+ {if $errorField == 'label' && $errorType[$labelGroup->groupID]|isset}
+ <small class="innerError">
+ {if $errorType[$labelGroup->groupID] == 'missing'}
+ {lang}wcf.label.error.missing{/lang}
+ {else}
+ {lang}wcf.label.error.invalid{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ {/if}
+ {/foreach}
+ {/if}
+
<dl{if $errorField == 'username'} class="formError"{/if}>
<dt><label for="username">{lang}wcf.acp.article.author{/lang}</label></dt>
<dd>
</div>
</form>
+{js application='wcf' file='WCF.Label' bundle='WCF.Combined'}
+<script data-relocate="true">
+ $(function() {
+ WCF.Language.addObject({
+ 'wcf.label.none': '{lang}wcf.label.none{/lang}',
+ });
+
+ {if !$labelGroups|empty}
+ new WCF.Label.ArticleLabelChooser({ {implode from=$labelGroupsToCategories key=__labelCategoryID item=labelGroupIDs}{@$__labelCategoryID}: [ {implode from=$labelGroupIDs item=labelGroupID}{@$labelGroupID}{/implode} ] {/implode} }, { {implode from=$labelIDs key=groupID item=labelID}{@$groupID}: {@$labelID}{/implode} }, '.articleAddForm');
+ {/if}
+ });
+</script>
+
{include file='footer'}
</span>
<div class="containerHeadline">
+ {if $article->hasLabels()}
+ <ul class="labelList" style="float: right; padding-left: 7px;">
+ {foreach from=$article->getLabels() item=label}
+ <li><span class="badge label{if $label->getClassNames()} {$label->getClassNames()}{/if}">{lang}{$label->label}{/lang}</span></li>
+ {/foreach}
+ </ul>
+ {/if}
+
<h3>
{if $article->isDeleted}<span class="badge label red jsIconDeleted">{lang}wcf.message.status.deleted{/lang}</span>{/if}
{if $article->publicationStatus == 0}<span class="badge">{lang}wcf.acp.article.publicationStatus.unpublished{/lang}</span>{/if}
}
}
});
+
+/**
+ * Handles displaying label groups based on the selected categories.
+ */
+WCF.Label.ArticleLabelChooser = WCF.Label.Chooser.extend({
+ /**
+ * maps the available label group ids to the categories
+ * @var object
+ */
+ _labelGroupsToCategories: null,
+
+ /**
+ * Initializes a new WCF.Label.ArticleLabelChooser object.
+ *
+ * @param object labelGroupsToCategories
+ * @param object selectedLabelIDs
+ * @param string containerSelector
+ * @param string submitButtonSelector
+ * @param boolean showWithoutSelection
+ */
+ init: function(labelGroupsToCategories, selectedLabelIDs, containerSelector, submitButtonSelector, showWithoutSelection) {
+ this._super(selectedLabelIDs, containerSelector, submitButtonSelector, showWithoutSelection);
+ this._labelGroupsToCategories = labelGroupsToCategories;
+
+ this._updateLabelGroups();
+
+ $('#categoryID').change($.proxy(this._updateLabelGroups, this));
+ },
+
+ /**
+ * Updates the visible label groups based on the selected categories.
+ */
+ _updateLabelGroups: function() {
+ // hide all label choosers first
+ $('.labelChooser').each(function(index, element) {
+ $(element).parents('dl:eq(0)').hide();
+ })
+
+ var visibleGroupIDs = [];
+ var categoryID = parseInt($('#categoryID').val());
+
+ if (this._labelGroupsToCategories[categoryID]) {
+ for (var i = 0, length = this._labelGroupsToCategories[categoryID].length; i < length; i++) {
+ $('#labelGroup' + this._labelGroupsToCategories[categoryID][i]).parents('dl:eq(0)').show();
+ }
+ }
+ },
+
+ /**
+ * @see WCF.Label.Chooser._submit()
+ */
+ _submit: function() {
+ // delete non-selected groups to avoid sumitting these labels
+ for (var groupID in this._groups) {
+ if (!this._groups[groupID].is(':visible')) {
+ delete this._groups[groupID];
+ }
+ }
+
+ this._super();
+ }
+});
\ No newline at end of file
use wcf\data\article\Article;
use wcf\data\article\ArticleAction;
use wcf\data\category\CategoryNodeTree;
+use wcf\data\label\group\ViewableLabelGroup;
use wcf\data\language\Language;
use wcf\data\media\Media;
use wcf\data\media\ViewableMediaList;
use wcf\data\user\User;
use wcf\form\AbstractForm;
+use wcf\system\cache\builder\ArticleCategoryLabelCacheBuilder;
use wcf\system\exception\UserInputException;
use wcf\system\html\input\HtmlInputProcessor;
+use wcf\system\label\object\ArticleLabelObjectHandler;
use wcf\system\language\LanguageFactory;
use wcf\system\request\LinkHandler;
use wcf\system\WCF;
*/
public $availableLanguages = [];
+ /**
+ * label group list
+ * @var ViewableLabelGroup[]
+ */
+ public $labelGroups;
+
+ /**
+ * list of label ids
+ * @var integer[]
+ */
+ public $labelIDs = [];
+
+ /**
+ * maps the label group ids to the article category ids
+ * @var array
+ */
+ public $labelGroupsToCategories = [];
+
/**
* @inheritDoc
*/
$this->availableLanguages = LanguageFactory::getInstance()->getLanguages();
$this->readMultilingualSetting();
+
+ // labels
+ ArticleLabelObjectHandler::getInstance()->setCategoryIDs(ArticleCategory::getAccessibleCategoryIDs());
}
/**
parent::readFormParameters();
$this->enableComments = 0;
+ if (isset($_POST['labelIDs']) && is_array($_POST['labelIDs'])) $this->labelIDs = $_POST['labelIDs'];
if (isset($_POST['username'])) $this->username = StringUtil::trim($_POST['username']);
if (isset($_POST['time'])) {
$this->time = $_POST['time'];
$this->htmlInputProcessors[0] = new HtmlInputProcessor();
$this->htmlInputProcessors[0]->process($this->content[0], 'com.woltlab.wcf.article.content', 0);
}
+
+ $this->validateLabelIDs();
+ }
+
+ /**
+ * Validates the selected labels.
+ */
+ protected function validateLabelIDs() {
+ // set category ids to selected category ids for validation
+ ArticleLabelObjectHandler::getInstance()->setCategoryIDs([$this->categoryID]);
+
+ $validationResult = ArticleLabelObjectHandler::getInstance()->validateLabelIDs($this->labelIDs, 'canSetLabel', false);
+
+ // reset category ids to accessible category ids
+ ArticleLabelObjectHandler::getInstance()->setCategoryIDs(ArticleCategory::getAccessibleCategoryIDs());
+
+ if (!empty($validationResult[0])) {
+ throw new UserInputException('labelIDs');
+ }
+
+ if (!empty($validationResult)) {
+ throw new UserInputException('label', $validationResult);
+ }
}
/**
'enableComments' => $this->enableComments,
'userID' => $this->author->userID,
'username' => $this->author->username,
- 'isMultilingual' => $this->isMultilingual
+ 'isMultilingual' => $this->isMultilingual,
+ 'hasLabels' => empty($this->labelIDs) ? 0 : 1
];
$this->objectAction = new ArticleAction([], 'create', ['data' => array_merge($this->additionalFields, $data), 'content' => $content]);
- $this->objectAction->executeAction();
+ $article = $this->objectAction->executeAction()['returnValues'];
+ // save labels
+ if (!empty($this->labelIDs)) {
+ ArticleLabelObjectHandler::getInstance()->setLabels($this->labelIDs, $article->articleID);
+ }
// call saved event
$this->saved();
public function readData() {
parent::readData();
+ $this->labelGroupsToCategories = ArticleCategoryLabelCacheBuilder::getInstance()->getData();
+ $this->labelGroups = ArticleCategory::getAccessibleLabelGroups();
+
if (empty($_POST)) {
$this->setDefaultValues();
}
'teaser' => $this->teaser,
'content' => $this->content,
'availableLanguages' => $this->availableLanguages,
- 'categoryNodeList' => (new CategoryNodeTree('com.woltlab.wcf.article.category'))->getIterator()
+ 'categoryNodeList' => (new CategoryNodeTree('com.woltlab.wcf.article.category'))->getIterator(),
+ 'labelIDs' => $this->labelIDs,
+ 'labelGroups' => $this->labelGroups,
+ 'labelGroupsToCategories' => $this->labelGroupsToCategories
]);
}
}
use wcf\form\AbstractForm;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\PermissionDeniedException;
+use wcf\system\label\object\ArticleLabelObjectHandler;
use wcf\system\language\LanguageFactory;
use wcf\system\tagging\TagEngine;
use wcf\system\version\VersionTracker;
public function save() {
AbstractForm::save();
+ // save labels
+ ArticleLabelObjectHandler::getInstance()->setLabels($this->labelIDs, $this->article->articleID);
+ $labelIDs = ArticleLabelObjectHandler::getInstance()->getAssignedLabels([$this->article->articleID], false);
+
$content = [];
if ($this->isMultilingual) {
foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
'enableComments' => $this->enableComments,
'userID' => $this->author->userID,
'username' => $this->author->username,
- 'time' => $this->timeObj->getTimestamp()
+ 'time' => $this->timeObj->getTimestamp(),
+ 'hasLabels' => (isset($labelIDs[$this->article->articleID]) && !empty($labelIDs[$this->article->articleID])) ? 1 : 0
];
$this->objectAction = new ArticleAction([$this->article], 'update', ['data' => array_merge($this->additionalFields, $data), 'content' => $content]);
}
$this->readImages();
+
+ // labels
+ $assignedLabels = ArticleLabelObjectHandler::getInstance()->getAssignedLabels([$this->article->articleID], true);
+ if (isset($assignedLabels[$this->article->articleID])) {
+ foreach ($assignedLabels[$this->article->articleID] as $label) {
+ $this->labelIDs[$label->groupID] = $label->labelID;
+ }
+ }
}
}
* @property-read integer $views number of times the article has been viewed
* @property-read integer $cumulativeLikes cumulative result of likes (counting `+1`) and dislikes (counting `-1`) for the article
* @property-read integer $isDeleted is 1 if the article is in trash bin, otherwise 0
+ * @property-read integer $hasLabels is `1` if labels are assigned to the article
*/
class Article extends DatabaseObject implements ILinkableObject {
/**
use wcf\data\article\category\ArticleCategory;
use wcf\data\article\content\ArticleContent;
use wcf\data\article\content\ViewableArticleContent;
+use wcf\data\label\Label;
use wcf\data\media\ViewableMedia;
use wcf\data\user\User;
use wcf\data\user\UserProfile;
*/
protected static $unreadArticles;
+ /**
+ * list of assigned labels
+ * @var Label[]
+ */
+ protected $labels = [];
+
/**
* Returns a specific article decorated as viewable article or `null` if it does not exist.
*
return $this->time > $this->getVisitTime();
}
+ /**
+ * Adds a label.
+ *
+ * @param Label $label
+ */
+ public function addLabel(Label $label) {
+ $this->labels[$label->labelID] = $label;
+ }
+
+ /**
+ * Returns a list of labels.
+ *
+ * @return Label[]
+ */
+ public function getLabels() {
+ return $this->labels;
+ }
+
+ /**
+ * Returns true if one or more labels are assigned to this article.
+ *
+ * @return boolean
+ */
+ public function hasLabels() {
+ return !empty($this->labels);
+ }
+
/**
* Returns the number of unread articles.
*
namespace wcf\data\article;
use wcf\data\article\content\ViewableArticleContentList;
use wcf\system\cache\runtime\UserProfileRuntimeCache;
+use wcf\system\label\object\ArticleLabelObjectHandler;
use wcf\system\like\LikeHandler;
use wcf\system\visitTracker\VisitTracker;
use wcf\system\WCF;
public function readObjects() {
parent::readObjects();
- $userIDs = [];
+ $userIDs = $articleIDs = [];
foreach ($this->getObjects() as $article) {
if ($article->userID) {
$userIDs[] = $article->userID;
}
+ if ($article->hasLabels) {
+ $articleIDs[] = $article->articleID;
+ }
}
// cache user profiles
$this->objects[$articleContent->articleID]->setArticleContent($articleContent);
}
}
+
+ // get labels
+ if (!empty($articleIDs)) {
+ $assignedLabels = ArticleLabelObjectHandler::getInstance()->getAssignedLabels($articleIDs);
+ foreach ($assignedLabels as $articleID => $labels) {
+ foreach ($labels as $label) {
+ $this->objects[$articleID]->addLabel($label);
+ }
+ }
+ }
}
/**
<?php
namespace wcf\data\article\category;
use wcf\data\category\AbstractDecoratedCategory;
+use wcf\data\label\group\ViewableLabelGroup;
use wcf\data\user\User;
use wcf\data\user\UserProfile;
use wcf\data\IAccessibleObject;
use wcf\data\ITitledLinkObject;
+use wcf\system\cache\builder\ArticleCategoryLabelCacheBuilder;
use wcf\system\category\CategoryHandler;
use wcf\system\category\CategoryPermissionHandler;
+use wcf\system\label\LabelHandler;
use wcf\system\request\LinkHandler;
use wcf\system\WCF;
return $categoryIDs;
}
+
+ /**
+ * Returns the label groups for all accessible categories.
+ *
+ * @return ViewableLabelGroup[]
+ */
+ public static function getAccessibleLabelGroups() {
+ $labelGroupsToCategories = ArticleCategoryLabelCacheBuilder::getInstance()->getData();
+ $accessibleCategoryIDs = self::getAccessibleCategoryIDs();
+
+ $groupIDs = [];
+ foreach ($labelGroupsToCategories as $categoryID => $__groupIDs) {
+ if (in_array($categoryID, $accessibleCategoryIDs)) {
+ $groupIDs = array_merge($groupIDs, $__groupIDs);
+ }
+ }
+ if (empty($groupIDs)) return [];
+
+ return LabelHandler::getInstance()->getLabelGroups(array_unique($groupIDs));
+ }
}
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\category\CategoryHandler;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\WCF;
+
+/**
+ * Caches the available label group ids for article categories.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Cache\Builder
+ * @since 3.1
+ */
+class ArticleCategoryLabelCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @inheritDoc
+ */
+ protected function rebuild(array $parameters) {
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add('objectTypeID = ?', [ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.label.objectType', 'com.woltlab.wcf.article.category')->objectTypeID]);
+ $conditionBuilder->add('objectID IN (SELECT categoryID FROM wcf'.WCF_N.'_category WHERE objectTypeID = ?)', [CategoryHandler::getInstance()->getObjectTypeByName('com.woltlab.wcf.article.category')->objectTypeID]);
+
+ $sql = "SELECT groupID, objectID
+ FROM wcf".WCF_N."_label_group_to_object
+ ".$conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditionBuilder->getParameters());
+
+ return $statement->fetchMap('objectID', 'groupID', false);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\label\object;
+use wcf\system\cache\builder\ArticleCategoryLabelCacheBuilder;
+use wcf\system\label\LabelHandler;
+
+/**
+ * Label handler for articles.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Label\Object
+ * @since 3.1
+ */
+class ArticleLabelObjectHandler extends AbstractLabelObjectHandler {
+ /**
+ * @inheritDoc
+ */
+ protected $objectType = 'com.woltlab.wcf.article';
+
+ /**
+ * Sets the label groups available for the categories with the given ids.
+ *
+ * @param integer[] $categoryIDs
+ */
+ public function setCategoryIDs($categoryIDs) {
+ $labelGroupsToCategories = ArticleCategoryLabelCacheBuilder::getInstance()->getData();
+
+ $groupIDs = [];
+ foreach ($labelGroupsToCategories as $categoryID => $__groupIDs) {
+ if (in_array($categoryID, $categoryIDs)) {
+ $groupIDs = array_merge($groupIDs, $__groupIDs);
+ }
+ }
+
+ $this->labelGroups = [];
+ if (!empty($groupIDs)) {
+ $this->labelGroups = LabelHandler::getInstance()->getLabelGroups(array_unique($groupIDs));
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\label\object\type;
+use wcf\data\article\category\ArticleCategoryNode;
+use wcf\data\article\category\ArticleCategoryNodeTree;
+use wcf\system\cache\builder\ArticleCategoryLabelCacheBuilder;
+
+/**
+ * Object type handler for article categories.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Label\Object\Type
+ * @since 3.1
+ */
+class ArticleCategoryLabelObjectTypeHandler extends AbstractLabelObjectTypeHandler {
+ /**
+ * category list
+ * @var \RecursiveIteratorIterator
+ */
+ public $categoryList;
+
+ /**
+ * @inheritDoc
+ */
+ protected function init() {
+ $categoryTree = new ArticleCategoryNodeTree('com.woltlab.wcf.article.category');
+ $this->categoryList = $categoryTree->getIterator();
+ $this->categoryList->setMaxDepth(0);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setObjectTypeID($objectTypeID) {
+ parent::setObjectTypeID($objectTypeID);
+
+ $this->container = new LabelObjectTypeContainer($this->objectTypeID);
+ /** @var ArticleCategoryNode $category */
+ foreach ($this->categoryList as $category) {
+ $this->container->add(new LabelObjectType($category->getTitle(), $category->categoryID, 0));
+ foreach ($category as $subCategory) {
+ $this->container->add(new LabelObjectType($subCategory->getTitle(), $subCategory->categoryID, 1));
+ foreach ($subCategory as $subSubCategory) {
+ $this->container->add(new LabelObjectType($subSubCategory->getTitle(), $subSubCategory->categoryID, 2));
+ }
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save() {
+ ArticleCategoryLabelCacheBuilder::getInstance()->reset();
+ }
+}
<item name="wcf.acp.label.showOrder.description"><![CDATA[Reihenfolge des Labels innerhalb seiner Labelgruppe. Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} das Feld leer {if LANGUAGE_USE_INFORMAL_VARIANT}lässt{else}lassen{/if}, wird das Label an letzter Position einsortiert.]]></item>
<item name="wcf.acp.label.sortAfterGroupFiltering"><![CDATA[Wenn {if LANGUAGE_USE_INFORMAL_VARIANT}du{else}Sie{/if} die Label-Liste nur nach einer bestimmten Labelgruppe {if LANGUAGE_USE_INFORMAL_VARIANT}filterst{else}filtern{/if}, {if LANGUAGE_USE_INFORMAL_VARIANT}kannst du{else}können Sie{/if} die Labels innerhalb dieser Gruppe durch Ziehen und Loslassen sortieren.]]></item>
<item name="wcf.acp.label.filter"><![CDATA[Filter]]></item>
+ <item name="wcf.acp.label.container.com.woltlab.wcf.article.category"><![CDATA[Artikel]]></item>
</category>
<category name="wcf.acp.language">
<item name="wcf.acp.label.showOrder.description"><![CDATA[Display order of the label in its label group. If you leave this field empty, the label will be placed at the last position.]]></item>
<item name="wcf.acp.label.sortAfterGroupFiltering"><![CDATA[If you only filter the label list by a certain label group, you can sort the labels in this group using drag and drop.]]></item>
<item name="wcf.acp.label.filter"><![CDATA[Filter]]></item>
+ <item name="wcf.acp.label.container.com.woltlab.wcf.article.category"><![CDATA[Articles]]></item>
</category>
<category name="wcf.acp.language">
views MEDIUMINT(7) NOT NULL DEFAULT 0,
cumulativeLikes MEDIUMINT(7) NOT NULL DEFAULT 0,
isDeleted TINYINT(1) NOT NULL DEFAULT 0,
+ hasLabels TINYINT(1) NOT NULL DEFAULT 0,
KEY (time)
);