From f247a1a66d53e3f94357b7b54e39525845d7c86e Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Fri, 20 Dec 2024 14:41:10 +0100 Subject: [PATCH] Add `IEmbeddedMessageObject` to load embedded object --- .../Core/Api/Messages/RenderQuote.ts | 3 +- ts/WoltLabSuite/Core/Component/Quote/List.ts | 40 +++++++++++++------ .../Core/Component/Quote/Storage.ts | 32 +++++++++++---- .../WoltLabSuite/Core/Component/Quote/List.js | 29 ++++++++++---- .../Core/Component/Quote/Storage.js | 16 ++++++-- .../lib/data/IEmbeddedMessageObject.class.php | 19 +++++++++ .../core/messages/RenderQuote.class.php | 10 +++-- ...PreloadPhrasesCollectingListener.class.php | 1 + 8 files changed, 116 insertions(+), 34 deletions(-) create mode 100644 wcfsetup/install/files/lib/data/IEmbeddedMessageObject.class.php diff --git a/ts/WoltLabSuite/Core/Api/Messages/RenderQuote.ts b/ts/WoltLabSuite/Core/Api/Messages/RenderQuote.ts index 85195f9474..fde2cdc377 100644 --- a/ts/WoltLabSuite/Core/Api/Messages/RenderQuote.ts +++ b/ts/WoltLabSuite/Core/Api/Messages/RenderQuote.ts @@ -19,7 +19,8 @@ type Response = { link: string; title: string; avatar: string; - message: string; + message: string | null; + rawMessage: string | null; }; export async function renderQuote( diff --git a/ts/WoltLabSuite/Core/Component/Quote/List.ts b/ts/WoltLabSuite/Core/Component/Quote/List.ts index 9445fa9578..8fe2d35578 100644 --- a/ts/WoltLabSuite/Core/Component/Quote/List.ts +++ b/ts/WoltLabSuite/Core/Component/Quote/List.ts @@ -8,8 +8,7 @@ * @woltlabExcludeBundle tiny */ -import { listenToCkeditor } from "WoltLabSuite/Core/Component/Ckeditor/Event"; -import type { CKEditor } from "WoltLabSuite/Core/Component/Ckeditor"; +import { listenToCkeditor, dispatchToCkeditor } from "WoltLabSuite/Core/Component/Ckeditor/Event"; import { getTabMenu } from "WoltLabSuite/Core/Component/Message/MessageTabMenu"; import { getPhrase } from "WoltLabSuite/Core/Language"; import { setActiveEditor } from "WoltLabSuite/Core/Component/Quote/Message"; @@ -20,10 +19,10 @@ const quoteLists = new Map(); class QuoteList { #container: HTMLElement; - #editor: CKEditor; + #editor: HTMLElement; #editorId: string; - constructor(editorId: string, editor: CKEditor) { + constructor(editorId: string, editor: HTMLElement) { this.#editorId = editorId; this.#editor = editor; this.#container = document.getElementById(`quotes_${editorId}`)!; @@ -41,13 +40,14 @@ class QuoteList { public renderQuotes(): void { this.#container.innerHTML = ""; + let quotesCount = 0; for (const [key, quotes] of getQuotes()) { const message = getMessage(key)!; + quotesCount += quotes.size; // TODO escape values // TODO create web components??? - this.#container.append( - DomUtil.createFragmentFromHtml(`
+ const fragment = DomUtil.createFragmentFromHtml(`
@@ -74,11 +74,12 @@ class QuoteList {
- ${quote} + ${quote.message}
`, ) @@ -87,16 +88,29 @@ class QuoteList {
-
`), - ); - // TODO render quotes +
`); + + fragment.querySelectorAll(".jsInsertQuote").forEach((button) => { + button.addEventListener("click", () => { + // TODO dont query the DOM + // TODO use rawMessage to insert if available otherwise use message + dispatchToCkeditor(this.#editor).insertQuote({ + author: message.author, + content: button.closest("li")!.querySelector(".jsQuote")!.innerHTML, + isText: false, + link: message.link, + }); + }); + }); + + this.#container.append(fragment); } - if (this.#container.hasChildNodes()) { + if (quotesCount > 0) { getTabMenu(this.#editorId)?.showTab( "quotes", getPhrase("wcf.message.quote.showQuotes", { - count: this.#container.childElementCount, + count: quotesCount, }), ); } else { @@ -127,7 +141,7 @@ export function setup(editorId: string): void { listenToCkeditor(editor).ready(({ ckeditor }) => { if (ckeditor.features.quoteBlock) { - quoteLists.set(editorId, new QuoteList(editorId, ckeditor)); + quoteLists.set(editorId, new QuoteList(editorId, editor)); } setActiveEditor(ckeditor, ckeditor.features.quoteBlock); diff --git a/ts/WoltLabSuite/Core/Component/Quote/Storage.ts b/ts/WoltLabSuite/Core/Component/Quote/Storage.ts index a4bdfbb545..314173fd44 100644 --- a/ts/WoltLabSuite/Core/Component/Quote/Storage.ts +++ b/ts/WoltLabSuite/Core/Component/Quote/Storage.ts @@ -23,8 +23,13 @@ interface Message { avatar: string; } +interface Quote { + message: string; + rawMessage?: string; +} + interface StorageData { - quotes: Map>; + quotes: Map>; messages: Map; } @@ -37,7 +42,9 @@ export async function saveQuote(objectType: string, objectId: number, objectClas return; } - storeQuote(objectType, result.value, message); + storeQuote(objectType, result.value, { + message, + }); refreshQuoteLists(); } @@ -60,11 +67,16 @@ export async function saveFullQuote(objectType: string, objectClassName: string, author: result.value.author, avatar: result.value.avatar, }, - result.value.message, + { + message: result.value.message!, + rawMessage: result.value.rawMessage!, + }, ); + + refreshQuoteLists(); } -export function getQuotes(): Map> { +export function getQuotes(): Map> { return getStorage().quotes; } @@ -74,7 +86,7 @@ export function getMessage(objectType: string, objectId?: number): Message | und return getStorage().messages.get(key); } -export function removeQuote(objectType: string, objectId: number, quote: string): void { +export function removeQuote(objectType: string, objectId: number, quote: Quote): void { const storage = getStorage(); const key = getKey(objectType, objectId); @@ -94,7 +106,7 @@ export function removeQuote(objectType: string, objectId: number, quote: string) refreshQuoteLists(); } -function storeQuote(objectType: string, message: Message, quote: string): void { +function storeQuote(objectType: string, message: Message, quote: Quote): void { const storage = getStorage(); const key = getKey(objectType, message.objectID); @@ -104,7 +116,13 @@ function storeQuote(objectType: string, message: Message, quote: string): void { storage.messages.set(key, message); - storage.quotes.get(key)!.add(quote); + if ( + !Array.from(storage.quotes.get(key)!) + .map((q) => q.message) + .includes(quote.message) + ) { + storage.quotes.get(key)!.add(quote); + } saveStorage(storage); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js index a8e1c59bf3..d2dd115fc7 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js @@ -33,11 +33,13 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Component/Ckeditor/Eve } renderQuotes() { this.#container.innerHTML = ""; + let quotesCount = 0; for (const [key, quotes] of (0, Storage_1.getQuotes)()) { const message = (0, Storage_1.getMessage)(key); + quotesCount += quotes.size; // TODO escape values // TODO create web components??? - this.#container.append(Util_1.default.createFragmentFromHtml(`
+ const fragment = Util_1.default.createFragmentFromHtml(`
@@ -63,11 +65,12 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Component/Ckeditor/Eve
- ${quote} + ${quote.message}
`) .join("")} @@ -75,12 +78,24 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Component/Ckeditor/Eve
-
`)); - // TODO render quotes +
`); + fragment.querySelectorAll(".jsInsertQuote").forEach((button) => { + button.addEventListener("click", () => { + // TODO dont query the DOM + // TODO use rawMessage to insert if available otherwise use message + (0, Event_1.dispatchToCkeditor)(this.#editor).insertQuote({ + author: message.author, + content: button.closest("li").querySelector(".jsQuote").innerHTML, + isText: false, + link: message.link, + }); + }); + }); + this.#container.append(fragment); } - if (this.#container.hasChildNodes()) { + if (quotesCount > 0) { (0, MessageTabMenu_1.getTabMenu)(this.#editorId)?.showTab("quotes", (0, Language_1.getPhrase)("wcf.message.quote.showQuotes", { - count: this.#container.childElementCount, + count: quotesCount, })); } else { @@ -106,7 +121,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Component/Ckeditor/Eve } (0, Event_1.listenToCkeditor)(editor).ready(({ ckeditor }) => { if (ckeditor.features.quoteBlock) { - quoteLists.set(editorId, new QuoteList(editorId, ckeditor)); + quoteLists.set(editorId, new QuoteList(editorId, editor)); } (0, Message_1.setActiveEditor)(ckeditor, ckeditor.features.quoteBlock); ckeditor.focusTracker.on("change:isFocused", (_evt, _name, isFocused) => { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/Storage.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/Storage.js index fd6e14848d..b71b4f69be 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/Storage.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/Storage.js @@ -23,7 +23,9 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Core", "WoltLabSuite/C // TODO error handling return; } - storeQuote(objectType, result.value, message); + storeQuote(objectType, result.value, { + message, + }); (0, List_1.refreshQuoteLists)(); } async function saveFullQuote(objectType, objectClassName, objectId) { @@ -40,7 +42,11 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Core", "WoltLabSuite/C authorID: result.value.authorID, author: result.value.author, avatar: result.value.avatar, - }, result.value.message); + }, { + message: result.value.message, + rawMessage: result.value.rawMessage, + }); + (0, List_1.refreshQuoteLists)(); } function getQuotes() { return getStorage().quotes; @@ -70,7 +76,11 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Core", "WoltLabSuite/C storage.quotes.set(key, new Set()); } storage.messages.set(key, message); - storage.quotes.get(key).add(quote); + if (!Array.from(storage.quotes.get(key)) + .map((q) => q.message) + .includes(quote.message)) { + storage.quotes.get(key).add(quote); + } saveStorage(storage); } function getStorage() { diff --git a/wcfsetup/install/files/lib/data/IEmbeddedMessageObject.class.php b/wcfsetup/install/files/lib/data/IEmbeddedMessageObject.class.php new file mode 100644 index 0000000000..da483e1eb6 --- /dev/null +++ b/wcfsetup/install/files/lib/data/IEmbeddedMessageObject.class.php @@ -0,0 +1,19 @@ + + * @since 6.2 + */ +interface IEmbeddedMessageObject +{ + /** + * Loads embedded objects for the given object type and object IDs. + */ + public function loadEmbeddedObjects(): void; +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/messages/RenderQuote.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/messages/RenderQuote.class.php index 21c1103cb2..aec19a6a4d 100644 --- a/wcfsetup/install/files/lib/system/endpoint/controller/core/messages/RenderQuote.class.php +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/messages/RenderQuote.class.php @@ -5,6 +5,7 @@ namespace wcf\system\endpoint\controller\core\messages; use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use wcf\data\IEmbeddedMessageObject; use wcf\data\IMessage; use wcf\http\Helper; use wcf\system\cache\runtime\UserProfileRuntimeCache; @@ -34,6 +35,10 @@ final class RenderQuote implements IController $userProfile = UserProfileRuntimeCache::getInstance()->getObject($object->getUserID()); + if ($object instanceof IEmbeddedMessageObject) { + $object->loadEmbeddedObjects(); + } + return new JsonResponse( [ "objectID" => $object->getObjectID(), @@ -43,7 +48,8 @@ final class RenderQuote implements IController "time" => (new \DateTime('@' . $object->getTime()))->format("c"), "title" => $object->getTitle(), "link" => $object->getLink(), - "message" => $parameters->fullQuote ? $this->renderFullQuote($object) : "" + "rawMessage" => $parameters->fullQuote ? $this->renderFullQuote($object) : null, + "message" => $parameters->fullQuote ? $object->getFormattedMessage() : null ], 200, ); @@ -51,8 +57,6 @@ final class RenderQuote implements IController private function renderFullQuote(IMessage $object): string { - // TODO load embedded objects? - $htmlInputProcessor = new HtmlInputProcessor(); $htmlInputProcessor->processIntermediate($object->getMessage()); diff --git a/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php b/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php index 8aaf4fe612..8cbac407ea 100644 --- a/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php +++ b/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php @@ -143,6 +143,7 @@ final class PreloadPhrasesCollectingListener $event->preload('wcf.message.quote.quoteAndReply'); $event->preload('wcf.message.quote.removeAllQuotes'); $event->preload('wcf.message.quote.showQuotes'); + $event->preload('wcf.message.quote.insertQuote'); $event->preload('wcf.moderation.report.reportContent'); -- 2.20.1