Add clipboard support for tags
authorMatthias Schmidt <gravatronics@live.com>
Mon, 25 May 2015 06:40:36 +0000 (08:40 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Mon, 25 May 2015 06:40:36 +0000 (08:40 +0200)
CHANGELOG.md
com.woltlab.wcf/clipboardAction.xml
com.woltlab.wcf/objectType.xml
wcfsetup/install/files/acp/js/WCF.ACP.js
wcfsetup/install/files/acp/templates/tagList.tpl
wcfsetup/install/files/acp/templates/userList.tpl
wcfsetup/install/files/lib/acp/page/TagListPage.class.php
wcfsetup/install/files/lib/data/tag/TagAction.class.php
wcfsetup/install/files/lib/data/tag/TagEditor.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 3fb56b90549d7704108e9daa218cadd8dae32caa..a0db772df36aea81ce1c47c82043e130e5fd97c4 100644 (file)
@@ -4,6 +4,7 @@
 
 ### 2.2.0 Alpha 1 (XXXX-YY-ZZ)
 
+* Clipboard support for tags in ACP ("delete" and "set as synonyms").
 * `wcf\data\user\UserProfileCache` for caching user profiles during runtime.
 * instruction file name for most PIPs has default value provided by `wcf\system\package\plugin\IPackageInstallationPlugin::getDefaultFilename()`.
 * `options` support for cronjobs.
index 2addad9f275e1c1fd9639fdba62956c7f920ccc9..c91192b202f058f031b93c30130472db96dd4adf 100644 (file)
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/clipboardAction.xsd">
        <import>
+               <!-- com.woltlab.wcf.user -->
                <action name="assignToGroup">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>1</showorder>
@@ -8,7 +9,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="delete">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>2</showorder>
@@ -16,7 +16,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="ban">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>3</showorder>
@@ -24,7 +23,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="merge">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>4</showorder>
@@ -32,7 +30,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="enable">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>5</showorder>
@@ -40,7 +37,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="sendMail">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>6</showorder>
@@ -48,7 +44,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="exportMailAddress">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>7</showorder>
@@ -56,7 +51,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="sendNewPassword">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserClipboardAction]]></actionclassname>
                        <showorder>8</showorder>
@@ -64,7 +58,6 @@
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
-               
                <action name="revertContentChanges">
                        <actionclassname><![CDATA[wcf\system\clipboard\action\UserContentClipboardAction]]></actionclassname>
                        <showorder>9</showorder>
                                <page><![CDATA[wcf\acp\page\UserListPage]]></page>
                        </pages>
                </action>
+               <!-- /com.woltlab.wcf.user -->
+               
+               <!-- com.woltlab.wcf.tag -->
+               <action name="delete">
+                       <actionclassname><![CDATA[wcf\system\clipboard\action\TagClipboardAction]]></actionclassname>
+                       <showorder>1</showorder>
+                       <pages>
+                               <page><![CDATA[wcf\acp\page\TagListPage]]></page>
+                       </pages>
+               </action>
+               <action name="setAsSynonyms">
+                       <actionclassname><![CDATA[wcf\system\clipboard\action\TagClipboardAction]]></actionclassname>
+                       <showorder>2</showorder>
+                       <pages>
+                               <page><![CDATA[wcf\acp\page\TagListPage]]></page>
+                       </pages>
+               </action>
+               <!-- com.woltlab.wcf.tag -->
        </import>
 </data>
index 83d5c75f2d2192fa92a21fb9abd298957c792cf3..0b2ef40b9c1496f4a72cd0a6a91f7305e069911b 100644 (file)
@@ -1,11 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/objectType.xsd">
        <import>
+               <!-- clipboard items -->
                <type>
                        <name>com.woltlab.wcf.user</name>
                        <definitionname>com.woltlab.wcf.clipboardItem</definitionname>
                        <listclassname><![CDATA[wcf\data\user\UserList]]></listclassname>
                </type>
+               <type>
+                       <name>com.woltlab.wcf.tag</name>
+                       <definitionname>com.woltlab.wcf.clipboardItem</definitionname>
+                       <listclassname><![CDATA[wcf\data\tag\TagList]]></listclassname>
+               </type>
+               <!-- /clipboard items -->
                
                <type>
                        <name>com.woltlab.wcf.collapsibleSidebar</name>
