FIX: Do not escape `fancy_title` again. (#8095)

FIX: Do not escape fancy_title again. (#8095)

fancy_title is already escaped by Rails. Escaping it again would print the HTML entity as-is, e.g. " instead of ".

This fixes the issue by introducing a new escapedContent attribute on the QuickAccessItem widget.

diff --git a/app/assets/javascripts/discourse/widgets/quick-access-item.js.es6 b/app/assets/javascripts/discourse/widgets/quick-access-item.js.es6
index a869484..a37200e 100644
--- a/app/assets/javascripts/discourse/widgets/quick-access-item.js.es6
+++ b/app/assets/javascripts/discourse/widgets/quick-access-item.js.es6
@@ -3,7 +3,22 @@ import RawHtml from "discourse/widgets/raw-html";
 import { createWidget } from "discourse/widgets/widget";
 import { emojiUnescape } from "discourse/lib/text";
 import { iconNode } from "discourse-common/lib/icon-library";
+import { escapeExpression } from "discourse/lib/utilities";
 
+/**
+ * This helper widget tries to enforce a consistent look and behavior for any
+ * item under any quick access panels.
+ *
+ * It accepts the following attributes:
+ *   action
+ *   actionParam
+ *   content
+ *   escapedContent
+ *   href
+ *   icon
+ *   read
+ *   username
+ */
 createWidget("quick-access-item", {
   tagName: "li",
 
@@ -18,13 +33,11 @@ createWidget("quick-access-item", {
     return result;
   },
 
-  html({ icon, href, content }) {
+  html({ icon, href }) {
     return h("a", { attributes: { href } }, [
       iconNode(icon),
       new RawHtml({
-        html: `<div>${this._usernameHtml()}${emojiUnescape(
-          Handlebars.Utils.escapeExpression(content)
-        )}</div>`
+        html: `<div>${this._usernameHtml()}${this._contentHtml()}</div>`
       })
     ]);
   },
@@ -37,6 +50,12 @@ createWidget("quick-access-item", {
     }
   },
 
+  _contentHtml() {
+    const content =
+      this.attrs.escapedContent || escapeExpression(this.attrs.content);
+    return emojiUnescape(content);
+  },
+
   _usernameHtml() {
     return this.attrs.username ? `<span>${this.attrs.username}</span> ` : "";
   }
diff --git a/app/assets/javascripts/discourse/widgets/quick-access-messages.js.es6 b/app/assets/javascripts/discourse/widgets/quick-access-messages.js.es6
index 9988e64..e8431bc 100644
--- a/app/assets/javascripts/discourse/widgets/quick-access-messages.js.es6
+++ b/app/assets/javascripts/discourse/widgets/quick-access-messages.js.es6
@@ -12,7 +12,7 @@ function toItem(message) {
   );
 
   return {
-    content: message.fancy_title,
+    escapedContent: message.fancy_title,
     href: postUrl(message.slug, message.id, nextUnreadPostNumber),
     icon: ICON,
     read: message.last_read_post_number >= message.highest_post_number,
diff --git a/test/javascripts/widgets/quick-access-item-test.js.es6 b/test/javascripts/widgets/quick-access-item-test.js.es6
new file mode 100644
index 0000000..19da5ef
--- /dev/null
+++ b/test/javascripts/widgets/quick-access-item-test.js.es6
@@ -0,0 +1,31 @@
+import { moduleForWidget, widgetTest } from "helpers/widget-test";
+
+moduleForWidget("quick-access-item");
+
+const CONTENT_DIV_SELECTOR = "li > a > div";
+
+widgetTest("content attribute is escaped", {
+  template: '{{mount-widget widget="quick-access-item" args=args}}',
+
+  beforeEach() {
+    this.set("args", { content: "<b>bold</b>" });
+  },
+
+  test(assert) {
+    const contentDiv = find(CONTENT_DIV_SELECTOR)[0];
+    assert.equal(contentDiv.innerText, "<b>bold</b>");
+  }
+});
+
+widgetTest("escapedContent attribute is not escaped", {
+  template: '{{mount-widget widget="quick-access-item" args=args}}',
+
+  beforeEach() {
+    this.set("args", { escapedContent: "&quot;quote&quot;" });
+  },
+
+  test(assert) {
+    const contentDiv = find(CONTENT_DIV_SELECTOR)[0];
+    assert.equal(contentDiv.innerText, '"quote"');
+  }
+});

GitHub sha: f0f03acb

1 Like