diff --git a/app/assets/javascripts/pretty-text/censored-words.js.es6 b/app/assets/javascripts/pretty-text/censored-words.js.es6 index ffb0cce3aeb..fc5254f58de 100644 --- a/app/assets/javascripts/pretty-text/censored-words.js.es6 +++ b/app/assets/javascripts/pretty-text/censored-words.js.es6 @@ -2,14 +2,17 @@ function escapeRegexp(text) { return text.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&').replace(/\*/g, "\S*"); } -export function censorFn(censoredWords, censoredPattern, replacementLetter) { +export function censorFn(censoredWords, censoredPattern, replacementLetter, watchedWordsRegularExpressions) { let patterns = []; replacementLetter = replacementLetter || "■"; if (censoredWords && censoredWords.length) { - patterns = censoredWords.split("|").map(t => `(${escapeRegexp(t)})`); + patterns = censoredWords.split("|"); + if (!watchedWordsRegularExpressions) { + patterns = patterns.map(t => `(${escapeRegexp(t)})`); + } } if (censoredPattern && censoredPattern.length > 0) { @@ -20,7 +23,11 @@ export function censorFn(censoredWords, censoredPattern, replacementLetter) { let censorRegexp; try { - censorRegexp = new RegExp("(\\b(?:" + patterns.join("|") + ")\\b)(?![^\\(]*\\))", "ig"); + if (watchedWordsRegularExpressions) { + censorRegexp = new RegExp("((?:" + patterns.join("|") + "))(?![^\\(]*\\))", "ig"); + } else { + censorRegexp = new RegExp("(\\b(?:" + patterns.join("|") + ")\\b)(?![^\\(]*\\))", "ig"); + } if (censorRegexp) { @@ -33,7 +40,11 @@ export function censorFn(censoredWords, censoredPattern, replacementLetter) { while (m && m[0]) { if (m[0].length > original.length) { return original; } // regex is dangerous const replacement = new Array(m[0].length+1).join(replacementLetter); - text = text.replace(new RegExp(`(\\b${escapeRegexp(m[0])}\\b)(?![^\\(]*\\))`, "ig"), replacement); + if (watchedWordsRegularExpressions) { + text = text.replace(new RegExp(`(${escapeRegexp(m[0])})(?![^\\(]*\\))`, "ig"), replacement); + } else { + text = text.replace(new RegExp(`(\\b${escapeRegexp(m[0])}\\b)(?![^\\(]*\\))`, "ig"), replacement); + } m = censorRegexp.exec(text); } diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 index 91a0d297ba0..b9a993b8bf0 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 @@ -25,6 +25,7 @@ function censorTree(state, censor) { export function setup(helper) { helper.registerOptions((opts, siteSettings) => { opts.censoredPattern = siteSettings.censored_pattern; + opts.watchedWordsRegularExpressions = siteSettings.watched_words_regular_expressions; }); helper.registerPlugin(md => { @@ -33,7 +34,7 @@ export function setup(helper) { if ((words && words.length > 0) || (patterns && patterns.length > 0)) { const replacement = String.fromCharCode(9632); - const censor = censorFn(words, patterns, replacement); + const censor = censorFn(words, patterns, replacement, md.options.discourse.watchedWordsRegularExpressions); md.core.ruler.push('censored', state => censorTree(state, censor)); } }); diff --git a/config/site_settings.yml b/config/site_settings.yml index acb9877b6e9..9ea097e0acf 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -633,6 +633,7 @@ posting: default: 30 min: 1 watched_words_regular_expressions: + client: true default: false enable_advanced_editor_preview_sync: hidden: true diff --git a/test/javascripts/lib/pretty-text-test.js.es6 b/test/javascripts/lib/pretty-text-test.js.es6 index 89704ee4cbd..8bcf2046065 100644 --- a/test/javascripts/lib/pretty-text-test.js.es6 +++ b/test/javascripts/lib/pretty-text-test.js.es6 @@ -607,6 +607,17 @@ QUnit.test("censoring", assert => { assert.cooked("No badword or apple here plz.", "

No ■■■■■■■ or ■■■■■ here plz.

", "it handles * as wildcard"); + + assert.cookedOptions( + "Pleased to meet you, but pleeeease call me later, xyz123", + { siteSettings: { + watched_words_regular_expressions: true, + censored_pattern: null + }, + censoredWords: 'xyz*|plee+ase' + }, + "

Pleased to meet you, but ■■■■■■■■■ call me later, ■■■123

", + "supports words as regular expressions"); }); QUnit.test("code blocks/spans hoisting", assert => {