index a1224ef2405525af2e595de4ad8cfd36a59fae53..6f70b2674e45751c6751525a390f2d05327a9ea5 100644 (file)
@@ -2754,3 +2754,93 @@ WCF.ACP.Ad.LocationHandler = Class.extend({
                }
        }
 });
+
+/**
+ * Initialize WCF.ACP.Tag namespace.
+ */
+WCF.ACP.Tag = { };
+
+/**
+ * Handles setting tags as synonyms of another tag by clipboard.
+ */
+WCF.ACP.Tag.SetAsSynonymsHandler = Class.extend({
+       /**
+        * dialog to select the "main" tag
+        * @var jQuery
+        */
+       _dialog: null,
+       
+       /**
+        * ids of the selected tags
+        * @var array<integer>
+        */
+       _objectIDs: [ ],
+       
+       /**
+        * Initializes the SetAsSynonymsHandler object.
+        * 
+        * @param       array<integer>          objectIDs
+        */
+       init: function() {
+               // bind listener
+               $('.jsClipboardEditor').each($.proxy(function(index, container) {
+                       var $container = $(container);
+                       var $types = eval($container.data('types'));
+                       if (WCF.inArray('com.woltlab.wcf.tag', $types)) {
+                               $container.on('clipboardAction', $.proxy(this._execute, this));
+                               return false;
+                       }
+               }, this));
+       },
+       
+       /**
+        * Handles clipboard actions.
+        * 
+        * @param       object          event
+        * @param       string          type
+        * @param       string          actionName
+        * @param       object          parameters
+        */
+       _execute: function(event, type, actionName, parameters) {
+               if (type !== 'com.woltlab.wcf.tag' || actionName !== 'com.woltlab.wcf.tag.setAsSynonyms') {
+                       return;
+               }
+               
+               this._objectIDs = parameters.objectIDs;
+               if (this._dialog === null) {
+                       this._dialog = $('<div id="setAsSynonymsDialog" />').hide().appendTo(document.body);
+                       this._dialog.wcfDialog({
+                               closable: false,
+                               title: WCF.Language.get('wcf.acp.tag.setAsSynonyms')
+                       });
+               }
+               
+               this._dialog.html(parameters.template);
+               $button = this._dialog.find('button[data-type="submit"]').disable().click($.proxy(this._submit, this));
+               this._dialog.find('input[type=radio]').change(function() { $button.enable(); });
+       },
+       
+       /**
+        * Saves the tags as synonyms.
+        */
+       _submit: function() {
+               new WCF.Action.Proxy({
+                       autoSend: true,
+                       data: {
+                               actionName: 'setAsSynonyms',
+                               className: 'wcf\\data\\tag\\TagAction',
+                               objectIDs: this._objectIDs,
+                               parameters: {
+                                       tagID: this._dialog.find('input[name="tagID"]:checked').val()
+                               }
+                       },
+                       success: $.proxy(function() {
+                               this._dialog.wcfDialog('close');
+                               
+                               new WCF.System.Notification().show(function() {
+                                       window.location.reload();
+                               });
+                       }, this)
+               });
+       }
+});
index a92742f903f69718f9a5e8b568be375bf7f317e5..f6620eb8d081010c72e9329c91b462bd39caf372 100644 (file)
@@ -3,7 +3,15 @@
 <script data-relocate="true">
        //<![CDATA[
        $(function() {
-               new WCF.Action.Delete('wcf\\data\\tag\\TagAction', '.jsTagRow');
+               WCF.Language.add('wcf.acp.tag.setAsSynonyms', '{lang}wcf.acp.tag.setAsSynonyms{/lang}');
+               
+               WCF.Clipboard.init('wcf\\acp\\page\\TagListPage', {@$hasMarkedItems}, {
+                       'com.woltlab.wcf.tag': {
+                               'delete': new WCF.Action.Delete('wcf\\data\\tag\\TagAction', '.jsTagRow')
+                       }
+               });
+               
+               new WCF.ACP.Tag.SetAsSynonymsHandler();
        });
        //]]>
 </script>
@@ -43,7 +51,7 @@
        <nav>
                <ul>
                        <li><a href="{link controller='TagAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.tag.add{/lang}</span></a></li>
