Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tab in the editor for quotes #6163

Open
wants to merge 66 commits into
base: 6.2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
c10a4c5
Add basic tabs for quote
Cyperghost Dec 17, 2024
d2adc8d
Render quote tab dynamic
Cyperghost Dec 17, 2024
1219a24
Don't accept template to render the quote container content
Cyperghost Dec 17, 2024
7e636aa
Mark `WCF.Message.Quote.Manager` as deprecated
Cyperghost Dec 18, 2024
5f65a24
Don't create new `WCF.Message.Quote.Manager`
Cyperghost Dec 18, 2024
ef8ca5f
Preload some language phrases for quotes
Cyperghost Dec 18, 2024
a888cf9
Mark `WoltLabSuite/Core/Ui/Message/Quote` as deprecated
Cyperghost Dec 18, 2024
1e70d82
Add a function to get the `focusTracker` from the ckeditor
Cyperghost Dec 19, 2024
11f927f
Set active editor to past the quote
Cyperghost Dec 19, 2024
fb73351
Add new endpoint of render the full quote of a message
Cyperghost Dec 19, 2024
b828e9b
Store information about the message author
Cyperghost Dec 19, 2024
629f005
Add basic template for quote list
Cyperghost Dec 19, 2024
8dc0b62
Refresh quote list, if new quote added
Cyperghost Dec 19, 2024
126bcda
Add function to remove quotes
Cyperghost Dec 19, 2024
f247a1a
Add `IEmbeddedMessageObject` to load embedded object
Cyperghost Dec 20, 2024
a1f4908
Handle guest objects
Cyperghost Dec 20, 2024
aa6a8a7
Present stored quotes using the existing UI design
dtdesign Jan 6, 2025
2a1b94a
Use the new tab counters to reflect the number of stored quotes
dtdesign Jan 8, 2025
bebbe96
Fixes an issue where the button area is too large when a word in the …
Cyperghost Jan 13, 2025
b7a2716
Set `hidden` to true
Cyperghost Jan 13, 2025
d7414b4
Save quotes in a set
Cyperghost Jan 13, 2025
ae906e3
Use `JSON.stringify` to compare
Cyperghost Jan 13, 2025
7eb03e6
Insert quote by pressing the button
Cyperghost Jan 13, 2025
0329f39
Remove quotes from the storage as soon as the message has been succes…
Cyperghost Jan 13, 2025
0462899
Generate uuid only if the quote is not already saved
Cyperghost Jan 13, 2025
aef20c1
Define `$quoteManager` for backwards compatibility
Cyperghost Jan 14, 2025
0dbe5ec
Show active quoted messages
Cyperghost Jan 14, 2025
f069c5f
Send the uuids from quoted messages to remove them from the next request
Cyperghost Jan 14, 2025
6309ec9
Mark `IMessageQuoteHandler` as deprecated
Cyperghost Jan 14, 2025
88b7899
Delete the quotes saved between the next request after successfully s…
Cyperghost Jan 14, 2025
a3f2fb0
Mark `QuotedMessage` as deprecated
Cyperghost Jan 14, 2025
58b8df0
Remove the deprecated marker from `MessageQuoteManager::renderQuote()`
Cyperghost Jan 14, 2025
715f9e1
Don't use `MessageQuoteManager::assignVariables()`
Cyperghost Jan 14, 2025
bc802c1
Register endpoint to delete removal quote uudis
Cyperghost Jan 14, 2025
ce28e25
Mark `IMessageQuoteAction` as deprecated
Cyperghost Jan 14, 2025
62c9aff
Change comment for the saved function
Cyperghost Jan 14, 2025
03ec3fd
Remove the `active` status when the quote for an editor is deleted
Cyperghost Jan 14, 2025
48453d9
Go to the page `href` if there is no active editor and `href` is a va…
Cyperghost Jan 14, 2025
6966f58
Reset the active editor as soon as the form has been successfully sub…
Cyperghost Jan 14, 2025
ccfa0ea
If the editor is no longer visible, do not set an editor as active fo…
Cyperghost Jan 14, 2025
8619047
Add comment why `.getAttribute(‘href’)` is used instead of `.href`
Cyperghost Jan 14, 2025
da69aa3
No redirect
Cyperghost Jan 14, 2025
3046a7e
Mark `renderQuote()`, `readParameters()` and `getQuoteMessageID()` as…
Cyperghost Jan 14, 2025
4169c85
Deprecated `WysiwygFormField::quoteData()` and added the replacement …
Cyperghost Jan 15, 2025
a06a5a7
Implement a new `WysiwygTabFormContainer`, which can have an icon
Cyperghost Jan 15, 2025
0ac017b
Merge branch 'bugfix/6.2-message-tab-menu-form-builder' into 6.2-quot…
Cyperghost Jan 15, 2025
9e546bd
Deprecate functions no longer required in the FormBuilder for quotes.
Cyperghost Jan 15, 2025
4f528a5
Move `MessageQuoteManager::saved()` into the data handler
Cyperghost Jan 15, 2025
64b6259
Recognise which full quotes have been used in another tab and unmark …
Cyperghost Jan 15, 2025
249b616
The template for the quotations in the inline editor is identical to …
Cyperghost Jan 15, 2025
6cbabea
Insert return statement again
Cyperghost Jan 15, 2025
56ff1a6
Insert return statement again
Cyperghost Jan 15, 2025
0a1facf
Fixes the problem that the same messages were inserted multiple times
Cyperghost Jan 15, 2025
b16ff7c
Mark variables that are no longer used
Cyperghost Jan 15, 2025
634775b
Cache used quotes that belong to a message that has not yet been save…
Cyperghost Jan 16, 2025
415d93d
Remove `MessageQuoteAction`
Cyperghost Jan 16, 2025
711defc
Remove quote object type interface
Cyperghost Jan 16, 2025
d4a1837
Add missing line-breaks at EOF
dtdesign Jan 22, 2025
b098421
Rename endpoint to `/core/messages/message-author`
Cyperghost Jan 22, 2025
88acdb6
Use `FontAwesomeIcon` instead of a string
Cyperghost Jan 22, 2025
8a17b55
Rename endpoint to `/core/messages/render-quote`
Cyperghost Jan 22, 2025
72b50a6
Run `tsc`
Cyperghost Jan 22, 2025
d96a628
Remove not used language phrases
Cyperghost Jan 22, 2025
9e34089
Remove `WCF.Message.Quote`, `WCF.Message.Quote.Handler` and `WCF.Mess…
Cyperghost Jan 22, 2025
489995a
Remove unused laugage variables
Cyperghost Jan 22, 2025
ad33be7
Run `tsc`
Cyperghost Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions com.woltlab.wcf/coreObject.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<coreobject>
<objectname>wcf\system\file\upload\UploadHandler</objectname>
</coreobject>
<coreobject>
<objectname>wcf\system\message\quote\MessageQuoteManager</objectname>
</coreobject>
</import>
<delete>
<coreobject name="wcf\system\user\authentication\UserAuthenticationFactory"/>
Expand Down
1 change: 0 additions & 1 deletion com.woltlab.wcf/objectTypeDefinition.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
</definition>
<definition>
<name>com.woltlab.wcf.message.quote</name>
<interfacename>wcf\system\message\quote\IMessageQuoteHandler</interfacename>
</definition>
<definition>
<name>com.woltlab.wcf.user.recentActivityEvent</name>
Expand Down
8 changes: 8 additions & 0 deletions com.woltlab.wcf/templates/__messageFormQuote.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div id="quotes_{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}"
class="messageTabMenuContent messageTabMenuContent--quotes"></div>

<script data-relocate="true">
require(["WoltLabSuite/Core/Component/Quote/List"], ({ setup }) => {
setup("{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}");
});
</script>
7 changes: 6 additions & 1 deletion com.woltlab.wcf/templates/headIncludeJavaScript.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ window.addEventListener('pageshow', function(event) {
{event name='javascriptShareButtonProviders'}
],
{/if}
styleChanger: {if $__wcf->getStyleHandler()->showStyleChanger()}true{else}false{/if}
styleChanger: {if $__wcf->getStyleHandler()->showStyleChanger()}true{else}false{/if},
{if $__wcf->user->userID && !$__wcf->getMessageQuoteManager()->getRemoveQuoteIDs()|empty}removeQuotes: [{implode from=$__wcf->getMessageQuoteManager()->getRemoveQuoteIDs() item=uuid}'{$uuid|encodeJS}'{/implode}],{/if}
{if $__wcf->user->userID && !$__wcf->getMessageQuoteManager()->getUsedQuotes()|empty}usedQuotes: new Map([
{foreach from=$__wcf->getMessageQuoteManager()->getUsedQuotes() key=editorID item=uuids}['{$editorID|encodeJS}', [{implode from=$uuids item=uuid}'{$uuid|encodeJS}'{/implode}]]{/foreach}
]),
{/if}
});
});
</script>
Expand Down
9 changes: 9 additions & 0 deletions com.woltlab.wcf/templates/messageFormTabs.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@
</li>
{/if}
{event name='tabMenuTabs'}

<li data-name="quotes" hidden>
<button type="button">
{icon name='quote-left'}
<span>{lang}wcf.bbcode.quote{/lang}</span>
</button>
</li>
</ul>
</nav>

Expand All @@ -53,4 +60,6 @@
{include file='__messageFormPoll'}

{event name='tabMenuContents'}

{include file='__messageFormQuote'}
</div>
11 changes: 10 additions & 1 deletion com.woltlab.wcf/templates/messageFormTabsInline.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
</li>
{/if}
{event name='tabMenuTabs'}

<li data-name="quotes" hidden>
<button type="button">
{icon name='quote-left'}
<span>{lang}wcf.bbcode.quote{/lang}</span>
</button>
</li>
</ul>
</nav>

Expand All @@ -57,4 +64,6 @@
{include file='__messageFormPollInline'}

{event name='tabMenuContents'}
</div>

{include file='__messageFormQuote'}
</div>
16 changes: 2 additions & 14 deletions com.woltlab.wcf/templates/shared_messageQuoteManager.tpl
Original file line number Diff line number Diff line change
@@ -1,14 +1,2 @@
WCF.Language.addObject({
'wcf.message.quote.insertAllQuotes': '{jslang}wcf.message.quote.insertAllQuotes{/jslang}',
'wcf.message.quote.insertSelectedQuotes': '{jslang}wcf.message.quote.insertSelectedQuotes{/jslang}',
'wcf.message.quote.manageQuotes': '{jslang}wcf.message.quote.manageQuotes{/jslang}',
'wcf.message.quote.quoteSelected': '{jslang}wcf.message.quote.quoteSelected{/jslang}',
'wcf.message.quote.quoteAndReply': '{jslang}wcf.message.quote.quoteAndReply{/jslang}',
'wcf.message.quote.removeAllQuotes': '{jslang}wcf.message.quote.removeAllQuotes{/jslang}',
'wcf.message.quote.removeSelectedQuotes': '{jslang}wcf.message.quote.removeSelectedQuotes{/jslang}',
'wcf.message.quote.showQuotes': '{jslang __literal=true}wcf.message.quote.showQuotes{/jslang}'
});

{if !$wysiwygSelector|isset}{assign var=wysiwygSelector value=''}{/if}
{if !$supportPaste|isset}{assign var=supportPaste value=false}{/if}
var $quoteManager = new WCF.Message.Quote.Manager({@$__quoteCount}, '{$wysiwygSelector|encodeJS}', {if $supportPaste}true{else}false{/if}, [ {implode from=$__quoteRemove item=quoteID}'{$quoteID}'{/implode} ]);
{* Deprecated since 6.2 *}
var $quoteManager = undefined;
23 changes: 0 additions & 23 deletions com.woltlab.wcf/templates/shared_wysiwygFormField.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,3 @@
*}>{$field->getValue()}</textarea>

{include file='shared_wysiwyg' wysiwygSelector=$field->getPrefixedId()}

{if $field->supportsQuotes()}
<script data-relocate="true">
// Bootstrap for window.__wcf_bc_eventHandler
require(['WoltLabSuite/Core/Bootstrap'], function(Bootstrap) {
{include file='shared_messageQuoteManager' wysiwygSelector=$field->getPrefixedId() supportPaste=true}

{if $field->getQuoteData() !== null}
var quoteHandler = new WCF.Message.Quote.Handler(
$quoteManager,
'{$field->getQuoteData('actionClass')|encodeJS}',
'{$field->getQuoteData('objectType')}',
'{$field->getQuoteData('selectors')[container]}',
'{$field->getQuoteData('selectors')[messageBody]}',
'{$field->getQuoteData('selectors')[messageContent]}',
true
);

elData(elById('{@$field->getPrefixedId()|encodeJS}'), 'quote-handler', quoteHandler);
{/if}
});
</script>
{/if}
16 changes: 16 additions & 0 deletions com.woltlab.wcf/templates/shared_wysiwygQuoteFormContainer.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div id="{$container->getPrefixedId()|encodeJS}Container"{*
*} class="messageTabMenuContent messageTabMenuContent--quotes"></div>

<script data-relocate="true">
require(["WoltLabSuite/Core/Component/Quote/List"], ({ setup }) => {
setup("{$container->getWysiwygId()|encodeJS}", "{$container->getPrefixedId()|encodeJS}Container");
});
</script>

{include file='shared_formContainerDependencies'}

<script data-relocate="true">
require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/WysiwygTab'], ({ WysiwygTab }) => {
new WysiwygTab('{$container->getPrefixedId()|encodeJS}Container', '{$container->getName()|encodeJS}', '{$container->getWysiwygId()|encodeJS}');
});
</script>
15 changes: 15 additions & 0 deletions com.woltlab.wcf/templates/shared_wysiwygTabFormContainer.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div id="{$container->getPrefixedId()}Container"{*
*}{if !$container->getClasses()|empty} class="{implode from=$container->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{*
*}{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{*
*}{if !$container->checkDependencies()} hidden{/if}{*
*}>
{include file='shared_formContainerChildren'}
</div>

{include file='shared_formContainerDependencies'}

<script data-relocate="true">
require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/WysiwygTab'], ({ WysiwygTab }) => {
new WysiwygTab('{$container->getPrefixedId()|encodeJS}Container', '{$container->getName()|encodeJS}', '{$container->getWysiwygId()|encodeJS}');
});
</script>
30 changes: 29 additions & 1 deletion com.woltlab.wcf/templates/shared_wysiwygTabMenuFormContainer.tpl
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
{include file='shared_tabMenuFormContainer' __tabMenuCSSClassName='messageTabMenuNavigation'}
<div id="{$container->getPrefixedId()}Container"{*
*}{if !$container->getClasses()|empty} class="{implode from=$container->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{*
*}{foreach from=$container->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{*
*}{if !$container->checkDependencies()} hidden{/if}>
<nav class="messageTabMenuNavigation jsOnly">
<ul>
{foreach from=$container item='child'}
{if $child->isAvailable()}
<li data-name="{$child->getName()}"{if !$child->checkDependencies()} hidden{/if}>
<button type="button">
{if $child->getIcon()}{icon name=$child->getIcon()}{/if}
<span>{@$child->getLabel()}</span>
</button>
</li>
{/if}
{/foreach}
</ul>
</nav>

{include file='shared_formContainerChildren'}
</div>

{include file='shared_formContainerDependencies'}

<script data-relocate="true">
require(['WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/WysiwygTabMenu'], ({ WysiwygTabMenu }) => {
new WysiwygTabMenu('{@$container->getPrefixedId()|encodeJS}Container');
});
</script>
37 changes: 37 additions & 0 deletions ts/WoltLabSuite/Core/Api/Messages/Author.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Requests render a full quote of a message.
*
* @author Olaf Braun
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
* @woltlabExcludeBundle tiny
*/

import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "../Result";

type Response = {
objectID: number;
authorID: number;
author: string;
time: string;
title: string;
link: string;
avatar: string;
};

export async function messageAuthor(className: string, objectID: number): Promise<ApiResult<Response>> {
const url = new URL(window.WSC_RPC_API_URL + "core/messages/messageauthor");
url.searchParams.set("className", className);
url.searchParams.set("objectID", objectID.toString());

let response: Response;
try {
response = (await prepareRequest(url).get().allowCaching().fetchAsJson()) as Response;
} catch (e) {
return apiResultFromError(e);
}

return apiResultFromValue(response);
}
45 changes: 45 additions & 0 deletions ts/WoltLabSuite/Core/Api/Messages/RenderQuote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Requests render a full quote of a message.
*
* @author Olaf Braun
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
* @woltlabExcludeBundle tiny
*/

import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "../Result";

type Response = {
objectID: number;
authorID: number | null;
author: string;
time: string;
link: string;
title: string;
avatar: string;
message: string | null;
rawMessage: string | null;
};

export async function renderQuote(
objectType: string,
className: string,
objectID: number,
): Promise<ApiResult<Response>> {
const url = new URL(window.WSC_RPC_API_URL + "core/messages/renderquote");
url.searchParams.set("objectType", objectType);
url.searchParams.set("className", className);
url.searchParams.set("fullQuote", "true");
url.searchParams.set("objectID", objectID.toString());

let response: Response;
try {
response = (await prepareRequest(url).get().fetchAsJson()) as Response;
} catch (e) {
return apiResultFromError(e);
}

return apiResultFromValue(response);
}
24 changes: 24 additions & 0 deletions ts/WoltLabSuite/Core/Api/Messages/ResetRemovalQuotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Requests to reset the removal quotes.
*
* @author Olaf Braun
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
* @woltlabExcludeBundle tiny
*/

import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "../Result";

export async function resetRemovalQuotes(): Promise<ApiResult<[]>> {
const url = new URL(window.WSC_RPC_API_URL + "core/messages/reset-removal-quotes");

try {
await prepareRequest(url).post().fetchAsJson();
} catch (e) {
return apiResultFromError(e);
}

return apiResultFromValue([]);
}
16 changes: 16 additions & 0 deletions ts/WoltLabSuite/Core/BootstrapFrontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ interface BootstrapOptions {
executeCronjobs: string | undefined;
shareButtonProviders?: ShareProvider[];
styleChanger: boolean;
removeQuotes?: string[];
usedQuotes?: Map<string, string[]>;
}

/**
Expand Down Expand Up @@ -86,6 +88,20 @@ export function setup(options: BootstrapOptions): void {
enableMobileMenu: true,
pageMenuMainProvider: new UiPageMenuMainFrontend(),
});

if (options.removeQuotes?.length) {
void import("./Component/Quote/Storage").then(({ removeQuotes }) => removeQuotes(options.removeQuotes!));
}
if (options.usedQuotes?.size) {
void import("./Component/Quote/Storage").then(({ markQuoteAsUsed }) => {
options.usedQuotes!.forEach((uuids, editorId) => {
for (const uuid of uuids) {
markQuoteAsUsed(editorId, uuid);
}
});
});
}

UiPageHeaderMenu.init();

if (options.styleChanger) {
Expand Down
4 changes: 4 additions & 0 deletions ts/WoltLabSuite/Core/Component/Ckeditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ class Ckeditor {
get sourceElement(): HTMLElement {
return this.#editor.sourceElement!;
}

get focusTracker(): CKEditor5.Utils.FocusTracker {
return this.#editor.ui.focusTracker;
}
}

function* findModelForRemoval(
Expand Down
4 changes: 4 additions & 0 deletions ts/WoltLabSuite/Core/Component/Comment/Add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { listenToCkeditor } from "../Ckeditor/Event";
import { createComment } from "WoltLabSuite/Core/Api/Comments/CreateComment";
import { getGuestToken } from "../GuestTokenDialog";
import User from "WoltLabSuite/Core/User";
import { clearQuotesForEditor } from "WoltLabSuite/Core/Component/Quote/Storage";
import { setActiveEditor } from "WoltLabSuite/Core/Component/Quote/Message";

type CallbackInsertComment = (commentId: number) => void;

Expand Down Expand Up @@ -180,6 +182,8 @@ export class CommentAdd {
*/
#reset(): void {
this.#getEditor().reset();
clearQuotesForEditor(this.#getEditor().sourceElement.id);
setActiveEditor();

if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur();
Expand Down
4 changes: 4 additions & 0 deletions ts/WoltLabSuite/Core/Component/Comment/Response/Add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { listenToCkeditor } from "../../Ckeditor/Event";
import User from "WoltLabSuite/Core/User";
import { getGuestToken } from "../../GuestTokenDialog";
import { createResponse } from "WoltLabSuite/Core/Api/Comments/Responses/CreateResponse";
import { clearQuotesForEditor } from "WoltLabSuite/Core/Component/Quote/Storage";
import { setActiveEditor } from "WoltLabSuite/Core/Component/Quote/Message";

type CallbackInsertResponse = (commentId: number, responseId: number) => void;

Expand Down Expand Up @@ -131,6 +133,8 @@ export class CommentResponseAdd {
*/
#reset(): void {
this.#getEditor().reset();
clearQuotesForEditor(this.#getEditor().sourceElement.id);
setActiveEditor();

if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur();
Expand Down
Loading
Loading