أحاول جعل المنشورات المجهولة أكثر مجهولية من خلال تجاوز كيفية عرض الاقتباسات في الفئات التي تم تمكين النشر المجهول فيها فقط. لذا، بدلاً من الافتراضي:
anonymous4302934
نص مقتبس ما
أريد أن يكون:
مجهول:
نص مقتبس ما
أستطيع فعل ذلك مع النص الذي يتم اقتباسه عبر زر شريط الأدوات، استنادًا إلى How to override the buildQuote function? - #7 by Canapin (كان علي إضافة بحث عن حاوية في الأسفل لجعل الأمر يعمل وفقًا للحل البديل هنا: api.modifyClass sometimes(!) not working - #12 by RGJ)
لقد بحثت في الملفات الأساسية ولا أستطيع تحديد المكان الذي يتم فيه بناء الاقتباس بالضبط عند إدراج اقتباس في المؤلّف عبر التحديد والنقر على زر الاقتباس. أرى في _selectionChanged() في quote-button.js أن opts.username يُعيين إذا كان النص المحدد داخل اقتباس، لكن إذا حاولت تعديله لتعيين opts.username يدويًا، فلا يؤثر ذلك على شيء.
هل هذا هو الشيء الصحيح الذي يجب محاولة تجاوزه؟ هل modifyClass هو النهج الصحيح للتعامل مع هذا؟
<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");
// If there is no current post, use the first post id from the stream
if (!postId && postStream) {
postId = postStream.get("stream.firstObject");
}
// If we're editing a post, fetch the reply when importing a quote
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;
}
// ensure we selected content inside 1 post *only*
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];
// computing markdown takes a lot of time on long posts
// this code attempts to compute it only when we can't fast track
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("|") || // tables are too complex
quoteState.buffer.match(/\n/g) || // linebreaks are too complex
matches?.length > 1 // duplicates are too complex
) {
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);
}
}
}
// avoid hard loops in quote selection unconditionally
// this can happen if you triple click text in firefox
if (this._prevSelection === _selectedText) {
return;
}
this._prevSelection = _selectedText;
// on Desktop, shows the button at the beginning of the selection
// on Mobile, shows the button at the end of the selection
const isMobileDevice = this.site.isMobileDevice;
const { isIOS, isAndroid, isOpera } = this.capabilities;
const showAtEnd = isMobileDevice || isIOS || isAndroid || isOpera;
const boundaryPosition = this._getRangeBoundaryRect(firstRange, showAtEnd);
// change the position of the button
schedule("afterRender", () => {
if (!this.element || this.isDestroying || this.isDestroyed) {
return;
}
let top = 0;
let left = 0;
const pxFromSelection = 5;
if (showAtEnd) {
// The selection-handles on iOS have a hit area of ~50px radius
// so we need to make sure our buttons are outside that radius
// Apply the same logic on all mobile devices for consistency
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 = [
{
// move to left
top,
left: left - width - safeRadius,
},
{
// move to right
top,
left: left + safeRadius,
},
{
// centered below end handle
top: top + safeRadius,
left: left - width / 2,
},
];
for (const pos of possiblePositions) {
// Ensure buttons are entirely within the .topic-area
pos.left = Math.max(topicArea.left, pos.left);
pos.left = Math.min(topicArea.right - width, pos.left);
let clearOfStartHandle = true;
if (isAndroid) {
// On android, the start-selection handle extends below the line, so we need to avoid it as well:
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) {
// We only enable CSS transitions after the initial positioning
// otherwise the button can appear to fly in from off-screen
next(() => this.set("animated", true));
}
});
}
}
});
const composerController = api.container.lookup("controller:composer");
const componentQuoteButton = api.container.lookup("component:quote-button");
</script>