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(
`Info
coucou `,
`Info
coucou `,
@@ -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
\ntest
\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
\ntest
\n `,
+ "details surrounded by finnish double quotes"
);
});
});