diff --git a/wcfsetup/install/files/lib/action/ArticleBulkAssignLabelDialogAction.class.php b/wcfsetup/install/files/lib/action/ArticleBulkAssignLabelDialogAction.class.php new file mode 100644 index 00000000000..0024f600bf7 --- /dev/null +++ b/wcfsetup/install/files/lib/action/ArticleBulkAssignLabelDialogAction.class.php @@ -0,0 +1,134 @@ + + * @since 6.3 + */ +final class ArticleBulkAssignLabelDialogAction implements RequestHandlerInterface +{ + #[\Override] + public function handle(ServerRequestInterface $request): ResponseInterface + { + $parameters = Helper::mapQueryParameters( + $request->getQueryParams(), + <<<'EOT' + array { + ids: non-empty-list + } + EOT + ); + + $articleList = new ArticleList(); + $articleList->setObjectIDs($parameters['ids']); + $articleList->readObjects(); + if ($articleList->getObjects() === []) { + throw new IllegalLinkException(); + } + + $articles = \array_values(\array_filter( + $articleList->getObjects(), + static fn(Article $article): bool => $article->canEdit() + )); + if ($articles === []) { + throw new PermissionDeniedException(); + } + + $labelGroups = $this->getCommonLabelGroups($articles); + if ($labelGroups === []) { + throw new IllegalLinkException(); + } + + $form = $this->getForm($labelGroups); + + if ($request->getMethod() === 'GET') { + return $form->toResponse(); + } elseif ($request->getMethod() === 'POST') { + $response = $form->validateRequest($request); + if ($response !== null) { + return $response; + } + + new AssignLabelsToArticles( + $articles, + \array_keys($labelGroups), + $form->getData()['labelIDs'] ?? [] + )(); + + return new JsonResponse([]); + } else { + throw new \LogicException('Unreachable'); + } + } + + /** + * Returns the label groups that are available in the categories of all given articles + * and that the active user is allowed to set. + * + * @param list
$articles + * @return array + */ + private function getCommonLabelGroups(array $articles): array + { + $labelGroups = null; + foreach ($articles as $article) { + $categoryLabelGroups = $article->getCategory()?->getLabelGroups() ?? []; + $labelGroups = $labelGroups === null + ? $categoryLabelGroups + : \array_intersect_key($labelGroups, $categoryLabelGroups); + + if ($labelGroups === []) { + return []; + } + } + + return \array_filter( + $labelGroups, + static fn(ViewableLabelGroup $labelGroup): bool => \count($labelGroup) !== 0 + ); + } + + /** + * @param array $labelGroups + */ + private function getForm(array $labelGroups): Psr15DialogForm + { + $form = new Psr15DialogForm( + static::class, + WCF::getLanguage()->get('wcf.article.button.assignLabel') + ); + + foreach ($labelGroups as $labelGroup) { + $form->appendChild( + LabelFormField::create('labelIDs' . $labelGroup->groupID) + ->objectProperty('labelIDs') + ->objectType('com.woltlab.wcf.article') + ->required($labelGroup->forceSelection !== 0) + ->labelGroup($labelGroup) + ); + } + $form->build(); + + return $form; + } +} diff --git a/wcfsetup/install/files/lib/command/article/AssignLabelsToArticles.class.php b/wcfsetup/install/files/lib/command/article/AssignLabelsToArticles.class.php new file mode 100644 index 00000000000..81eefa2b349 --- /dev/null +++ b/wcfsetup/install/files/lib/command/article/AssignLabelsToArticles.class.php @@ -0,0 +1,61 @@ + + * @since 6.3 + */ +final class AssignLabelsToArticles +{ + /** + * @param list
$articles + * @param list $labelGroupIDs + * @param array $labelIDs + */ + public function __construct( + private readonly array $articles, + private readonly array $labelGroupIDs, + private readonly array $labelIDs + ) {} + + public function __invoke(): void + { + if ($this->articles === [] || $this->labelGroupIDs === []) { + return; + } + + $objectTypeID = LabelHandler::getInstance()->getObjectType('com.woltlab.wcf.article')->objectTypeID; + $articleIDs = []; + foreach ($this->articles as $article) { + $articleIDs[] = $article->articleID; + } + + foreach ($this->articles as $article) { + LabelHandler::getInstance()->replaceLabels( + $this->labelGroupIDs, + $this->labelIDs, + 'com.woltlab.wcf.article', + $article->articleID + ); + } + + // Do not validate permissions for the hasLabels flag. + $assignedLabels = LabelHandler::getInstance()->getAssignedLabels($objectTypeID, $articleIDs, false); + + // update `hasLabels` flag + foreach ($this->articles as $article) { + ArticleBuilder::forUpdate($article) + ->setHasLabels(($assignedLabels[$article->articleID] ?? []) !== []) + ->update(); + } + } +} diff --git a/wcfsetup/install/files/lib/system/interaction/bulk/admin/ArticleBulkInteractions.class.php b/wcfsetup/install/files/lib/system/interaction/bulk/admin/ArticleBulkInteractions.class.php index a2d89a04271..6c154fc3ff6 100644 --- a/wcfsetup/install/files/lib/system/interaction/bulk/admin/ArticleBulkInteractions.class.php +++ b/wcfsetup/install/files/lib/system/interaction/bulk/admin/ArticleBulkInteractions.class.php @@ -3,6 +3,7 @@ namespace wcf\system\interaction\bulk\admin; use wcf\acp\action\ArticleCategoryAction; +use wcf\action\ArticleBulkAssignLabelDialogAction; use wcf\data\article\AccessibleArticleList; use wcf\data\article\Article; use wcf\event\interaction\bulk\admin\ArticleBulkInteractionCollecting; @@ -121,7 +122,19 @@ function (Article $article): bool { 'setCategory', ArticleCategoryAction::class, 'wcf.article.button.setCategory' - ) + ), + new BulkFormBuilderDialogInteraction( + 'assign-label', + ArticleBulkAssignLabelDialogAction::class, + 'wcf.article.button.assignLabel', + static function (Article $article): bool { + if (!$article->canEdit()) { + return false; + } + + return ($article->getCategory()?->getLabelGroups() ?? []) !== []; + } + ), ]); EventHandler::getInstance()->fire( diff --git a/wcfsetup/install/files/lib/system/interaction/bulk/user/ArticleBulkInteractions.class.php b/wcfsetup/install/files/lib/system/interaction/bulk/user/ArticleBulkInteractions.class.php index ecaf211daa6..13e352ded66 100644 --- a/wcfsetup/install/files/lib/system/interaction/bulk/user/ArticleBulkInteractions.class.php +++ b/wcfsetup/install/files/lib/system/interaction/bulk/user/ArticleBulkInteractions.class.php @@ -2,12 +2,14 @@ namespace wcf\system\interaction\bulk\user; +use wcf\action\ArticleBulkAssignLabelDialogAction; use wcf\data\article\AccessibleArticleList; use wcf\data\article\Article; use wcf\event\interaction\bulk\user\ArticleBulkInteractionCollecting; use wcf\system\event\EventHandler; use wcf\system\interaction\bulk\AbstractBulkInteractionProvider; use wcf\system\interaction\bulk\BulkDeleteInteraction; +use wcf\system\interaction\bulk\BulkFormBuilderDialogInteraction; use wcf\system\interaction\bulk\BulkRestoreInteraction; use wcf\system\interaction\bulk\BulkRpcInteraction; use wcf\system\interaction\bulk\BulkSoftDeleteInteraction; @@ -113,6 +115,18 @@ function (Article $article): bool { return $article->isFeatured !== 0; } ), + new BulkFormBuilderDialogInteraction( + 'assign-label', + ArticleBulkAssignLabelDialogAction::class, + 'wcf.article.button.assignLabel', + static function (Article $article): bool { + if (!$article->canEdit()) { + return false; + } + + return ($article->getCategory()?->getLabelGroups() ?? []) !== []; + } + ), ] ); } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 1515b193a1b..15d709d3379 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -3319,6 +3319,7 @@ freigeschaltet. {if LANGUAGE_USE_INFORMAL_VARIANT}Du kannst{else}Sie können{/if + username}]]> diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 02808901451..bc1913baf89 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -3244,6 +3244,7 @@ Your account on {@PAGE_TITLE|phrase} [URL:{link isEmail=true}{/link}] has been a + username}]]>