</pages>
</action>
<!-- /com.woltlab.wcf.media -->
+
+ <!-- com.woltlab.wcf.article -->
+ <action name="trash">
+ <actionclassname>wcf\system\clipboard\action\ArticleClipboardAction</actionclassname>
+ <showorder>1</showorder>
+ <pages>
+ <page>wcf\acp\page\ArticleListPage</page>
+ </pages>
+ </action>
+ <action name="delete">
+ <actionclassname>wcf\system\clipboard\action\ArticleClipboardAction</actionclassname>
+ <showorder>2</showorder>
+ <pages>
+ <page>wcf\acp\page\ArticleListPage</page>
+ </pages>
+ </action>
+ <action name="restore">
+ <actionclassname>wcf\system\clipboard\action\ArticleClipboardAction</actionclassname>
+ <showorder>3</showorder>
+ <pages>
+ <page>wcf\acp\page\ArticleListPage</page>
+ </pages>
+ </action>
+ <action name="unpublish">
+ <actionclassname>wcf\system\clipboard\action\ArticleClipboardAction</actionclassname>
+ <showorder>4</showorder>
+ <pages>
+ <page>wcf\acp\page\ArticleListPage</page>
+ </pages>
+ </action>
+ <action name="publish">
+ <actionclassname>wcf\system\clipboard\action\ArticleClipboardAction</actionclassname>
+ <showorder>5</showorder>
+ <pages>
+ <page>wcf\acp\page\ArticleListPage</page>
+ </pages>
+ </action>
+ <action name="setCategory">
+ <actionclassname>wcf\system\clipboard\action\ArticleClipboardAction</actionclassname>
+ <showorder>6</showorder>
+ <pages>
+ <page>wcf\acp\page\ArticleListPage</page>
+ </pages>
+ </action>
+ <!-- /com.woltlab.wcf.article -->
</import>
</data>
<definitionname>com.woltlab.wcf.clipboardItem</definitionname>
<listclassname>wcf\data\media\ViewableMediaList</listclassname>
</type>
+ <type>
+ <name>com.woltlab.wcf.article</name>
+ <definitionname>com.woltlab.wcf.clipboardItem</definitionname>
+ <listclassname>wcf\data\article\ArticleList</listclassname>
+ </type>
<!-- /clipboard items -->
<!-- articles -->
--- /dev/null
+<div class="section">
+ <dl>
+ <dt>{lang}wcf.acp.article.category{/lang}</dt>
+ <dd>
+ <select name="categoryID" id="categoryID">
+ <option value="0">{lang}wcf.global.noSelection{/lang}</option>
+
+ {foreach from=$categoryNodeList item=category}
+ <option value="{@$category->categoryID}">{if $category->getDepth() > 1}{@" "|str_repeat:($category->getDepth() - 1)}{/if}{$category->getTitle()}</option>
+ {/foreach}
+ </select>
+ </dd>
+ </dl>
+</div>
+
+<div class="formSubmit">
+ <button data-type="submit">{lang}wcf.global.button.submit{/lang}</button>
+</div>
{include file='header' pageTitle='wcf.acp.article.list'}
<script data-relocate="true">
- require(['Language', 'WoltLabSuite/Core/Ui/User/Search/Input', 'WoltLabSuite/Core/Acp/Ui/Article/InlineEditor'], function(Language, UiUserSearchInput, AcpUiArticleInlineEditor) {
+ require(['Language', 'WoltLabSuite/Core/Controller/Clipboard', 'WoltLabSuite/Core/Ui/User/Search/Input', 'WoltLabSuite/Core/Acp/Ui/Article/InlineEditor'],
+ function(Language, ControllerClipboard, UiUserSearchInput, AcpUiArticleInlineEditor) {
Language.addObject({
+ 'wcf.acp.article.publicationStatus.unpublished': '{lang}wcf.acp.article.publicationStatus.unpublished{/lang}',
+ 'wcf.acp.article.setCategory': '{lang}wcf.acp.article.setCategory{/lang}',
'wcf.message.status.deleted': '{lang}wcf.message.status.deleted{/lang}'
});
new UiUserSearchInput(elBySel('input[name="username"]'));
new AcpUiArticleInlineEditor(0);
+
+ ControllerClipboard.setup({
+ hasMarkedItems: {if $hasMarkedItems}true{else}false{/if},
+ pageClassName: 'wcf\\acp\\page\\ArticleListPage'
+ });
});
</script>
{if $objects|count}
<div class="section tabularBox">
- <table class="table">
+ <table data-type="com.woltlab.wcf.article" class="table jsClipboardContainer">
<thead>
<tr>
+ <th class="columnMark"><label><input type="checkbox" class="jsClipboardMarkAll"></label></th>
<th class="columnID columnArticleID{if $sortField == 'articleID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='ArticleList'}pageNo={@$pageNo}&sortField=articleID&sortOrder={if $sortField == 'articleID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
<th class="columnText columnArticleTitle{if $sortField == 'title'} active {@$sortOrder}{/if}"><a href="{link controller='ArticleList'}pageNo={@$pageNo}&sortField=title&sortOrder={if $sortField == 'title' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.global.title{/lang}</a></th>
<th class="columnDigits columnComments{if $sortField == 'comments'} active {@$sortOrder}{/if}"><a href="{link controller='ArticleList'}pageNo={@$pageNo}&sortField=comments&sortOrder={if $sortField == 'comments' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.global.comments{/lang}</a></th>
<tbody>
{foreach from=$objects item=article}
- <tr class="jsArticleRow" data-object-id="{@$article->articleID}">
+ <tr class="jsArticleRow jsClipboardObject" data-object-id="{@$article->articleID}">
+ <td class="columnMark"><input type="checkbox" class="jsClipboardItem" data-object-id="{@$article->articleID}"></td>
<td class="columnIcon">
<a href="{link controller='ArticleEdit' id=$article->articleID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon24 fa-pencil"></span></a>
{if $article->canDelete()}
<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}
+ {if $article->publicationStatus == 0}<span class="badge jsUnpublishedArticle">{lang}wcf.acp.article.publicationStatus.unpublished{/lang}</span>{/if}
{if $article->publicationStatus == 2}<span class="badge" title="{$article->publicationDate|plainTime}">{lang}wcf.acp.article.publicationStatus.delayed{/lang}</span>{/if}
<a href="{link controller='ArticleEdit' id=$article->articleID}{/link}" title="{lang}wcf.acp.article.edit{/lang}" class="jsTooltip">{$article->title}</a>
</h3>
<ul class="inlineList dotSeparated">
{if $article->categoryID}
- <li>{$article->getCategory()->getTitle()}</li>
+ <li class="jsArticleCategory">{$article->getCategory()->getTitle()}</li>
{/if}
{if $article->username}
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @module WoltLabSuite/Core/Acp/Ui/Article/InlineEditor
*/
-define(['Ajax', 'Core', 'Dictionary', 'Language', 'Ui/Confirmation', 'Ui/Notification'], function (Ajax, Core, Dictionary, Language, UiConfirmation, UiNotification) {
+define(['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'EventHandler', 'Language', 'Ui/Confirmation', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Controller/Clipboard'],
+ function (Ajax, Core, Dictionary, DomUtil, EventHandler, Language, UiConfirmation, UiDialog, UiNotification, ControllerClipboard) {
"use strict";
var _articles = new Dictionary();
}
else {
elBySelAll('.jsArticleRow', undefined, this._initArticle.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.article', this._clipboardAction.bind(this));
+ }
+ },
+
+ /**
+ * Reacts to executed clipboard actions.
+ *
+ * @param {object<string, *>} actionData data of the executed clipboard action
+ */
+ _clipboardAction: function(actionData) {
+ // only consider events if the action has been executed
+ if (actionData.responseData !== null) {
+ var triggerFunction;
+ switch (actionData.data.actionName) {
+ case 'com.woltlab.wcf.article.delete':
+ triggerFunction = this._triggerDelete;
+ break;
+
+ case 'com.woltlab.wcf.article.publish':
+ triggerFunction = this._triggerPublish;
+ break;
+
+ case 'com.woltlab.wcf.article.restore':
+ triggerFunction = this._triggerRestore;
+ break;
+
+ case 'com.woltlab.wcf.article.trash':
+ triggerFunction = this._triggerTrash;
+ break;
+
+ case 'com.woltlab.wcf.article.unpublish':
+ triggerFunction = this._triggerUnpublish;
+ break;
+ }
+
+ if (triggerFunction) {
+ for (var i = 0, length = actionData.responseData.objectIDs.length; i < length; i++) {
+ triggerFunction(actionData.responseData.objectIDs[i]);
+ }
+
+ UiNotification.show();
+ }
+ }
+ else if (actionData.data.actionName === 'com.woltlab.wcf.article.setCategory') {
+ try {
+ dialog = UiDialog.getDialog('articleCategoryDialog');
+ UiDialog.openStatic('articleCategoryDialog');
+ }
+ catch (e) {
+ UiDialog.openStatic('articleCategoryDialog', actionData.data.internalData.template, {
+ title: Language.get('wcf.acp.article.setCategory')
+ });
+
+ elBySel('[data-type=submit]', UiDialog.getDialog('articleCategoryDialog').content).addEventListener('click', this._submitSetCategory.bind(this));
+ }
+ }
+ },
+
+ /**
+ * Is called, if the set category dialog form is submitted.
+ *
+ * @param {Event} event form submit button click event
+ */
+ _submitSetCategory: function(event) {
+ var dialog = UiDialog.getDialog('articleCategoryDialog').content;
+ var innerErrors = elByClass('innerError', dialog);
+ var select = elBySel('select[name=categoryID]', dialog);
+
+ var categoryId = ~~elBySel('select[name=categoryID]', event.currentTarget.parentNode.parentNode).value;
+ if (categoryId) {
+ Ajax.api(this, {
+ actionName: 'setCategory',
+ parameters: {
+ categoryID: categoryId,
+ useMarkedArticles: true
+ }
+ });
+
+ if (innerErrors.length === 1) {
+ elRemove(innerErrors.item(0));
+ }
+
+ UiDialog.close('articleCategoryDialog');
+ }
+ else if (innerErrors.length === 0) {
+ var innerError = elCreate('small');
+ innerError.className = 'innerError';
+ innerError.innerText = Language.get('wcf.global.form.error.empty');
+
+ DomUtil.insertAfter(innerError, select);
}
},
*/
_initArticle: function (article, objectId) {
var isArticleEdit = false;
- if (~~objectId > 0) {
+ if (!article && ~~objectId > 0) {
isArticleEdit = true;
article = undefined;
}
});
},
+ /**
+ * Handles an article being deleted.
+ *
+ * @param {int} articleId id of the deleted article
+ */
+ _triggerDelete: function(articleId) {
+ var article = _articles.get(articleId);
+
+ if (article.isArticleEdit) {
+ //noinspection JSUnresolvedVariable
+ window.location = data.returnValues.redirectURL;
+ }
+ else {
+ var tbody = article.element.parentNode;
+ elRemove(article.element);
+
+ if (elBySel('tr', tbody) === null) {
+ window.location.reload();
+ }
+ }
+ },
+
+ /**
+ * Handles publishing an article via clipboard.
+ *
+ * @param {int} articleId id of the published article
+ */
+ _triggerPublish: function(articleId) {
+ var article = _articles.get(articleId);
+
+ if (article.isArticleEdit) {
+ // unsupported
+ }
+ else {
+ elRemove(elBySel('.jsUnpublishedArticle', article.element));
+ }
+ },
+
+ /**
+ * Handles an article being restored.
+ *
+ * @param {int} articleId id of the deleted article
+ */
+ _triggerRestore: function(articleId) {
+ var article = _articles.get(articleId);
+
+ elHide(article.buttons.delete);
+ elHide(article.buttons.restore);
+ elShow(article.buttons.trash);
+
+ if (article.isArticleEdit) {
+ elHide(elBySel('.jsArticleNoticeTrash'));
+ }
+ else {
+ elRemove(elBySel('.jsIconDeleted', article.element));
+ }
+ },
+
+ /**
+ * Handles an article being trashed.
+ *
+ * @param {int} articleId id of the deleted article
+ */
+ _triggerTrash: function(articleId) {
+ var article = _articles.get(articleId);
+
+ elShow(article.buttons.delete);
+ elShow(article.buttons.restore);
+ elHide(article.buttons.trash);
+
+ if (article.isArticleEdit) {
+ elShow(elBySel('.jsArticleNoticeTrash'));
+ }
+ else {
+ var badge = elCreate('span');
+ badge.className = 'badge label red jsIconDeleted';
+ badge.textContent = Language.get('wcf.message.status.deleted');
+
+ var h3 = elBySel('.containerHeadline > h3', article.element);
+ h3.insertBefore(badge, h3.firstChild);
+ }
+ },
+
+ /**
+ * Handles unpublishing an article via clipboard.
+ *
+ * @param {int} articleId id of the unpublished article
+ */
+ _triggerUnpublish: function(articleId) {
+ var article = _articles.get(articleId);
+
+ if (article.isArticleEdit) {
+ // unsupported
+ }
+ else {
+ var badge = elCreate('span');
+ badge.className = 'badge jsUnpublishedArticle';
+ badge.textContent = Language.get('wcf.acp.article.publicationStatus.unpublished');
+
+ var h3 = elBySel('.containerHeadline > h3', article.element);
+ var a = elBySel('a', h3);
+
+ h3.insertBefore(badge, a);
+ h3.insertBefore(document.createTextNode(" "), a);
+ }
+ },
+
_ajaxSuccess: function (data) {
- var article = _articles.get(data.objectIDs[0]);
+ var notificationCallback;
+
switch (data.actionName) {
case 'delete':
- if (article.isArticleEdit) {
- //noinspection JSUnresolvedVariable
- window.location = data.returnValues.redirectURL;
- }
- else {
- var tbody = article.element.parentNode;
- elRemove(article.element);
-
- if (elBySel('tr', tbody) === null) {
- window.location.reload();
- }
- }
+ this._triggerDelete(data.objectIDs[0]);
break;
case 'restore':
- elHide(article.buttons.delete);
- elHide(article.buttons.restore);
- elShow(article.buttons.trash);
+ this._triggerRestore(data.objectIDs[0]);
+ break;
- if (article.isArticleEdit) {
- elHide(elBySel('.jsArticleNoticeTrash'));
- }
- else {
- elRemove(elBySel('.jsIconDeleted', article.element));
- }
+ case 'setCategory':
+ notificationCallback = window.location.reload.bind(window.location);
break;
case 'toggleI18n':
break;
case 'trash':
- elShow(article.buttons.delete);
- elShow(article.buttons.restore);
- elHide(article.buttons.trash);
-
- if (article.isArticleEdit) {
- elShow(elBySel('.jsArticleNoticeTrash'));
- }
- else {
- var badge = elCreate('span');
- badge.className = 'badge label red jsIconDeleted';
- badge.textContent = Language.get('wcf.message.status.deleted');
-
- var h3 = elBySel('.containerHeadline > h3', article.element);
- h3.insertBefore(badge, h3.firstChild);
- }
-
+ this._triggerTrash(data.objectIDs[0]);
break;
}
- UiNotification.show();
+ UiNotification.show(undefined, notificationCallback);
+ ControllerClipboard.reload();
},
_ajaxSetup: function () {
use wcf\data\category\CategoryNodeTree;
use wcf\data\user\User;
use wcf\page\SortablePage;
+use wcf\system\clipboard\ClipboardHandler;
use wcf\system\language\LanguageFactory;
use wcf\system\WCF;
use wcf\util\StringUtil;
'showArticleAddDialog' => $this->showArticleAddDialog,
'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
'categoryNodeList' => (new CategoryNodeTree('com.woltlab.wcf.article.category'))->getIterator(),
- 'publicationStatus' => $this->publicationStatus
+ 'publicationStatus' => $this->publicationStatus,
+ 'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.article')),
]);
}
}
<?php
namespace wcf\data\article;
+use wcf\data\article\category\ArticleCategory;
use wcf\data\article\content\ArticleContent;
use wcf\data\article\content\ArticleContentAction;
use wcf\data\article\content\ArticleContentEditor;
use wcf\data\language\Language;
use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\clipboard\ClipboardHandler;
use wcf\system\comment\CommentHandler;
use wcf\system\exception\UserInputException;
use wcf\system\language\LanguageFactory;
}
}
+ /**
+ * Validates parameters to delete articles.
+ *
+ * @throws UserInputException
+ */
+ public function validateDelete() {
+ WCF::getSession()->checkPermissions(['admin.content.article.canManageArticle']);
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ foreach ($this->getObjects() as $article) {
+ if (!$article->isDeleted) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+ }
+
/**
* @inheritDoc
*/
SearchIndexManager::getInstance()->delete('com.woltlab.wcf.article', $articleContentIDs);
}
+ $this->unmarkItems();
+
return [
+ 'objectIDs' => $this->objectIDs,
'redirectURL' => LinkHandler::getInstance()->getLink('ArticleList', ['isACP' => true])
];
}
/**
- * Validates parameters to move an article to the trash bin.
+ * Validates parameters to move articles to the trash bin.
*
- * @throws UserInputException
+ * @throws UserInputException
*/
public function validateTrash() {
WCF::getSession()->checkPermissions(['admin.content.article.canManageArticle']);
- $this->articleEditor = $this->getSingleObject();
- if ($this->articleEditor->isDeleted) {
- throw new UserInputException('objectIDs');
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ foreach ($this->getObjects() as $article) {
+ if ($article->isDeleted) {
+ throw new UserInputException('objectIDs');
+ }
}
}
/**
- * Moves an article to the trash bin.
+ * Moves articles to the trash bin.
*/
public function trash() {
- $this->articleEditor->update(['isDeleted' => 1]);
+ foreach ($this->getObjects() as $articleEditor) {
+ $articleEditor->update(['isDeleted' => 1]);
+ }
+
+ $this->unmarkItems();
+
+ return ['objectIDs' => $this->objectIDs];
}
/**
- * Validates parameters o restore an article.
+ * Validates parameters to restore articles.
*
- * @throws UserInputException
+ * @throws UserInputException
*/
public function validateRestore() {
- WCF::getSession()->checkPermissions(['admin.content.article.canManageArticle']);
-
- $this->articleEditor = $this->getSingleObject();
- if (!$this->articleEditor->isDeleted) {
- throw new UserInputException('objectIDs');
- }
+ $this->validateDelete();
}
/**
- * Restores an article.
+ * Restores articles.
*/
public function restore() {
- $this->articleEditor->update(['isDeleted' => 0]);
+ foreach ($this->getObjects() as $articleEditor) {
+ $articleEditor->update(['isDeleted' => 0]);
+ }
+
+ $this->unmarkItems();
+
+ return ['objectIDs' => $this->objectIDs];
}
/**
public function validateMarkAllAsRead() {
// does nothing
}
+
+ /**
+ * Validates the `setCategory` action.
+ *
+ * @throws UserInputException
+ */
+ public function validateSetCategory() {
+ WCF::getSession()->checkPermissions(['admin.content.article.canManageArticle']);
+
+ $this->readBoolean('useMarkedArticles', true);
+
+ // if no object ids are given, use clipboard handler
+ if (empty($this->objectIDs) && $this->parameters['useMarkedArticles']) {
+ $this->objectIDs = array_keys(ClipboardHandler::getInstance()->getMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.article')));
+ }
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ $this->readInteger('categoryID');
+ if (ArticleCategory::getCategory($this->parameters['categoryID']) === null) {
+ throw new UserInputException('categoryID');
+ }
+ }
+
+ /**
+ * Sets the category of articles.
+ */
+ public function setCategory() {
+ foreach ($this->getObjects() as $articleEditor) {
+ $articleEditor->update(['categoryID' => $this->parameters['categoryID']]);
+ }
+
+ $this->unmarkItems();
+ }
+
+ /**
+ * Validates the `publish` action.
+ *
+ * @throws UserInputException
+ */
+ public function validatePublish() {
+ WCF::getSession()->checkPermissions(['admin.content.article.canManageArticle']);
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ foreach ($this->getObjects() as $article) {
+ if ($article->publicationStatus == Article::PUBLISHED) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+ }
+
+ /**
+ * Publishes articles.
+ */
+ public function publish() {
+ foreach ($this->getObjects() as $articleEditor) {
+ $articleEditor->update([
+ 'time' => TIME_NOW,
+ 'publicationStatus' => Article::PUBLISHED,
+ 'publicationDate' => 0
+ ]);
+ }
+
+ $this->unmarkItems();
+ }
+
+ /**
+ * Validates the `unpublish` action.
+ *
+ * @throws UserInputException
+ */
+ public function validateUnpublish() {
+ WCF::getSession()->checkPermissions(['admin.content.article.canManageArticle']);
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ foreach ($this->getObjects() as $article) {
+ if ($article->publicationStatus != Article::PUBLISHED) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+ }
+
+ /**
+ * Unpublishes articles.
+ */
+ public function unpublish() {
+ foreach ($this->getObjects() as $articleEditor) {
+ $articleEditor->update(['publicationStatus' => Article::UNPUBLISHED]);
+ }
+
+ $this->unmarkItems();
+ }
+
+ /**
+ * Unmarks articles.
+ *
+ * @param integer[] $articleIDs
+ */
+ protected function unmarkItems(array $articleIDs = []) {
+ if (empty($articleIDs)) {
+ foreach ($this->getObjects() as $article) {
+ $articleIDs[] = $article->articleID;
+ }
+ }
+
+ if (!empty($articleIDs)) {
+ ClipboardHandler::getInstance()->unmark($articleIDs, ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.article'));
+ }
+ }
}
--- /dev/null
+<?php
+namespace wcf\system\clipboard\action;
+use wcf\data\article\Article;
+use wcf\data\article\ArticleAction;
+use wcf\data\category\CategoryNodeTree;
+use wcf\data\clipboard\action\ClipboardAction;
+use wcf\system\WCF;
+
+/**
+ * Clipboard action implementation for articles.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Clipboard\Action
+ * @since 3.1
+ */
+class ArticleClipboardAction extends AbstractClipboardAction {
+ /**
+ * @inheritDoc
+ */
+ protected $actionClassActions = [
+ 'delete',
+ 'publish',
+ 'restore',
+ 'trash',
+ 'unpublish'
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ protected $supportedActions = [
+ 'delete',
+ 'publish',
+ 'restore',
+ 'setCategory',
+ 'trash',
+ 'unpublish'
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(array $objects, ClipboardAction $action) {
+ $item = parent::execute($objects, $action);
+
+ if ($item === null) {
+ return null;
+ }
+
+ // handle actions
+ switch ($action->actionName) {
+ case 'delete':
+ $item->addInternalData('confirmMessage', WCF::getLanguage()->getDynamicVariable('wcf.clipboard.item.com.woltlab.wcf.article.delete.confirmMessage', [
+ 'count' => $item->getCount()
+ ]));
+ break;
+
+ case 'setCategory':
+ $item->addInternalData('template', WCF::getTPL()->fetch('articleCategoryDialog', 'wcf', [
+ 'categoryNodeList' => (new CategoryNodeTree('com.woltlab.wcf.article.category'))->getIterator()
+ ]));
+ break;
+
+ case 'trash':
+ $item->addInternalData('confirmMessage', WCF::getLanguage()->getDynamicVariable('wcf.clipboard.item.com.woltlab.wcf.article.trash.confirmMessage', [
+ 'count' => $item->getCount()
+ ]));
+ break;
+ }
+
+ return $item;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getClassName() {
+ return ArticleAction::class;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getTypeName() {
+ return 'com.woltlab.wcf.article';
+ }
+
+ /**
+ * Returns the ids of the articles that can be deleted.
+ *
+ * @return integer[]
+ */
+ public function validateDelete() {
+ if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
+ return [];
+ }
+
+ $objectIDs = [];
+
+ /** @var Article $article */
+ foreach ($this->objects as $article) {
+ if ($article->isDeleted) {
+ $objectIDs[] = $article->articleID;
+ }
+ }
+
+ return $objectIDs;
+ }
+
+ /**
+ * Returns the ids of the articles that can be published.
+ *
+ * @return integer[]
+ */
+ public function validatePublish() {
+ if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
+ return [];
+ }
+
+ $objectIDs = [];
+
+ /** @var Article $article */
+ foreach ($this->objects as $article) {
+ if ($article->publicationStatus == Article::UNPUBLISHED) {
+ $objectIDs[] = $article->articleID;
+ }
+ }
+
+ return $objectIDs;
+ }
+
+ /**
+ * Returns the ids of the articles that can be restored.
+ *
+ * @return integer[]
+ */
+ public function validateRestore() {
+ return $this->validateDelete();
+ }
+
+ /**
+ * Returns the ids of the articles whose category can be set.
+ *
+ * @return integer[]
+ */
+ public function validateSetCategory() {
+ if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
+ return [];
+ }
+
+ return array_keys($this->objects);
+ }
+
+ /**
+ * Returns the ids of the articles that can be trashed.
+ *
+ * @return integer[]
+ */
+ public function validateTrash() {
+ if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
+ return [];
+ }
+
+ $objectIDs = [];
+
+ /** @var Article $article */
+ foreach ($this->objects as $article) {
+ if (!$article->isDeleted) {
+ $objectIDs[] = $article->articleID;
+ }
+ }
+
+ return $objectIDs;
+ }
+
+ /**
+ * Returns the ids of the articles that can be unpublished.
+ *
+ * @return integer[]
+ */
+ public function validateUnpublish() {
+ if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
+ return [];
+ }
+
+ $objectIDs = [];
+
+ /** @var Article $article */
+ foreach ($this->objects as $article) {
+ if ($article->publicationStatus == Article::PUBLISHED) {
+ $objectIDs[] = $article->articleID;
+ }
+ }
+
+ return $objectIDs;
+ }
+}
<item name="wcf.acp.article.publicationStatus.published"><![CDATA[Veröffentlicht]]></item>
<item name="wcf.acp.article.publicationStatus.delayed"><![CDATA[Zeitgesteuerte Veröffentlichung]]></item>
<item name="wcf.acp.article.restore.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} {if $isArticleEdit|empty}den Artikel <span class="confirmationObject">{$article->getTitle()}</span>{else}diesen Artikel{/if} wirklich wiederherstellen?]]></item>
+ <item name="wcf.acp.article.setCategory"><![CDATA[Kategorie ändern]]></item>
<item name="wcf.acp.article.teaser"><![CDATA[Einleitungstext]]></item>
<item name="wcf.acp.article.trash.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} {if $isArticleEdit|empty}den Artikel <span class="confirmationObject">{$article->getTitle()}</span>{else}diesen Artikel{/if} wirklich in den Papierkorb verschieben?]]></item>
<item name="wcf.acp.article.trash.notice"><![CDATA[Dieser Artikel befindet sich im Papierkorb und wird gegenwärtig nicht angezeigt.]]></item>
<category name="wcf.clipboard">
<item name="wcf.clipboard.item.unmarkAll"><![CDATA[Demarkieren]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete"><![CDATA[Löschen ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete.confirmMessage"><![CDATA[{if $count == 1}Eine{else}{#$count}{/if} Artikel löschen?]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.publish"><![CDATA[Veröffentlichen ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.restore"><![CDATA[Wiederherstellen ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.setCategory"><![CDATA[Kategorie ändern ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.trash"><![CDATA[Löschen ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.trash.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} {if $count == 1}einen{else}{#$count}{/if} Artikel wirklich in den Papierkorb verschieben?]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.unpublish"><![CDATA[Veröffentlichung zurückziehen ({#$count})]]></item>
+
<item name="wcf.clipboard.item.com.woltlab.wcf.media.delete"><![CDATA[Löschen ({#$count})]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.media.delete.confirmMessage"><![CDATA[{if $count == 1}Eine Datei{else}{#$count} Dateien{/if} löschen?]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.media.insert"><![CDATA[Einfügen ({#$count})]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.user.sendNewPassword"><![CDATA[Neues Kennwort senden ({#$count})]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.user.sendNewPassword.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} wirklich {if $count == 1}einem Benutzer{else}{#$count} Benutzern{/if} ein neues Kennwort zusenden?]]></item>
+ <item name="wcf.clipboard.label.com.woltlab.wcf.article.marked"><![CDATA[{if $count == 1}Ein{else}{#$count}{/if} Artikel]]></item>
<item name="wcf.clipboard.label.com.woltlab.wcf.media.marked"><![CDATA[{if $count == 1}Eine Datei{else}{#$count} Dateien{/if}]]></item>
<item name="wcf.clipboard.label.com.woltlab.wcf.tag.marked"><![CDATA[{if $count == 1}Ein Tag{else}{#$count} Tags{/if}]]></item>
<item name="wcf.clipboard.label.com.woltlab.wcf.user.marked"><![CDATA[{if $count == 1}Ein{else}{#$count}{/if} Benutzer]]></item>
<item name="wcf.acp.article.publicationStatus.published"><![CDATA[Published]]></item>
<item name="wcf.acp.article.publicationStatus.delayed"><![CDATA[Delayed publishing]]></item>
<item name="wcf.acp.article.restore.confirmMessage"><![CDATA[Do you really want to restore {if $isArticleEdit|empty}the article <span class="confirmationObject">{$article->getTitle()}</span>{else}this article{/if}?]]></item>
+ <item name="wcf.acp.article.setCategory"><![CDATA[Set Category]]></item>
<item name="wcf.acp.article.teaser"><![CDATA[Teaser]]></item>
<item name="wcf.acp.article.trash.confirmMessage"><![CDATA[Do you really want to move {if $isArticleEdit|empty}the article <span class="confirmationObject">{$article->getTitle()}</span>{else}this article{/if} to the trash bin?]]></item>
<item name="wcf.acp.article.trash.notice"><![CDATA[This article has been moved to the trash bin and is currently hidden from view.]]></item>
<category name="wcf.clipboard">
<item name="wcf.clipboard.item.unmarkAll"><![CDATA[Unmark All]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete"><![CDATA[Delete ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete.confirmMessage"><![CDATA[Do you really want to delete {#$count} article{if $count != 1}s{/if}?]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.publish"><![CDATA[Publish ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.restore"><![CDATA[Restore ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.setCategory"><![CDATA[Set Category ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.trash"><![CDATA[Move to Trash ({#$count})]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.trash.confirmMessage"><![CDATA[Do you really want to move {#$count} article{if $count != 1}s{/if} to the trash bin?]]></item>
+ <item name="wcf.clipboard.item.com.woltlab.wcf.article.unpublish"><![CDATA[Withdraw Publication ({#$count})]]></item>
+
<item name="wcf.clipboard.item.com.woltlab.wcf.media.delete"><![CDATA[Delete ({#$count})]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.media.delete.confirmMessage"><![CDATA[Do you really want to delete {#$count} file{if $count != 1}s{/if}?]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.media.insert"><![CDATA[Insert ({#$count})]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.user.sendNewPassword"><![CDATA[Send New Password ({#$count})]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.user.sendNewPassword.confirmMessage"><![CDATA[Do you really want to send a new password to {#$count} user{if $count != 1}s{/if}?]]></item>
+ <item name="wcf.clipboard.label.com.woltlab.wcf.article.marked"><![CDATA[{if $count == 1}One Article{else}{#$count} Articles{/if}]]></item>
<item name="wcf.clipboard.label.com.woltlab.wcf.media.marked"><![CDATA[{if $count == 1}One File{else}{#$count} Files{/if}]]></item>
<item name="wcf.clipboard.label.com.woltlab.wcf.tag.marked"><![CDATA[{if $count == 1}One Tag{else}{#$count} Tags{/if}]]></item>
<item name="wcf.clipboard.label.com.woltlab.wcf.user.marked"><![CDATA[{if $count == 1}One User{else}{#$count} Users{/if}]]></item>