FIX: Which topic do you want to reply to rendering raw HTML (#30962)

Because of
2b63830496
`Which topic do you want to reply to rendering HTML` was rendering raw
HTML.

Added `htmlSafe` for now.

I'll work on testing for this feature.
This commit is contained in:
Gabriel Grubba 2025-01-23 20:37:48 -03:00 committed by GitHub
parent 1b9e2ff4f9
commit e78c937a5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 49 deletions

View File

@ -0,0 +1,76 @@
import Component from "@glimmer/component";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import { eq } from "truth-helpers";
import DButton from "discourse/components/d-button";
import { categoryLinkHTML } from "discourse/helpers/category-link";
import dIcon from "discourse/helpers/d-icon";
import discourseTags from "discourse/helpers/discourse-tags";
const TopicLabelButton = <template>
<DButton class={{@class}} @action={{@action}}>
<div class="topic-title">
<div class="topic-title__top-line">
<span class="topic-statuses">
{{#if (eq @topic.archetype "private_message")}}
<span class="topic-status">
{{dIcon "envelope"}}
</span>
{{/if}}
{{#if @topic.bookmarked}}
<span class="topic-status">
{{dIcon "bookmark"}}
</span>
{{/if}}
{{#if @topic.closed}}
<span class="topic-status">
{{dIcon "lock"}}
</span>
{{/if}}
{{#if @topic.pinned}}
<span class="topic-status">
{{dIcon "thumbtack"}}
</span>
{{/if}}
</span>
<span class="fancy-title">
{{@topic.title}}
</span>
</div>
<div class="topic-title__bottom-line">
{{categoryLinkHTML @topic (hash link=false)}}
{{discourseTags @topic}}
</div>
</div>
</DButton>
</template>;
export default class TopicLabelContent extends Component {
@action
replyOnOriginal() {
this.args.model.replyOnOriginal();
}
@action
replyOnCurrent() {
this.args.model.replyOnCurrent();
}
<template>
<TopicLabelButton
@class="btn-primary btn-reply-where btn-reply-on-original"
@action={{this.replyOnOriginal}}
@topic={{@model.originalTopic}}
/>
<TopicLabelButton
@class="btn-reply-where btn-reply-here"
@action={{this.replyOnCurrent}}
@topic={{@model.currentTopic}}
/>
</template>
}

View File

@ -2,7 +2,6 @@ import EmberObject, { action, computed } from "@ember/object";
import { alias, and, or, reads } from "@ember/object/computed";
import { cancel, scheduleOnce } from "@ember/runloop";
import Service, { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import { isEmpty } from "@ember/utils";
import { observes, on } from "@ember-decorators/object";
import $ from "jquery";
@ -10,7 +9,7 @@ import { Promise } from "rsvp";
import DiscardDraftModal from "discourse/components/modal/discard-draft";
import PostEnqueuedModal from "discourse/components/modal/post-enqueued";
import SpreadsheetEditor from "discourse/components/modal/spreadsheet-editor";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import TopicLabelContent from "discourse/components/topic-label-content";
import {
cannotPostAgain,
durationTextFromSeconds,
@ -26,11 +25,9 @@ import prepareFormTemplateData, {
import { shortDate } from "discourse/lib/formatter";
import { getOwnerWithFallback } from "discourse/lib/get-owner";
import getURL from "discourse/lib/get-url";
import { iconHTML } from "discourse/lib/icon-library";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { buildQuote } from "discourse/lib/quote";
import renderTags from "discourse/lib/render-tags";
import { emojiUnescape } from "discourse/lib/text";
import {
authorizesOneOrMoreExtensions,
@ -1092,58 +1089,27 @@ export default class ComposerService extends Service {
return;
}
const topicLabelContent = function (topicOption) {
const topicClosed = topicOption.closed
? `<span class="topic-status">${iconHTML("lock")}</span>`
: "";
const topicPinned = topicOption.pinned
? `<span class="topic-status">${iconHTML("thumbtack")}</span>`
: "";
const topicBookmarked = topicOption.bookmarked
? `<span class="topic-status">${iconHTML("bookmark")}</span>`
: "";
const topicPM =
topicOption.archetype === "private_message"
? `<span class="topic-status">${iconHTML("envelope")}</span>`
: "";
return `<div class="topic-title">
<div class="topic-title__top-line">
<span class="topic-statuses">
${topicPM}${topicBookmarked}${topicClosed}${topicPinned}
</span>
<span class="fancy-title">
${topicOption.fancyTitle}
</span>
</div>
<div class="topic-title__bottom-line">
${categoryBadgeHTML(topicOption.category, {
link: false,
})}${htmlSafe(renderTags(topicOption))}
</div>
</div>`;
};
if (
currentTopic.id !== composer.get("topic.id") &&
(this.isStaffUser || !currentTopic.closed)
) {
this.dialog.alert({
title: i18n("composer.posting_not_on_topic"),
bodyComponent: TopicLabelContent,
bodyComponentModel: {
originalTopic,
replyOnOriginal: () => {
this.save(true);
this.dialog.didConfirmWrapped();
},
currentTopic,
replyOnCurrent: () => {
composer.setProperties({ topic: currentTopic, post: null });
this.save(true);
this.dialog.didConfirmWrapped();
},
},
buttons: [
{
label: topicLabelContent(originalTopic),
class: "btn-primary btn-reply-where btn-reply-on-original",
action: () => this.save(true),
},
{
label: topicLabelContent(currentTopic),
class: "btn-reply-where btn-reply-here",
action: () => {
composer.setProperties({ topic: currentTopic, post: null });
this.save(true);
},
},
{
label: i18n("composer.cancel"),
class: "btn-flat btn-text btn-reply-where__cancel",

View File

@ -406,6 +406,8 @@ acceptance("Composer", function (needs) {
await click("#reply-control button.create");
assert.dom(".reply-where-modal").exists("pops up a modal");
assert.dom(".topic-title").exists({ count: 2 }); // modal buttons
await click(".btn-reply-here");
assert
.dom(".topic-post:last-of-type .cooked p")