{/if}
<form method="post" action="{link controller='Search'}{/link}">
- <div class="section">
- <dl{if $errorField == 'q'} class="formError"{/if}>
- <dt><label for="searchTerm">{lang}wcf.search.query{/lang}</label></dt>
- <dd>
- <input type="text" id="searchTerm" name="q" value="{$query}" class="long" maxlength="255" autofocus>
- {if $errorField == 'q'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.search.query.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- <label><input type="checkbox" name="subjectOnly" value="1"{if $subjectOnly == 1} checked{/if}> {lang}wcf.search.subjectOnly{/lang}</label>
- {event name='queryOptions'}
+ <div class="section tabMenuContainer staticTabMenuContainer">
+ <nav class="tabMenu">
+ <ul>
+ <li class="active"><a href="{link controller='Search'}{/link}">{lang}wcf.search.type.keywords{/lang}</a></li>
+ <li><a href="{link controller='TagSearch'}{/link}">{lang}wcf.search.type.tags{/lang}</a></li>
- <small>{lang}wcf.search.query.description{/lang}</small>
- </dd>
- </dl>
-
- <dl>
- <dt><label for="searchAuthor">{lang}wcf.search.author{/lang}</label></dt>
- <dd>
- <input type="text" id="searchAuthor" name="username" value="{$username}" class="medium" maxlength="255" autocomplete="off">
- <label><input type="checkbox" name="nameExactly" value="1"{if $nameExactly == 1} checked{/if}> {lang}wcf.search.matchExactly{/lang}</label>
- {event name='authorOptions'}
- </dd>
- </dl>
-
- <dl>
- <dt><label for="startDate">{lang}wcf.search.period{/lang}</label></dt>
- <dd>
- <input type="date" id="startDate" name="startDate" value="{$startDate}" data-placeholder="{lang}wcf.date.period.start{/lang}">
- <input type="date" id="endDate" name="endDate" value="{$endDate}" data-placeholder="{lang}wcf.date.period.end{/lang}">
- {event name='periodOptions'}
- </dd>
- </dl>
+ {event name='tabMenuTabs'}
+ </ul>
+ </nav>
- <dl>
- <dt><label for="sortField">{lang}wcf.search.sortBy{/lang}</label></dt>
- <dd>
- <select id="sortField" name="sortField">
- <option value="relevance"{if $sortField == 'relevance'} selected{/if}>{lang}wcf.search.sortBy.relevance{/lang}</option>
- <option value="subject"{if $sortField == 'subject'} selected{/if}>{lang}wcf.global.subject{/lang}</option>
- <option value="time"{if $sortField == 'time'} selected{/if}>{lang}wcf.search.sortBy.time{/lang}</option>
- <option value="username"{if $sortField == 'username'} selected{/if}>{lang}wcf.search.sortBy.username{/lang}</option>
- </select>
+ <div class="tabMenuContent">
+ <div class="section">
+ <dl{if $errorField == 'q'} class="formError"{/if}>
+ <dt><label for="searchTerm">{lang}wcf.search.query{/lang}</label></dt>
+ <dd>
+ <input type="text" id="searchTerm" name="q" value="{$query}" class="long" maxlength="255" autofocus>
+ {if $errorField == 'q'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.search.query.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <label><input type="checkbox" name="subjectOnly" value="1"{if $subjectOnly == 1} checked{/if}> {lang}wcf.search.subjectOnly{/lang}</label>
+ {event name='queryOptions'}
+
+ <small>{lang}wcf.search.query.description{/lang}</small>
+ </dd>
+ </dl>
- <select name="sortOrder">
- <option value="ASC"{if $sortOrder == 'ASC'} selected{/if}>{lang}wcf.global.sortOrder.ascending{/lang}</option>
- <option value="DESC"{if $sortOrder == 'DESC'} selected{/if}>{lang}wcf.global.sortOrder.descending{/lang}</option>
- </select>
+ <dl>
+ <dt><label for="searchAuthor">{lang}wcf.search.author{/lang}</label></dt>
+ <dd>
+ <input type="text" id="searchAuthor" name="username" value="{$username}" class="medium" maxlength="255" autocomplete="off">
+ <label><input type="checkbox" name="nameExactly" value="1"{if $nameExactly == 1} checked{/if}> {lang}wcf.search.matchExactly{/lang}</label>
+ {event name='authorOptions'}
+ </dd>
+ </dl>
- {event name='displayOptions'}
- </dd>
- </dl>
-
- {event name='generalFields'}
-
- <dl>
- <dt>{lang}wcf.search.type{/lang}</dt>
- <dd class="floated">
- {foreach from=$objectTypes key=objectTypeName item=objectType}
- {if $objectType->isAccessible()}
- <label><input id="{@'.'|str_replace:'_':$objectTypeName}" type="checkbox" name="types[]" value="{@$objectTypeName}"{if $objectTypeName|in_array:$selectedObjectTypes} checked{/if}> {lang}wcf.search.type.{@$objectTypeName}{/lang}</label>
- {/if}
- {/foreach}
- </dd>
- </dl>
- </div>
-
- {event name='sections'}
-
- {foreach from=$objectTypes key=objectTypeName item=objectType}
- {if $objectType->isAccessible() && $objectType->getFormTemplateName()}
- {assign var='__jsID' value='.'|str_replace:'_':$objectTypeName}
- <section class="section" id="{@$__jsID}Form">
- <h2 class="sectionTitle">{lang}wcf.search.type.{@$objectTypeName}{/lang}</h2>
+ <dl>
+ <dt><label for="startDate">{lang}wcf.search.period{/lang}</label></dt>
+ <dd>
+ <input type="date" id="startDate" name="startDate" value="{$startDate}" data-placeholder="{lang}wcf.date.period.start{/lang}">
+ <input type="date" id="endDate" name="endDate" value="{$endDate}" data-placeholder="{lang}wcf.date.period.end{/lang}">
+ {event name='periodOptions'}
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="sortField">{lang}wcf.search.sortBy{/lang}</label></dt>
+ <dd>
+ <select id="sortField" name="sortField">
+ <option value="relevance"{if $sortField == 'relevance'} selected{/if}>{lang}wcf.search.sortBy.relevance{/lang}</option>
+ <option value="subject"{if $sortField == 'subject'} selected{/if}>{lang}wcf.global.subject{/lang}</option>
+ <option value="time"{if $sortField == 'time'} selected{/if}>{lang}wcf.search.sortBy.time{/lang}</option>
+ <option value="username"{if $sortField == 'username'} selected{/if}>{lang}wcf.search.sortBy.username{/lang}</option>
+ </select>
+
+ <select name="sortOrder">
+ <option value="ASC"{if $sortOrder == 'ASC'} selected{/if}>{lang}wcf.global.sortOrder.ascending{/lang}</option>
+ <option value="DESC"{if $sortOrder == 'DESC'} selected{/if}>{lang}wcf.global.sortOrder.descending{/lang}</option>
+ </select>
+
+ {event name='displayOptions'}
+ </dd>
+ </dl>
- {include file=$objectType->getFormTemplateName() application=$objectType->getApplication()}
+ {event name='generalFields'}
- <script data-relocate="true">
- $(function() {
- $('#{@$__jsID}').click(function() {
- if (this.checked) $('#{@$__jsID}Form').wcfFadeIn();
- else $('#{@$__jsID}Form').wcfFadeOut();
- });
- {if !$objectTypeName|in_array:$selectedObjectTypes}$('#{@$__jsID}Form').hide();{/if}
- });
- </script>
- </section>
- {/if}
- {/foreach}
-
- {include file='captcha' supportsAsyncCaptcha=true}
-
- <div class="formSubmit">
- <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
- {@SECURITY_TOKEN_INPUT_TAG}
+ <dl>
+ <dt>{lang}wcf.search.type{/lang}</dt>
+ <dd class="floated">
+ {foreach from=$objectTypes key=objectTypeName item=objectType}
+ {if $objectType->isAccessible()}
+ <label><input id="{@'.'|str_replace:'_':$objectTypeName}" type="checkbox" name="types[]" value="{@$objectTypeName}"{if $objectTypeName|in_array:$selectedObjectTypes} checked{/if}> {lang}wcf.search.type.{@$objectTypeName}{/lang}</label>
+ {/if}
+ {/foreach}
+ </dd>
+ </dl>
+ </div>
+
+ {event name='sections'}
+
+ {foreach from=$objectTypes key=objectTypeName item=objectType}
+ {if $objectType->isAccessible() && $objectType->getFormTemplateName()}
+ {assign var='__jsID' value='.'|str_replace:'_':$objectTypeName}
+ <section class="section" id="{@$__jsID}Form">
+ <h2 class="sectionTitle">{lang}wcf.search.type.{@$objectTypeName}{/lang}</h2>
+
+ {include file=$objectType->getFormTemplateName() application=$objectType->getApplication()}
+
+ <script data-relocate="true">
+ $(function() {
+ $('#{@$__jsID}').click(function() {
+ if (this.checked) $('#{@$__jsID}Form').wcfFadeIn();
+ else $('#{@$__jsID}Form').wcfFadeOut();
+ });
+ {if !$objectTypeName|in_array:$selectedObjectTypes}$('#{@$__jsID}Form').hide();{/if}
+ });
+ </script>
+ </section>
+ {/if}
+ {/foreach}
+
+ {include file='captcha' supportsAsyncCaptcha=true}
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+ </div>
</div>
</form>
--- /dev/null
+{include file='header' __disableAds=true}
+
+{include file='formError'}
+
+{if $errorMessage|isset}
+ <p class="error">{@$errorMessage}</p>
+{/if}
+
+<form method="post" action="{link controller='TagSearch'}{/link}">
+ <div class="section tabMenuContainer staticTabMenuContainer">
+ <nav class="tabMenu">
+ <ul>
+ <li><a href="{link controller='Search'}{/link}">{lang}wcf.search.type.keywords{/lang}</a></li>
+ <li class="active"><a href="{link controller='TagSearch'}{/link}">{lang}wcf.search.type.tags{/lang}</a></li>
+
+ {event name='tabMenuTabs'}
+ </ul>
+ </nav>
+
+ <div class="tabMenuContent">
+ <div class="section jsOnly">
+ {include file='messageFormMultilingualism'}
+
+ <dl>
+ <dt><label for="tagSearchInput">{lang}wcf.tagging.tags{/lang}</label></dt>
+ <dd>
+ <input id="tagSearchInput" type="text" value="" class="long">
+ <small>{lang}wcf.tagging.tags.description{/lang}</small>
+ </dd>
+ </dl>
+
+ <script data-relocate="true">
+ require(['WoltLabSuite/Core/Language/Chooser', 'WoltLabSuite/Core/Ui/ItemList'], function(LanguageChooser, UiItemList) {
+ UiItemList.init(
+ 'tagSearchInput',
+ [{if !$tagNames|empty}{implode from=$tagNames item=tagName}'{@$tagName|encodeJS}'{/implode}{/if}],
+ {
+ ajax: {
+ className: 'wcf\\data\\tag\\TagAction'
+ },
+ maxItems: {@SEARCH_MAX_COMBINED_TAGS},
+ restricted: true,
+ submitFieldName: 'tagNames[]'
+ }
+ );
+
+ var languageId = {@$languageID};
+ LanguageChooser.getChooser('languageID').callback = function (listItem) {
+ var newLanguageId = parseInt(elData(listItem, 'language-id'), 10);
+ if (newLanguageId !== languageId) {
+ languageId = newLanguageId;
+ }
+ };
+ });
+ </script>
+ </div>
+
+ {if !$tags|empty}
+ <section class="section">
+ <h2 class="sectionTitle">{lang}wcf.search.type.tags.popular{/lang}</h2>
+
+ {include file='tagCloudBox'}
+ </section>
+ {/if}
+
+ {event name='sections'}
+
+ {include file='captcha' supportsAsyncCaptcha=true}
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+ </div>
+ </div>
+</form>
+
+{include file='footer' __disableAds=true}
/**
* Creates a new CategoryArticleList object.
*
- * @param Tag $tag
+ * @param Tag|Tag[] $tag
*/
- public function __construct(Tag $tag) {
+ public function __construct($tag) {
parent::__construct();
$this->sqlOrderBy = 'article.time ' . ARTICLE_SORT_ORDER;
- $this->getConditionBuilder()->add("article.articleID IN (SELECT articleID FROM wcf".WCF_N."_article_content WHERE articleContentID IN (SELECT objectID FROM wcf".WCF_N."_tag_to_object WHERE objectTypeID = ? AND languageID = ? AND tagID = ?))", [TagEngine::getInstance()->getObjectTypeID('com.woltlab.wcf.article'), $tag->languageID, $tag->tagID]);
+
+ $innerSql = TagEngine::getInstance()->getSqlForObjectsByTags('com.woltlab.wcf.article', $tag);
+ $this->getConditionBuilder()->add("article.articleID IN (SELECT articleID FROM wcf".WCF_N."_article_content WHERE articleContentID IN (".$innerSql['sql']."))", [$innerSql['parameters']]);
}
}
--- /dev/null
+<?php
+namespace wcf\form;
+use wcf\data\language\Language;
+use wcf\data\tag\Tag;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\LanguageFactory;
+use wcf\system\request\LinkHandler;
+use wcf\system\tagging\TagCloud;
+use wcf\system\tagging\TagEngine;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+use wcf\util\HeaderUtil;
+
+/**
+ * Shows the tag search form.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Form
+ * @since 3.2
+ */
+class TagSearchForm extends AbstractCaptchaForm {
+ /**
+ * @var Language[]
+ */
+ public $availableContentLanguages = [];
+
+ /**
+ * @var int
+ */
+ public $languageID;
+
+ /**
+ * @var TagCloud
+ */
+ public $tagCloud;
+
+ /**
+ * @var string[]
+ */
+ public $tagNames;
+
+ /**
+ * @var Tag[]
+ */
+ public $tags;
+
+ /**
+ * @inheritDoc
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ $this->availableContentLanguages = LanguageFactory::getInstance()->getContentLanguages();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if (isset($_POST['languageID'])) $this->languageID = intval($_POST['languageID']);
+ if (isset($_POST['tagNames']) && is_array($_POST['tagNames'])) $this->tagNames = ArrayUtil::trim($_POST['tagNames']);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validate() {
+ parent::validate();
+
+ if ($this->languageID !== null) {
+ if (!in_array($this->languageID, LanguageFactory::getInstance()->getContentLanguageIDs())) {
+ throw new UserInputException('languageID');
+ }
+ }
+
+ $this->tags = TagEngine::getInstance()->getTagsByName($this->tagNames, $this->languageID);
+ if (empty($this->tags)) {
+ throw new UserInputException('tags');
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readData() {
+ if (empty($_POST)) {
+ $this->languageID = WCF::getLanguage()->languageID;
+ }
+
+ parent::readData();
+
+ $this->tagCloud = new TagCloud();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save() {
+ parent::save();
+
+ HeaderUtil::redirect(
+ LinkHandler::getInstance()->getLink('CombinedTagged', ['tagIDs' => array_keys($this->tags)]),
+ true,
+ true
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign([
+ 'availableContentLanguages' => $this->availableContentLanguages,
+ 'languageID' => $this->languageID ?: 0,
+ 'tags' => $this->tagCloud->getTags(),
+ 'tagNames' => $this->tagNames,
+ ]);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\page;
+use wcf\data\object\type\ObjectType;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\tag\Tag;
+use wcf\data\tag\TagList;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\tagging\TypedTagCloud;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the a list of tagged objects.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Page
+ */
+class CombinedTaggedPage extends TaggedPage {
+ public $tags = [];
+ public $tagIDs = [];
+
+ /**
+ * @inheritDoc
+ */
+ public function readParameters() {
+ MultipleLinkPage::readParameters();
+
+ if (isset($_GET['tagIDs']) && is_array($this->tagIDs)) $this->tagIDs = ArrayUtil::toIntegerArray($_GET['tagIDs']);
+ if (empty($this->tagIDs)) {
+ throw new IllegalLinkException();
+ }
+ else if (count($this->tagIDs) > SEARCH_MAX_COMBINED_TAGS) {
+ throw new PermissionDeniedException();
+ }
+
+ $tagList = new TagList();
+ $tagList->getConditionBuilder()->add('tagID IN (?)', [$this->tagIDs]);
+ $tagList->readObjects();
+
+ $this->tags = $tagList->getObjects();
+ if (empty($this->tags)) {
+ throw new IllegalLinkException();
+ }
+
+ $this->availableObjectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.tagging.taggableObject');
+ foreach ($this->availableObjectTypes as $key => $objectType) {
+ if (!$objectType->validateOptions() || !$objectType->validatePermissions()) {
+ unset($this->availableObjectTypes[$key]);
+ }
+ }
+
+ if (empty($this->availableObjectTypes)) {
+ throw new IllegalLinkException();
+ }
+
+ if (isset($_REQUEST['objectType'])) {
+ $objectType = StringUtil::trim($_REQUEST['objectType']);
+ if (!isset($this->availableObjectTypes[$objectType])) {
+ throw new IllegalLinkException();
+ }
+ $this->objectType = $this->availableObjectTypes[$objectType];
+ }
+ else {
+ // No object type provided, use the first object type.
+ $this->objectType = reset($this->availableObjectTypes);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function initObjectList() {
+ $this->objectList = $this->objectType->getProcessor()->getObjectListFor($this->tags);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readData() {
+ parent::readData();
+
+ $this->tagCloud = new TypedTagCloud($this->objectType->objectType);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign([
+ 'tag' => $this->tag,
+ 'tags' => $this->tagCloud->getTags(100),
+ 'availableObjectTypes' => $this->availableObjectTypes,
+ 'objectType' => $this->objectType->objectType,
+ 'resultListTemplateName' => $this->objectType->getProcessor()->getTemplateName(),
+ 'resultListApplication' => $this->objectType->getProcessor()->getApplication()
+ ]);
+
+ if (count($this->objectList) === 0) {
+ @header('HTTP/1.1 404 Not Found');
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\tagging;
+use wcf\data\tag\Tag;
+
+/**
+ * Abstract implementation of a taggable with support for searches with multiple tags.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Tagging
+ * @since 3.2
+ */
+abstract class AbstractCombinedTaggable extends AbstractTaggable implements ICombinedTaggable {
+ /**
+ * @inheritDoc
+ */
+ public function getObjectList(Tag $tag) {
+ return $this->getObjectListFor([$tag]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getObjectListFor(array $tags) {
+ return null;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\tagging;
+use wcf\data\DatabaseObjectList;
+use wcf\data\tag\Tag;
+
+/**
+ * Extended interface for taggable objects that support searches for objects
+ * that match multiple tags.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Tagging
+ * @since 3.2
+ */
+interface ICombinedTaggable extends ITaggable {
+ /**
+ * Returns a list of tagged objects that match all provided tags.
+ *
+ * @param Tag[] $tags
+ * @return DatabaseObjectList
+ * @since 3.2
+ */
+ public function getObjectListFor(array $tags);
+}
<?php
namespace wcf\system\tagging;
+use wcf\data\DatabaseObjectList;
use wcf\data\tag\Tag;
/**
* Returns a list of tagged objects.
*
* @param Tag $tag
- * @return \wcf\data\DatabaseObjectList
+ * @return DatabaseObjectList
+ * @deprecated 3.2
*/
public function getObjectList(Tag $tag);
use wcf\data\object\type\ObjectTypeCache;
use wcf\data\tag\Tag;
use wcf\data\tag\TagAction;
+use wcf\data\tag\TagList;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\exception\InvalidObjectTypeException;
+use wcf\system\language\LanguageFactory;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
return key($languageIDs);
}
+
+ /**
+ * Generates the inner SQL statement to fetch object ids that have all listed
+ * tags assigned to them.
+ *
+ * @param string $objectType
+ * @param Tag[] $tags
+ * @return array
+ * @since 3.2
+ */
+ public function getSqlForObjectsByTags($objectType, array $tags) {
+ $parameters = [$this->getObjectTypeID($objectType)];
+ $tagIDs = implode(',', array_map(function(Tag $tag) {
+ $parameters[] = $tag->tagID;
+
+ return '?';
+ }, $tags));
+ $parameters[] = count($tags);
+
+ $sql = "SELECT objectID
+ FROM wcf".WCF_N."_tag_to_object
+ WHERE objectTypeID = ?
+ AND tagID IN (".$tagIDs.")
+ GROUP BY objectID
+ HAVING COUNT(objectID) = ?";
+
+ return [
+ 'sql' => $sql,
+ 'parameters' => $parameters,
+ ];
+ }
+
+ /**
+ * Returns the matching tags by name.
+ *
+ * @param string[] $names
+ * @param int $languageID
+ * @return Tag[]
+ * @since 3.2
+ */
+ public function getTagsByName(array $names, $languageID) {
+ $tagList = new TagList();
+ $tagList->getConditionBuilder()->add('name IN (?)', [$names]);
+ $tagList->getConditionBuilder()->add('languageID = ?', [$languageID ?: WCF::getLanguage()->languageID]);
+ $tagList->readObjects();
+
+ return $tagList->getObjects();
+ }
}
<?php
namespace wcf\system\tagging;
use wcf\data\article\TaggedArticleList;
-use wcf\data\tag\Tag;
/**
* Implementation of ITaggable for tagging of cms articles.
* @package WoltLabSuite\Core\System\Tagging
* @since 3.0
*/
-class TaggableArticle extends AbstractTaggable {
+class TaggableArticle extends AbstractCombinedTaggable {
/**
* @inheritDoc
*/
- public function getObjectList(Tag $tag) {
- return new TaggedArticleList($tag);
+ public function getObjectListFor(array $tags) {
+ return new TaggedArticleList($tags);
}
/**