-                               
+                       
                        {event name='contentNavigationButtonsTop'}
                </ul>
        </nav>
                        <h2>{lang}wcf.acp.tag.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
                </header>
                
-               <table class="table">
+               <table data-type="com.woltlab.wcf.tag" class="table jsClipboardContainer">
                        <thead>
                                <tr>
+                                       <th class="columnMark"><label><input type="checkbox" class="jsClipboardMarkAll" /></label></th>
                                        <th class="columnID columnTagID{if $sortField == 'tagID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='TagList'}pageNo={@$pageNo}&sortField=tagID&sortOrder={if $sortField == 'tagID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}&search={@$search|rawurlencode}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
                                        <th class="columnTitle columnName{if $sortField == 'name'} active {@$sortOrder}{/if}"><a href="{link controller='TagList'}pageNo={@$pageNo}&sortField=name&sortOrder={if $sortField == 'name' && $sortOrder == 'ASC'}DESC{else}ASC{/if}&search={@$search|rawurlencode}{/link}">{lang}wcf.acp.tag.name{/lang}</a></th>
                                        <th class="columnDigits columnUsageCount{if $sortField == 'usageCount'} active {@$sortOrder}{/if}"><a href="{link controller='TagList'}pageNo={@$pageNo}&sortField=usageCount&sortOrder={if $sortField == 'usageCount' && $sortOrder == 'ASC'}DESC{else}ASC{/if}&search={@$search|rawurlencode}{/link}">{lang}wcf.acp.tag.usageCount{/lang}</a></th>
@@ -70,7 +79,8 @@
                        
                        <tbody>
                                {foreach from=$objects item=tag}
-                                       <tr class="jsTagRow">
+                                       <tr class="jsTagRow jsClipboardObject">
+                                               <td class="columnMark"><input type="checkbox" class="jsClipboardItem" data-object-id="{@$tag->tagID}" /></td>
                                                <td class="columnIcon">
                                                        <a href="{link controller='TagEdit' object=$tag}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
                                                        <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$tag->tagID}" data-confirm-message="{lang}wcf.acp.tag.delete.sure{/lang}"></span>
                <nav>
                        <ul>
                                <li><a href="{link controller='TagAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.tag.add{/lang}</span></a></li>
-                                       
+                               
                                {event name='contentNavigationButtonsBottom'}
                        </ul>
                </nav>
+               
+               <nav class="jsClipboardEditor" data-types="[ 'com.woltlab.wcf.tag' ]"></nav>
        </div>
 {else}
        <p class="info">{lang}wcf.global.noItems{/lang}</p>
index b82943671787d15123abe65d8fa85e1df0a04d46..5c147ac9c0e0b427da4daeb970892049b750ee79 100644 (file)
                        </tbody>
                </table>
        </div>
-               
+       
        <div class="contentNavigation">
                {@$pagesLinks}
                
index f404c918f5fcfe3b76ea4aa377954a83c049f37d..0e9a581bee11290f45cd03bfcd593ed09c947c37 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\acp\page;
 use wcf\page\SortablePage;
+use wcf\system\clipboard\ClipboardHandler;
 use wcf\system\WCF;
 use wcf\util\StringUtil;
 
@@ -58,6 +59,7 @@ class TagListPage extends SortablePage {
                parent::assignVariables();
                
                WCF::getTPL()->assign(array(
+                       'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.tag')),
                        'search' => $this->search
                ));
        }
index d1fb8ea79142b9fdde840023db5f943cb093bc1f..43c61e23eca1d4865c06ac6561f9fd7dba490f8c 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\data\tag;
 use wcf\data\AbstractDatabaseObjectAction;
 use wcf\data\ISearchAction;
