From 3927b27f34fc6959e0965b2c848a404b18ae8f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 24 Jun 2024 12:33:24 +0200 Subject: [PATCH] FIX: allow quote-less details BBCode In 53b3d2f0dc460348a4c23e12f843ccf11426a080 we introduced a stricter BBCode Tag parser. It prevents having "values" with spaces when they're not surrounded by a valid pair of quotes. The `[details=` BBCode Tag is popular enough that it's worth adding a special case for it (especially since it doesn't support other parameters). This also adds the Finnish pair of quotes. Context - https://meta.discourse.org/t/details-accepts-only-one-word-as-summary/313019 --- .../src/features/bbcode-block.js | 27 ++++++++------- .../javascripts/lib/details-cooked-test.js | 34 ++++++++----------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js b/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js index 8e847f4d968..763e597ab5d 100644 --- a/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js +++ b/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-block.js @@ -34,15 +34,9 @@ function trailingSpaceOnly(src, start, max) { return true; } -// Easiest case is the closing tag which never has any attributes -const BBCODE_CLOSING_TAG_REGEXP = /^\[\/([-\w]+)\]/i; - -// Old case where we supported attributes without quotation marks -const BBCODE_QUOTE_TAG_REGEXP = /^\[quote=([-\w,: ]+)\]/i; - // Most common quotation marks. // More can be found at https://en.wikipedia.org/wiki/Quotation_mark -const QUOTATION_MARKS = [`""`, `''`, `“”`, `‘’`, `„“`, `‚’`, `«»`, `‹›`]; +const QUOTATION_MARKS = [`""`, `''`, `“”`, `””`, `‘’`, `„“`, `‚’`, `«»`, `‹›`]; const QUOTATION_MARKS_NO_MATCH = QUOTATION_MARKS.map( ([a, b]) => `${a}[^${b}]+${b}` @@ -52,6 +46,15 @@ const QUOTATION_MARKS_WITH_MATCH = QUOTATION_MARKS.map( ([a, b]) => `${a}([^${b}]+)${b}` ).join("|"); +// Easiest case is the closing tag which never has any attributes +const BBCODE_CLOSING_TAG_REGEXP = /^\[\/([-\w]+)\]/i; + +// Old case where we supported attributes without quotation marks +const BBCODE_QUOTE_OR_DETAILS_TAG_REGEXP = new RegExp( + `^\\[(quote|details)=(\\s*[^${QUOTATION_MARKS.join("")}].+?)\\]`, + "i" +); + // This is used to match a **valid** opening tag // NOTE: it does not match the closing bracket "]" because it makes the regexp too slow // due to the backtracking. So we check for the "]" manually. @@ -86,18 +89,18 @@ export function parseBBCodeTag(src, start, max, multiline) { }; } - // CASE 2 - [quote=...] tag (without quotes) - m = BBCODE_QUOTE_TAG_REGEXP.exec(text); + // CASE 2 - [quote=...] or [details=...] tag (without quotes) + m = BBCODE_QUOTE_OR_DETAILS_TAG_REGEXP.exec(text); - if (m && m[0] && m[1]) { + if (m && m[0] && m[1] && m[2]) { if (multiline && !trailingSpaceOnly(src, start + m[0].length, max)) { return null; } return { - tag: "quote", + tag: m[1], length: m[0].length, - attrs: { _default: m[1] }, + attrs: { _default: m[2] }, }; } diff --git a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js index c8a999e1c48..f8b22a38688 100644 --- a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js +++ b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js @@ -1,23 +1,16 @@ +import { setupTest } from "ember-qunit"; import { module, test } from "qunit"; import { cook } from "discourse/lib/text"; -const opts = { - siteSettings: { - enable_emoji: true, - emoji_set: "twitter", - highlighted_languages: "json|ruby|javascript", - default_code_lang: "auto", - }, - censoredWords: "shucks|whiz|whizzer", - getURL: (url) => url, -}; +module("lib:details-cooked-test", (hooks) => { + setupTest(hooks); -module("lib:details-cooked-test", function () { - test("details", async function (assert) { + test("details", async (assert) => { const testCooked = async (input, expected, text) => { - const cooked = (await cook(input, opts)).toString(); + const cooked = (await cook(input)).toString(); assert.strictEqual(cooked, expected, text); }; + await testCooked( `
Infocoucou
`, `
Infocoucou
`, @@ -25,12 +18,15 @@ module("lib:details-cooked-test", function () { ); await testCooked( - "[details=testing]\ntest\n[/details]", - `
- -testing -

test

-
` + `[details=test'ing all the things]\ntest\n[/details]`, + `
\n\ntest'ing all the things\n

test

\n
`, + "details with spaces and a single quote" + ); + + await testCooked( + `[details=”test'ing all the things”]\ntest\n[/details]`, + `
\n\ntest'ing all the things\n

test

\n
`, + "details surrounded by finnish double quotes" ); }); });