From 3db0f431b8db3aebb11ec9ac3d69a6199143b738 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 28 Apr 2023 14:24:55 +0200 Subject: [PATCH] Implement mention support for user groups Fixes #5439 --- .../Core/Component/Ckeditor/Mention.ts | 46 ++++++++++----- .../Core/Component/Ckeditor/Mention.js | 25 ++++++--- ...ditorGetMentionSuggestionsAction.class.php | 56 ++++++++++++++----- 3 files changed, 92 insertions(+), 35 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/Ckeditor/Mention.ts b/ts/WoltLabSuite/Core/Component/Ckeditor/Mention.ts index 6dd999b309..f3628fb200 100644 --- a/ts/WoltLabSuite/Core/Component/Ckeditor/Mention.ts +++ b/ts/WoltLabSuite/Core/Component/Ckeditor/Mention.ts @@ -13,21 +13,27 @@ import { createFragmentFromHtml } from "../../Dom/Util"; import { listenToCkeditor } from "./Event"; import { EditorConfig } from "./Types"; -type SearchResultItem = { - avatarTag: string; - username: string; - userID: number; - type: "user"; -}; +type SearchResultItem = + | { + avatarTag: string; + username: string; + userID: number; + type: "user"; + } + | { + name: string; + groupID: string; + type: "group"; + }; type ResultGetSearchResultList = SearchResultItem[]; -type UserMention = { +type Mention = { id: string; text: string; icon: string; }; -async function getPossibleMentions(query: string): Promise { +async function getPossibleMentions(query: string): Promise { // TODO: Provide the URL as a parameter. const url = new URL(window.WSC_API_URL + "index.php?editor-get-mention-suggestions/"); url.searchParams.set("query", query); @@ -39,13 +45,23 @@ async function getPossibleMentions(query: string): Promise { .fetchAsJson()) as ResultGetSearchResultList; return result.map((item) => { - return { - id: `@${item.username}`, - text: item.username, - icon: item.avatarTag, - objectId: item.userID, - type: item.type, - }; + if (item.type === "user") { + return { + id: `@${item.username}`, + text: item.username, + icon: item.avatarTag, + objectId: item.userID, + type: item.type, + }; + } else { + return { + id: `@${item.name}`, + text: item.name, + icon: '', + objectId: item.groupID, + type: item.type, + }; + } }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Mention.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Mention.js index 0ee189c1f3..2213c9e723 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Mention.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Mention.js @@ -20,13 +20,24 @@ define(["require", "exports", "../../Ajax/Backend", "../../Dom/Util", "./Event"] .disableLoadingIndicator() .fetchAsJson()); return result.map((item) => { - return { - id: `@${item.username}`, - text: item.username, - icon: item.avatarTag, - objectId: item.userID, - type: item.type, - }; + if (item.type === "user") { + return { + id: `@${item.username}`, + text: item.username, + icon: item.avatarTag, + objectId: item.userID, + type: item.type, + }; + } + else { + return { + id: `@${item.name}`, + text: item.name, + icon: '', + objectId: item.groupID, + type: item.type, + }; + } }); } function getMentionConfiguration() { diff --git a/wcfsetup/install/files/lib/action/EditorGetMentionSuggestionsAction.class.php b/wcfsetup/install/files/lib/action/EditorGetMentionSuggestionsAction.class.php index ed10bbe3c9..45630b0bc7 100644 --- a/wcfsetup/install/files/lib/action/EditorGetMentionSuggestionsAction.class.php +++ b/wcfsetup/install/files/lib/action/EditorGetMentionSuggestionsAction.class.php @@ -6,6 +6,7 @@ use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use wcf\data\user\group\UserGroup; use wcf\data\user\UserProfile; use wcf\data\user\UserProfileList; use wcf\http\Helper; @@ -31,20 +32,28 @@ final class EditorGetMentionSuggestionsAction implements RequestHandlerInterface EOT, ); - $users = $this->getUsers($parameters); + $query = \mb_strtolower($parameters['query']); + $matches = []; - // TODO: Groups + foreach ($this->getGroups($query) as $userGroup) { + $matches[] = [ + 'name' => $userGroup->getName(), + 'groupID' => $userGroup->groupID, + 'type' => 'group', + ]; + } + + foreach ($this->getUsers($query) as $userProfile) { + $matches[] = [ + 'avatarTag' => $userProfile->getAvatar()->getImageTag(16), + 'username' => $userProfile->getUsername(), + 'userID' => $userProfile->getObjectID(), + 'type' => 'user', + ]; + } return new JsonResponse( - \array_map( - static fn (UserProfile $userProfile) => [ - 'avatarTag' => $userProfile->getAvatar()->getImageTag(16), - 'username' => $userProfile->getUsername(), - 'userID' => $userProfile->getObjectID(), - 'type' => 'user', - ], - $users - ), + $matches, 200, [ 'cache-control' => [ @@ -57,14 +66,35 @@ final class EditorGetMentionSuggestionsAction implements RequestHandlerInterface /** * @return list */ - private function getUsers(array $parameters): array + private function getUsers(string $query): array { $userProfileList = new UserProfileList(); - $userProfileList->getConditionBuilder()->add("username LIKE ?", [$parameters['query'] . '%']); + $userProfileList->getConditionBuilder()->add("username LIKE ?", [$query . '%']); $userProfileList->sqlLimit = 10; $userProfileList->readObjects(); return \array_values($userProfileList->getObjects()); } + + /** + * @return list + */ + private function getGroups(string $query): array + { + $userGroups = UserGroup::getMentionableGroups(); + if ($userGroups === []) { + return []; + } + + $userGroups = \array_filter($userGroups, static function (UserGroup $userGroup) use ($query) { + return \str_starts_with(\mb_strtolower($userGroup->getName()), $query); + }); + + \usort($userGroups, static function (UserGroup $a, UserGroup $b) { + return \mb_strtolower($a->getName()) <=> \mb_strtolower($b->getName()); + }); + + return $userGroups; + } } -- 2.20.1