Ich versuche, anonyme Beiträge noch anonymer zu gestalten, indem ich die Anzeige von Zitaten in Kategorien überschreibe, in denen nur das anonyme Posten erlaubt ist. Anstatt des Standardformats
anonymous4302934
ein zitiertes Textstück
möchte ich folgendes erreichen:
Anonymous:
ein zitiertes Textstück
Ich kann dies für Text umsetzen, der über die Symbolleisten-Schaltfläche zitiert wird, basierend auf How to override the buildQuote function? - #7 by Canapin (ich musste unten einen Container-Lookup hinzufügen, damit es gemäß der Workaround hier funktioniert: api.modifyClass sometimes(!) not working - #12 by RGJ).
Ich habe mich durch die Kern-Dateien gearbeitet, kann aber nicht genau herausfinden, wo das Zitat erstellt wird, wenn ein Zitat über die Auswahl und das Klicken auf die Zitat-Schaltfläche in den Komponisten eingefügt wird. Ich sehe in _selectionChanged() in quote-button.js, dass opts.username gesetzt wird, wenn der ausgewählte Text in einem Blockzitat steht. Wenn ich jedoch versuche, dies zu ändern, um opts.username manuell zu setzen, hat dies keine Auswirkung.
Ist dies der richtige Ansatz, um dies zu überschreiben, oder ist modifyClass der richtige Weg, um dies anzugehen?
<script type="text/discourse-plugin" version="0.8">
const controller = api.container.lookup('controller:topic');
const anon_categories = [5, 6, 14, 15, 16, 17, 18]
function buildQuote(post, contents, opts = {}) {
if (!post || !contents) {
return "";
}
let anon_name = ``;
// console.log(anon_categories, controller.get("model.category_id"))
if (anon_categories.includes(controller.get("model.category_id"))) {
anon_name = `Anonymous`;
} else {
anon_name = opts.username || post.username;
}
const params = [
anon_name,
`post:${opts.post || post.post_number}`,
`topic:${opts.topic || post.topic_id}`
];
if (opts.full) params.push("full:true");
return `\n[quote="${params.join(", ")}"]\n${contents.trim()}\n[/quote]\n\n`;
}
api.modifyClass('controller:composer', {
pluginId: 'anonymize-quotes',
actions: {
importQuote(toolbarEvent) {
const postStream = this.get("topic.postStream");
let postId = this.get("model.post.id");
// Wenn es keinen aktuellen Beitrag gibt, verwende die erste Post-ID aus dem Stream
if (!postId && postStream) {
postId = postStream.get("stream.firstObject");
}
// Wenn wir einen Beitrag bearbeiten, rufe die Antwort beim Import eines Zitats ab
if (this.get("model.editingPost")) {
const replyToPostNumber = this.get("model.post.reply_to_post_number");
if (replyToPostNumber) {
const replyPost = postStream.posts.findBy(
"post_number",
replyToPostNumber
);
if (replyPost) {
postId = replyPost.id;
}
}
}
if (postId) {
this.set("model.loading", true);
return this.store.find("post", postId).then(post => {
const quote = buildQuote(post, post.raw, {
full: true
});
toolbarEvent.addText(quote);
this.set("model.loading", false);
});
}
}
}
});
api.modifyClass('component:quote-button', {
pluginId: 'anonymize-quotes',
actions: {
_selectionChanged() {
if (this._displayFastEditInput) {
return;
}
const quoteState = this.quoteState;
const selection = window.getSelection();
if (selection.isCollapsed) {
if (this.visible) {
this._hideButton();
}
return;
}
// Stellen sicher, dass wir Inhalte innerhalb von nur 1 Beitrag ausgewählt haben
let firstRange, postId;
for (let r = 0; r < selection.rangeCount; r++) {
const range = selection.getRangeAt(r);
const $selectionStart = $(range.startContainer);
const $ancestor = $(range.commonAncestorContainer);
if ($selectionStart.closest(".cooked").length === 0) {
return;
}
firstRange = firstRange || range;
postId = postId || $ancestor.closest(".boxed, .reply").data("post-id");
if ($ancestor.closest(".contents").length === 0 || !postId) {
if (this.visible) {
this._hideButton();
}
return;
}
}
const _selectedElement = selectedElement();
const _selectedText = selectedText();
const $selectedElement = $(_selectedElement);
const cooked =
$selectedElement.find(".cooked")[0] ||
$selectedElement.closest(".cooked")[0];
// Die Berechnung von Markdown dauert bei langen Beiträgen sehr lange
// Dieser Code versucht, dies nur zu berechnen, wenn kein schneller Pfad möglich ist
let opts = {
full:
selectedRange().startOffset > 0
? false
: _selectedText === toMarkdown(cooked.innerHTML),
};
for (
let element = _selectedElement;
element && element.tagName !== "ARTICLE";
element = element.parentElement
) {
if (element.tagName === "ASIDE" && element.classList.contains("quote")) {
opts.username = element.dataset.username || getQuoteTitle(element);
opts.post = element.dataset.post;
opts.topic = element.dataset.topic;
break;
}
}
opts.username = `Anonymous`
quoteState.selected(postId, _selectedText, opts);
this.set("visible", quoteState.buffer.length > 0);
if (this.siteSettings.enable_fast_edit) {
this.set(
"_canEditPost",
this.topic.postStream.findLoadedPost(postId)?.can_edit
);
if (this._canEditPost) {
const regexp = new RegExp(regexSafeStr(quoteState.buffer), "gi");
const matches = cooked.innerHTML.match(regexp);
if (
quoteState.buffer.length < 1 ||
quoteState.buffer.includes("|") || // Tabellen sind zu komplex
quoteState.buffer.match(/\n/g) || // Zeilenumbrüche sind zu komplex
matches?.length > 1 // Duplikate sind zu komplex
) {
this.set("_isFastEditable", false);
this.set("_fastEditInitalSelection", null);
this.set("_fastEditNewSelection", null);
} else if (matches?.length === 1) {
this.set("_isFastEditable", true);
this.set("_fastEditInitalSelection", quoteState.buffer);
this.set("_fastEditNewSelection", quoteState.buffer);
}
}
}
// Vermeide harte Schleifen bei der Zitauswahl bedingungslos
// Dies kann passieren, wenn man in Firefox Text dreimal anklickt
if (this._prevSelection === _selectedText) {
return;
}
this._prevSelection = _selectedText;
// Auf Desktop wird die Schaltfläche am Anfang der Auswahl angezeigt
// Auf Mobile wird die Schaltfläche am Ende der Auswahl angezeigt
const isMobileDevice = this.site.isMobileDevice;
const { isIOS, isAndroid, isOpera } = this.capabilities;
const showAtEnd = isMobileDevice || isIOS || isAndroid || isOpera;
const boundaryPosition = this._getRangeBoundaryRect(firstRange, showAtEnd);
// Ändere die Position der Schaltfläche
schedule("afterRender", () => {
if (!this.element || this.isDestroying || this.isDestroyed) {
return;
}
let top = 0;
let left = 0;
const pxFromSelection = 5;
if (showAtEnd) {
// Die Auswahlgriffe auf iOS haben eine Trefferfläche von ca. 50px Radius
// Daher müssen wir sicherstellen, dass unsere Schaltflächen außerhalb dieses Radius liegen
// Wende dieselbe Logik auf allen mobilen Geräten für Konsistenz an
top = boundaryPosition.bottom + pxFromSelection;
left = boundaryPosition.left;
const safeRadius = 50;
const topicArea = document
.querySelector(".topic-area")
.getBoundingClientRect();
topicArea.x += document.documentElement.scrollLeft;
topicArea.y += document.documentElement.scrollTop;
const endHandlePosition = boundaryPosition;
const width = this.element.clientWidth;
const possiblePositions = [
{
// nach links verschieben
top,
left: left - width - safeRadius,
},
{
// nach rechts verschieben
top,
left: left + safeRadius,
},
{
// zentriert unter dem End-Griff
top: top + safeRadius,
left: left - width / 2,
},
];
for (const pos of possiblePositions) {
// Stellen sicher, dass die Schaltflächen vollständig innerhalb des .topic-area liegen
pos.left = Math.max(topicArea.left, pos.left);
pos.left = Math.min(topicArea.right - width, pos.left);
let clearOfStartHandle = true;
if (isAndroid) {
// Auf Android erstreckt sich der Start-Auswahlgriff unter die Linie, daher müssen wir ihn ebenfalls vermeiden:
const startHandlePosition = this._getRangeBoundaryRect(
firstRange,
false
);
clearOfStartHandle =
pos.top - startHandlePosition.bottom >= safeRadius ||
pos.left + width <= startHandlePosition.left - safeRadius ||
pos.left >= startHandlePosition.left + safeRadius;
}
const clearOfEndHandle =
pos.top - endHandlePosition.top >= safeRadius ||
pos.left + width <= endHandlePosition.left - safeRadius ||
pos.left >= endHandlePosition.left + safeRadius;
if (clearOfStartHandle && clearOfEndHandle) {
left = pos.left;
top = pos.top;
break;
}
}
} else {
// Desktop
top =
boundaryPosition.top - this.element.clientHeight - pxFromSelection;
left = boundaryPosition.left;
}
Object.assign(this.element.style, { top: `${top}px`, left: `${left}px` });
if (!this.animated) {
// Wir aktivieren CSS-Übergänge erst nach der initialen Positionierung
// Andernfalls kann die Schaltfläche so wirken, als würde sie von außerhalb des Bildschirms fliegen
next(() => this.set("animated", true));
}
});
}
}
});
const composerController = api.container.lookup("controller:composer");
const componentQuoteButton = api.container.lookup("component:quote-button");
</script>