+use wcf\system\clipboard\ClipboardHandler;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
@@ -42,6 +43,12 @@ class TagAction extends AbstractDatabaseObjectAction implements ISearchAction {
         */
        protected $requireACP = array('delete', 'update');
        
+       /**
+        * tag for which other tags will be used as synonyms
+        * @var \wcf\data\tag\TagEditor
+        */
+       public $tagEditor = null;
+       
        /**
         * @see \wcf\data\ISearchAction::validateGetSearchResultList()
         */
@@ -84,4 +91,68 @@ class TagAction extends AbstractDatabaseObjectAction implements ISearchAction {
                
                return $list;
        }
+       
+       /**
+        * @see \wcf\data\IDeleteAction::delete()
+        */
+       public function delete() {
+               $returnValue = parent::delete();
+               
+               $this->unmarkItems();
+               
+               return $returnValue;
+       }
+       
+       /**
+        * Validates the 'setAsSynonyms' action.
+        */
+       public function validateSetAsSynonyms() {
+               WCF::getSession()->checkPermissions([ 'admin.content.tag.canManageTag' ]);
+               if (empty($this->objects)) {
+                       $this->readObjects();
+                       
+                       if (count($this->objects) < 2) {
+                               throw new UserInputException('objectIDs');
+                       }
+               }
+               
+               $this->readInteger('tagID');
+               $this->tagEditor = new TagEditor(new Tag($this->parameters['tagID']));
+               if (!$this->tagEditor->tagID) {
+                       throw new UserInputException('tagID');
+               }
+       }
+       
+       /**
+        * Sets a number of tags as a synonyms of another tag.
+        */
+       public function setAsSynonyms() {
+               // the "main" tag may not be a synonym itself
+               if ($this->tagEditor->synonymFor) {
+                       $this->tagEditor->update([
+                               'synonymFor' => null
+                       ]);
+               }
+               
+               foreach ($this->objects as $tagEditor) {
+                       $this->tagEditor->addSynonym($tagEditor->getDecoratedObject());
+               }
+               
+               $this->unmarkItems();
+       }
+       
+       /**
+        * Unmarks tags.
+        * 
+        * @param       array<integer>          $tagIDs
+        */
+       protected function unmarkItems(array $tagIDs = array()) {
+               if (empty($tagIDs)) {
+                       $tagIDs = $this->objectIDs;
+               }
+               
+               if (!empty($tagIDs)) {
+                       ClipboardHandler::getInstance()->unmark($tagIDs, ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.tag'));
+               }
+       }
 }
index 4c3a9be330020717798ef26e22634ae80030426e..64ccc4f065183529685fec48fc36cc6647c7f769 100644 (file)
@@ -26,7 +26,7 @@ class TagEditor extends DatabaseObjectEditor {
         */
        public function addSynonym(Tag $synonym) {
                if ($synonym->tagID == $this->tagID) return;
-
+               
                // assign all associations for the synonym with this tag
                $sql = "UPDATE IGNORE   wcf".WCF_N."_tag_to_object
                        SET             tagID = ?
index 4a373f56d8ced36351d892a466bc1234c6f507d9..107fb5ddcb8357fdda870d7b3f89115f94791fdb 100644 (file)
@@ -1505,6 +1505,8 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.tag.synonymFor"><![CDATA[Synonym für]]></item>
                <item name="wcf.acp.tag.usageCount"><![CDATA[Verwendungen]]></item>
                <item name="wcf.acp.tag.error.name.duplicate"><![CDATA[Ein Tag mit diesem Namen existiert bereits.]]></item>
+               <item name="wcf.acp.tag.setAsSynonyms"><![CDATA[Als Synonyme verwenden]]></item>
+               <item name="wcf.acp.tag.setAsSynonyms.description"><![CDATA[Wählen Sie einen Tag aus. Die übrigen Tags werden als Synonyme des ausgewählten Tags verwendet.]]></item>
        </category>
        
        <category name="wcf.acp.template">
@@ -1880,18 +1882,25 @@ Fehler sind beispielsweise:
        
        <category name="wcf.clipboard">
                <item name="wcf.clipboard.item.unmarkAll"><![CDATA[Demarkieren]]></item>
+               
+               <item name="wcf.clipboard.item.com.woltlab.wcf.tag.delete"><![CDATA[Löschen ({#$count})]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.tag.delete.confirmMessage"><![CDATA[{if $count == 1}Einen Tag{else}{#$count} Tag{/if} löschen?]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.tag.setAsSynonyms"><![CDATA[Als Synonyme verwenden ({#$count})]]></item>
+               
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.assignToGroup"><![CDATA[Benutzergruppe zuweisen]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.ban"><![CDATA[Sperren ({#$count})]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.delete"><![CDATA[Löschen ({#$count})]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.delete.confirmMessage"><![CDATA[{if $count == 1}Einen{else}{#$count}{/if} Benutzer löschen?]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.user.enable"><![CDATA[Aktivieren]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.exportMailAddress"><![CDATA[E-Mail-Adressen exportieren]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.user.merge"><![CDATA[Zusammenfügen]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.user.revertContentChanges"><![CDATA[Änderungen an Inhalten zurücksetzen]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.sendMail"><![CDATA[E-Mail senden]]></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[Wollen Sie wirklich {if $count == 1}einem{else}{#$count}{/if} Benutzer ein neues Kennwort zusenden?]]></item>
+               
+               <item name="wcf.clipboard.label.com.woltlab.wcf.tag.marked"><![CDATA[{if $count == 1}Ein Tag{else}{#$count} Tags{/if} markiert]]></item>
                <item name="wcf.clipboard.label.com.woltlab.wcf.user.marked"><![CDATA[{if $count == 1}Ein{else}{#$count}{/if} Benutzer markiert]]></item>
-               <item name="wcf.clipboard.item.com.woltlab.wcf.user.merge"><![CDATA[Zusammenfügen]]></item>
-               <item name="wcf.clipboard.item.com.woltlab.wcf.user.revertContentChanges"><![CDATA[Änderungen an Inhalten zurücksetzen]]></item>
-               <item name="wcf.clipboard.item.com.woltlab.wcf.user.enable"><![CDATA[Aktivieren]]></item>
        </category>
        
        <category name="wcf.comment">
index 79f0214529d46aff344144e61af3e7236a1aa615..8422b42529632c7742ebb527dca2c9164af0252d 100644 (file)
@@ -1504,6 +1504,8 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.tag.synonymFor"><![CDATA[Synonym for]]></item>
                <item name="wcf.acp.tag.usageCount"><![CDATA[Usages]]></item>
                <item name="wcf.acp.tag.error.name.duplicate"><![CDATA[This tag name is already in use.]]></item>
+               <item name="wcf.acp.tag.setAsSynonyms"><![CDATA[Set as Synonyms]]></item>
+               <item name="wcf.acp.tag.setAsSynonyms.description"><![CDATA[Select one of the tags. The other tags will be set as synonyms of the selected tag.]]></item>
        </category>
        
        <category name="wcf.acp.template">
@@ -1879,18 +1881,25 @@ Errors are:
        
        <category name="wcf.clipboard">
                <item name="wcf.clipboard.item.unmarkAll"><![CDATA[Unmark All]]></item>
+               
+               <item name="wcf.clipboard.item.com.woltlab.wcf.tag.delete"><![CDATA[Delete ({#$count})]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.tag.delete.confirmMessage"><![CDATA[Do you really want to delete {#$count} tag{if $count != 1}s{/if}?]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.tag.setAsSynonyms"><![CDATA[Set as Synonyms ({#$count})]]></item>
+               
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.assignToGroup"><![CDATA[Assign to User Group]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.ban"><![CDATA[Ban ({#$count})]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.delete"><![CDATA[Delete ({#$count})]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.delete.confirmMessage"><![CDATA[Do you really want to delete {#$count} user{if $count != 1}s{/if}?]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.user.enable"><![CDATA[Approve]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.exportMailAddress"><![CDATA[Export Email addresses]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.user.merge"><![CDATA[Merge]]></item>
+               <item name="wcf.clipboard.item.com.woltlab.wcf.user.revertContentChanges"><![CDATA[Revert Changes To Contents]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.user.sendMail"><![CDATA[Send Email]]></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.tag.marked"><![CDATA[{#$count} Tag{if $count != 1}s{/if} marked]]></item>
                <item name="wcf.clipboard.label.com.woltlab.wcf.user.marked"><![CDATA[{#$count} User{if $count != 1}s{/if} marked]]></item>
-               <item name="wcf.clipboard.item.com.woltlab.wcf.user.merge"><![CDATA[Merge]]></item>
-               <item name="wcf.clipboard.item.com.woltlab.wcf.user.revertContentChanges"><![CDATA[Revert Changes To Contents]]></item>
-               <item name="wcf.clipboard.item.com.woltlab.wcf.user.enable"><![CDATA[Approve]]></item>
        </category>
        
        <category name="wcf.comment">