Replace a string in all posts

Want to replace a string in all the posts? Let’s do it!

Access your site

First connect to your Droplet via SSH, and enter the Docker container for your Discourse instances:

cd /var/discourse
./launcher enter app

:warning: WARNING

We strongly recommend you take a full backup before proceeding, and make sure your string replacement is specific enough to affect only the places you want it to. If this string replacement goes wrong, every post on your site will look broken!

Remap all posts containing a specific string

Basic Case-sensitive Remap
Run the following command, substituting find with the string you wish to replace and replace with the replacement string. Note that this does not respect word boundaries.

rake posts:remap["find","replace"]

Example results:

find —> replace
Find —> Find
finders keepers —> replaceers keepers
finding —> replaceing

This method can be useful for tasks such as replacing emojis:

rake posts:remap[":slightly_smiling:",":slight_smile:"]

The above command will replace all occurrences of :slightly_smiling: with :slight_smile:.

Case-insensitive Remap

rake posts:remap["find","replace","string",true]

Example results:

find —> replace
Find —> replace
FIND —> replace
finders keepers —> replaceers keepers
finding —> replaceing

Regex Remap

If you need more specificity and feel a little adventurous, you can use regex!

To handle word boundaries, you currently need to format your command like so:

rake posts:remap['(?<!\\w)(?=\\w)find(?<=\\w)(?!\\w)','replace','regex']

Example results:

replace —> replace
Find —> Find
finders keepers —> finders keepers
finding —> finding

Remove/Delete all occurrences of a word/string

The concept is the same as above, except you are removing a word entirely instead of replacing it:

Basic Case-sensitive Delete

rake posts:delete_word["word-to-delete"]

Case-insensitive Delete

rake posts:delete_word["word-to-delete","string",true]

Regex Delete

rake posts:delete_word['\\[color=#[0-9a-fA-F]{3\,6}\\]','regex']

Example results:

[color=#ff3333] —>
[color=#ffffff]testing —> testing
[color=#101010]testing[/color] —> testing[/color]


It’s a tricky one. The problem I’m finding is that the posts to be remapped are found with the Post.raw_match method. That method seems to expect TCL word boundaries (\mfoo\M or \yfoo\y.)

When the posts are returned, the content is replaced by calling gsub on the post’s raw content. gsub expects \b for word boundaries.

If I add a line to the rake task to substitute \b for \y in the regex pattern after the posts have been found, calling rake posts:remap['\\yfoo\\y','pizza','regex'] returns the correct results.

There might be a way to look for word boundaries that would be accepted by both mothods that are used in the task.

Edit: from the TCL docs:

If your regex flavor supports lookahead and lookbehind, you can use (?<!\w)(?=\w) to emulate Tcl’s \m and (?<=\w)(?!\w) to emulate \M. Though quite a bit more verbose, these lookaround constructs match exactly the same as Tcl’s word boundaries.

This works:
rake posts:remap['(?<!\\w)(?=\\w)foo(?<=\\w)(?!\\w)','pizza','regex']