diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2331add3ab..593cf81266 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,78 @@
+#### v1.18.3 (2021-09-22)
+
+##### Chores
+
+* **deps:**  update docker/build-push-action action to v2.7.0 (ee027719)
+*  incrementing version number - v1.18.2 (0a56158b)
+*  update changelog for v1.18.2 (27e9282a)
+
+##### New Features
+
+*  move filter:topic.post hook to top of method (f194809f)
+*  add client-side static hook to fire immediately before any topic action (hint: delete `action` to stop default behaviour) (66eaae44)
+*  allow removing multiple items from list (397835a0)
+*  add uid to filter:user.saveSettings (7f48edc0)
+*  headers for global privs #9717 (#9762) (84ff1152)
+*  add ACP option to require email address on new registration (006fc700)
+
+##### Bug Fixes
+
+* **deps:**
+  *  update dependency nodebb-plugin-composer-default to v7.0.7 (98554294)
+  *  update dependency postcss to v8.3.7 (6ebe707c)
+  *  update dependency autoprefixer to v10.3.5 (25687441)
+  *  update dependency nodebb-plugin-composer-default to v7.0.6 (#9815) (c18678ce)
+  *  update dependency nodebb-theme-persona to v11.2.8 (#9816) (39d73d0c)
+  *  update dependency connect-mongo to v4.6.0 (8e886c85)
+  *  update dependency nodebb-plugin-composer-default to v7.0.4 (8af54255)
+  *  update dependency mongodb to v3.7.1 (9049dcd7)
+  *  update dependency nodebb-theme-persona to v11.2.6 (506035b5)
+  *  update dependency nodebb-theme-slick to v1.4.13 (787306a6)
+  *  update dependency nodebb-plugin-composer-default to v7.0.3 (732b59c2)
+*  fixed element shifting in ACP menu that's been bothering me for 5-ish years (31975a62)
+*  #9822, use correct username/pwd (30f38771)
+*  remove unused translator (2add84a5)
+*  ban info test (07859f7e)
+*  #9819, show same time info for ban (9f0e55ad)
+*  show local time for ban (7a2f0ae1)
+*  crash (c437b336)
+*  remove caller from payload after hooks is done (15f9aaa6)
+*  bad uid reference (ce8ea6ea)
+*  update Topics.post and Topics.reply so that plugins can modify uid (or redirect a reply to a different topic) (7777812e)
+*  #9818, fix totalTime calculation (c4fc7bf9)
+*  missing microdata in category data (1ed62aa8)
+*  #9812, add topics:schedule (c0a52924)
+*  for subfolders (31a6d4b3)
+*  req.path doesn't have full url (0236ea86)
+*  escape moderation note before adding to dom (75ebe786)
+*  #9811, send bodyClass on 403 (40c9fca9)
+*  also launch docker workflow on release branches (944a7985)
+*  xss on flags page via ban reason (ba3582b8)
+*  up timeout for psql tests (896ff215)
+*  redis batch (0c4b875e)
+*  redis processing batch+1 items every iteration (3261edcc)
+*  #9560, don't save post diffs if content didn't change (8b576a37)
+*  #9790, get baseIndex on update for infinitescroll (6a55c027)
+*  #9790, fix sorting of more than one page of pinned topics (2657804c)
+*  privileges added by plugins (#9802) (3ecbb624)
+*  #9800, don't send all welcome test emails to test@example.org @julianlam (71ed50b9)
+*  docker - remove sha tag (b06e8dba)
+*  Return QEMU back, remove platforms definition (52eace4b)
+*  Docker workflow tweaks (#9792) (e7f4cde4)
+*  browsers autocompleting smtp fields when they should not (34afb747)
+
+##### Refactors
+
+*  no regex (18252fb9)
+*  remove async.waterfall (58ac55c1)
+*  remove async.waterfall (222dccaf)
+*  remove async.waterfall (f35a0f43)
+*  allow plugins to replace og:image, or specify additional og:image (819917da)
+
+##### Code Style Changes
+
+*  give me an A! :100: (0b4d7d1f)
+
 #### v1.18.2 (2021-09-08)
 
 ##### Chores
diff --git a/README.md b/README.md
index a295e6f03a..3f1527d221 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ Additional functionality is enabled through the use of third-party plugins.
 * [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/)
 * [NodeBB Blog](http://blog.nodebb.org)
 * [Premium Hosting for NodeBB](http://www.nodebb.org/ "NodeBB")
+* Unofficial IRC community – channel `#nodebb` on Libera.chat
 * [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
 * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
 
diff --git a/install/data/defaults.json b/install/data/defaults.json
index acd8e9cf17..dfe0c2b7bc 100644
--- a/install/data/defaults.json
+++ b/install/data/defaults.json
@@ -14,6 +14,7 @@
     "newbiePostEditDuration": 3600,
     "postDeleteDuration": 0,
     "enablePostHistory": 1,
+    "topicBacklinks": 1,
     "postCacheSize": 10485760,
     "disableChat": 0,
     "chatEditDuration": 0,
diff --git a/install/package.json b/install/package.json
index dd52eab1c9..4a78966b57 100644
--- a/install/package.json
+++ b/install/package.json
@@ -32,7 +32,7 @@
         "ace-builds": "^1.4.12",
         "archiver": "^5.2.0",
         "async": "^3.2.0",
-        "autoprefixer": "10.3.5",
+        "autoprefixer": "10.3.7",
         "bcryptjs": "2.4.3",
         "benchpressjs": "2.4.3",
         "body-parser": "^1.19.0",
@@ -48,7 +48,7 @@
         "connect-flash": "^0.1.1",
         "connect-mongo": "4.6.0",
         "connect-multiparty": "^2.2.0",
-        "connect-pg-simple": "^6.2.1",
+        "connect-pg-simple": "^7.0.0",
         "connect-redis": "6.0.0",
         "cookie-parser": "^1.4.5",
         "cron": "^1.8.2",
@@ -78,14 +78,14 @@
         "material-design-lite": "^1.3.0",
         "mime": "^2.5.2",
         "mkdirp": "^1.0.4",
-        "mongodb": "3.7.1",
+        "mongodb": "4.1.3",
         "morgan": "^1.10.0",
         "mousetrap": "^1.6.5",
         "multiparty": "4.2.2",
         "@nodebb/bootswatch": "3.4.2",
         "nconf": "^0.11.2",
-        "nodebb-plugin-composer-default": "7.0.7",
-        "nodebb-plugin-dbsearch": "5.0.3",
+        "nodebb-plugin-composer-default": "7.0.8",
+        "nodebb-plugin-dbsearch": "5.0.5",
         "nodebb-plugin-emoji": "^3.5.0",
         "nodebb-plugin-emoji-android": "2.0.5",
         "nodebb-plugin-markdown": "8.14.3",
@@ -93,21 +93,21 @@
         "nodebb-plugin-spam-be-gone": "0.7.9",
         "nodebb-rewards-essentials": "0.1.5",
         "nodebb-theme-lavender": "5.2.1",
-        "nodebb-theme-persona": "11.2.8",
+        "nodebb-theme-persona": "11.2.10",
         "nodebb-theme-slick": "1.4.13",
         "nodebb-theme-vanilla": "12.1.3",
         "nodebb-widget-essentials": "5.0.4",
         "nodemailer": "^6.5.0",
         "nprogress": "0.2.0",
-        "passport": "^0.4.1",
+        "passport": "^0.5.0",
         "passport-http-bearer": "^1.0.1",
         "passport-local": "1.0.0",
         "pg": "^8.7.1",
         "pg-cursor": "^2.7.1",
-        "postcss": "8.3.7",
+        "postcss": "8.3.9",
         "postcss-clean": "1.2.0",
         "prompt": "^1.1.0",
-        "ioredis": "4.27.9",
+        "ioredis": "4.27.10",
         "request": "2.88.2",
         "request-promise-native": "^1.0.9",
         "requirejs": "2.3.6",
@@ -137,13 +137,13 @@
         "winston": "3.3.3",
         "xml": "^1.0.1",
         "xregexp": "^5.0.1",
-        "yargs": "17.1.1",
+        "yargs": "17.2.1",
         "zxcvbn": "^4.4.2"
     },
     "devDependencies": {
         "@apidevtools/swagger-parser": "10.0.3",
-        "@commitlint/cli": "13.1.0",
-        "@commitlint/config-angular": "13.1.0",
+        "@commitlint/cli": "13.2.0",
+        "@commitlint/config-angular": "13.2.0",
         "coveralls": "3.1.1",
         "eslint": "7.32.0",
         "eslint-config-nodebb": "0.0.2",
@@ -152,8 +152,8 @@
         "grunt-contrib-watch": "1.1.0",
         "husky": "7.0.2",
         "jsdom": "17.0.0",
-        "lint-staged": "11.1.2",
-        "mocha": "9.1.1",
+        "lint-staged": "11.2.0",
+        "mocha": "9.1.2",
         "mocha-lcov-reporter": "1.3.0",
         "mockdate": "3.0.5",
         "nyc": "15.1.0",
diff --git a/public/language/ar/admin/settings/notifications.json b/public/language/ar/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/ar/admin/settings/notifications.json
+++ b/public/language/ar/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/ar/admin/settings/post.json b/public/language/ar/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/ar/admin/settings/post.json
+++ b/public/language/ar/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/ar/modules.json b/public/language/ar/modules.json
index 12437e0624..8bd1274ece 100644
--- a/public/language/ar/modules.json
+++ b/public/language/ar/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json
index 21aa90016a..5541e97d22 100644
--- a/public/language/ar/topic.json
+++ b/public/language/ar/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "اضغط هنا للعودة لأخر مشاركة مقروءة في الموضوع",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "من الأحدث إلى الأقدم",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "موضوع جديد",
diff --git a/public/language/bg/admin/settings/notifications.json b/public/language/bg/admin/settings/notifications.json
index d32a556040..c3831f2bc2 100644
--- a/public/language/bg/admin/settings/notifications.json
+++ b/public/language/bg/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Известия",
 	"welcome-notification": "Приветствено известие",
 	"welcome-notification-link": "Връзка за приветственото известие",
-	"welcome-notification-uid": "Потр. ид. за приветственото известие"
+	"welcome-notification-uid": "Потр. ид. за приветственото известие",
+	"post-queue-notification-uid": "Потр. ид. за опашката с публикации"
 }
\ No newline at end of file
diff --git a/public/language/bg/admin/settings/post.json b/public/language/bg/admin/settings/post.json
index 8f4ef2a238..4f41a2cd57 100644
--- a/public/language/bg/admin/settings/post.json
+++ b/public/language/bg/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Показване на раздела „Помощ“",
 	"composer.enable-plugin-help": "Позволяване на добавките да добавят съдържание в раздела за помощ",
 	"composer.custom-help": "Персонализиран текст за помощ",
+	"backlinks": "Обратни връзки",
+	"backlinks.enabled": "Включване на обратните връзки в темите",
+	"backlinks.help": "Ако в публикацията има препратка към друга тема, там ще бъде поставена връзка към публикацията, с конкретното време.",
 	"ip-tracking": "Записване на IP адреса",
 	"ip-tracking.each-post": "Записване на IP адреса за всяка публикация",
 	"enable-post-history": "Включване на историята на публикациите"
diff --git a/public/language/bg/modules.json b/public/language/bg/modules.json
index e7336cedbe..cfd8578016 100644
--- a/public/language/bg/modules.json
+++ b/public/language/bg/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Зачертан",
     "composer.formatting.code": "Код",
     "composer.formatting.link": "Връзка",
-    "composer.formatting.picture": "Снимка",
+    "composer.formatting.picture": "Връзка към изображение",
     "composer.upload-picture": "Качване на изображение",
     "composer.upload-file": "Качване на файл",
     "composer.zen_mode": "Режим Дзен",
diff --git a/public/language/bg/topic.json b/public/language/bg/topic.json
index d624f68667..b1a10de9b7 100644
--- a/public/language/bg/topic.json
+++ b/public/language/bg/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Възстановена от",
     "moved-from-by": "Преместена от %1 от",
     "queued-by": "Публикацията е добавена в опашката за одобрение →",
+    "backlink": "Спомената от",
     "bookmark_instructions": "Щракнете тук, за да се върнете към последно прочетената публикация в тази тема.",
     "flag-post": "Докладване на тази публикация",
     "flag-user": "Докладване на този потребител",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Първо най-новите",
     "most_votes": "Първо тези с най-много гласове",
     "most_posts": "Първо тези с най-много публикации",
+    "most_views": "Първо тези с най-много преглеждания",
     "stale.title": "Създаване на нова тема вместо това?",
     "stale.warning": "Темата, в която отговаряте, е доста стара. Искате ли вместо това да създадете нова и да направите препратка към тази в отговора си?",
     "stale.create": "Създаване на нова тема",
diff --git a/public/language/bn/admin/settings/notifications.json b/public/language/bn/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/bn/admin/settings/notifications.json
+++ b/public/language/bn/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/bn/admin/settings/post.json b/public/language/bn/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/bn/admin/settings/post.json
+++ b/public/language/bn/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/bn/modules.json b/public/language/bn/modules.json
index f95d48b7a0..7e7c986d0a 100644
--- a/public/language/bn/modules.json
+++ b/public/language/bn/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/bn/topic.json b/public/language/bn/topic.json
index 57e9638bd4..f7482e41b5 100644
--- a/public/language/bn/topic.json
+++ b/public/language/bn/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "নতুন থেকে পুরাতন",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/cs/admin/settings/notifications.json b/public/language/cs/admin/settings/notifications.json
index 39bc83bdcb..83e73d288e 100644
--- a/public/language/cs/admin/settings/notifications.json
+++ b/public/language/cs/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Oznámení",
 	"welcome-notification": "Uvítání",
 	"welcome-notification-link": "Odkaz na uvítání",
-	"welcome-notification-uid": "Uvítání uživatele (UID)"
+	"welcome-notification-uid": "Uvítání uživatele (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/cs/admin/settings/post.json b/public/language/cs/admin/settings/post.json
index af5702173a..a76f6b446f 100644
--- a/public/language/cs/admin/settings/post.json
+++ b/public/language/cs/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Zobrazit záložku „Nápověda”",
 	"composer.enable-plugin-help": "Povolit rozšíření přidat obsah do záložky nápovědy",
 	"composer.custom-help": "Uživatelský text nápovědy",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Sledování IP",
 	"ip-tracking.each-post": "Sledovat adresu IP u každého příspěvku",
 	"enable-post-history": "Povolit historii příspěvku"
diff --git a/public/language/cs/modules.json b/public/language/cs/modules.json
index 5ac3ffbede..57bc4fcf9e 100644
--- a/public/language/cs/modules.json
+++ b/public/language/cs/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Přeškrtnutí",
     "composer.formatting.code": "Kód",
     "composer.formatting.link": "Odkaz",
-    "composer.formatting.picture": "Obrázek",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Nahrát obrázek",
     "composer.upload-file": "Nahrát soubor",
     "composer.zen_mode": "Režim Zem",
diff --git a/public/language/cs/topic.json b/public/language/cs/topic.json
index 02dea5d855..96cf0fbffe 100644
--- a/public/language/cs/topic.json
+++ b/public/language/cs/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Pro návrat k poslednímu čtenému příspěvku v tématu, klikněte zde.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Od nejnovějších po nejstarší",
     "most_votes": "S nejvíce hlasy",
     "most_posts": "S nejvíce příspěvky",
+    "most_views": "Most Views",
     "stale.title": "Raději vytvořit nové téma?",
     "stale.warning": "Reagujete na starší téma. Nechcete raději vytvořit nové téma a na původní v něm odkázat?",
     "stale.create": "Vytvořit nové téma",
diff --git a/public/language/da/admin/settings/notifications.json b/public/language/da/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/da/admin/settings/notifications.json
+++ b/public/language/da/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/da/admin/settings/post.json b/public/language/da/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/da/admin/settings/post.json
+++ b/public/language/da/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/da/modules.json b/public/language/da/modules.json
index 590fab6db5..35d7aaa396 100644
--- a/public/language/da/modules.json
+++ b/public/language/da/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/da/topic.json b/public/language/da/topic.json
index bb153483fc..5d6d8ac404 100644
--- a/public/language/da/topic.json
+++ b/public/language/da/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klik her for at vende tilbage til den sidst læste indlæg i denne tråd.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Nyeste til ældste",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Opret nyt emne istedet?",
     "stale.warning": "Emnet du svarer på er ret gammelt. Vil du oprette et nyt emne istedet og referere dette indlæg i dit svar?",
     "stale.create": "Opret nyt emne",
diff --git a/public/language/de/admin/settings/notifications.json b/public/language/de/admin/settings/notifications.json
index e66499b081..d3947744c5 100644
--- a/public/language/de/admin/settings/notifications.json
+++ b/public/language/de/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Benachrichtigungen",
 	"welcome-notification": "Wilkommensnachricht",
 	"welcome-notification-link": "Wilkommensnachricht-Link",
-	"welcome-notification-uid": "Wilkommensbenachrichtigung Benutzer (UID)"
+	"welcome-notification-uid": "Wilkommensbenachrichtigung Benutzer (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/de/admin/settings/post.json b/public/language/de/admin/settings/post.json
index caa1fae9ae..09f5c41b56 100644
--- a/public/language/de/admin/settings/post.json
+++ b/public/language/de/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "\"Hilfe\"-Tab anzeigen",
 	"composer.enable-plugin-help": "Plugins erlauben Inhalte dem \"Help\"-Tab hinzuzufügen",
 	"composer.custom-help": "Benutzerdefinierter Hilfe-Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP-Verfolgung",
 	"ip-tracking.each-post": "IP-Adresse für jeden Beitrag speichern",
 	"enable-post-history": "Aktiviere Beitrags-Änderungsgeschichte"
diff --git a/public/language/de/modules.json b/public/language/de/modules.json
index 7e4b640c81..6bc6968388 100644
--- a/public/language/de/modules.json
+++ b/public/language/de/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Durchstreichen",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Bild",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Bild hochladen",
     "composer.upload-file": "Datei hochladen",
     "composer.zen_mode": "Zen Modus",
diff --git a/public/language/de/topic.json b/public/language/de/topic.json
index 2f29147bc1..63581ae290 100644
--- a/public/language/de/topic.json
+++ b/public/language/de/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klicke hier, um zum letzten gelesenen Beitrag des Themas zurückzukehren.",
     "flag-post": "Diesen Post melden",
     "flag-user": "Diesen Benutzer melden",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Neuste zuerst",
     "most_votes": "Meiste Stimmen",
     "most_posts": "Meiste Beiträge",
+    "most_views": "Most Views",
     "stale.title": "Stattdessen ein neues Thema erstellen?",
     "stale.warning": "Das Thema auf das du antworten willst ist ziemlich alt. Möchtest du stattdessen ein neues Thema erstellen und auf dieses in deiner Antwort hinweisen?",
     "stale.create": "Ein neues Thema erstellen",
diff --git a/public/language/el/admin/settings/notifications.json b/public/language/el/admin/settings/notifications.json
index e2d5967239..b38c65a34c 100644
--- a/public/language/el/admin/settings/notifications.json
+++ b/public/language/el/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Ειδοποιήσεις",
 	"welcome-notification": "Ειδοποίηση καλωσορίσματος",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/el/admin/settings/post.json b/public/language/el/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/el/admin/settings/post.json
+++ b/public/language/el/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/el/modules.json b/public/language/el/modules.json
index 9a50ce2e45..f4dae3a98f 100644
--- a/public/language/el/modules.json
+++ b/public/language/el/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/el/topic.json b/public/language/el/topic.json
index f7fbf49f0c..94b2586d41 100644
--- a/public/language/el/topic.json
+++ b/public/language/el/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Νεότερο προς Παλαιότερο",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/en-GB/admin/settings/notifications.json b/public/language/en-GB/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/en-GB/admin/settings/notifications.json
+++ b/public/language/en-GB/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/en-GB/admin/settings/post.json b/public/language/en-GB/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/en-GB/admin/settings/post.json
+++ b/public/language/en-GB/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/en-GB/modules.json b/public/language/en-GB/modules.json
index fb16c945d7..ded95a7130 100644
--- a/public/language/en-GB/modules.json
+++ b/public/language/en-GB/modules.json
@@ -56,7 +56,7 @@
 	"composer.formatting.strikethrough": "Strikethrough",
 	"composer.formatting.code": "Code",
 	"composer.formatting.link": "Link",
-	"composer.formatting.picture": "Picture",
+	"composer.formatting.picture": "Image Link",
 	"composer.upload-picture": "Upload Image",
 	"composer.upload-file": "Upload File",
 	"composer.zen_mode": "Zen Mode",
diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json
index b95a86c862..68d605dc03 100644
--- a/public/language/en-GB/topic.json
+++ b/public/language/en-GB/topic.json
@@ -51,6 +51,7 @@
 	"restored-by": "Restored by",
 	"moved-from-by": "Moved from %1 by",
 	"queued-by": "Post queued for approval →",
+	"backlink": "Referenced by",
 
 	"bookmark_instructions" : "Click here to return to the last read post in this thread.",
 
@@ -180,6 +181,7 @@
 	"newest_to_oldest": "Newest to Oldest",
 	"most_votes": "Most Votes",
 	"most_posts": "Most Posts",
+	"most_views": "Most Views",
 
 	"stale.title": "Create new topic instead?",
 	"stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
diff --git a/public/language/en-US/admin/settings/notifications.json b/public/language/en-US/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/en-US/admin/settings/notifications.json
+++ b/public/language/en-US/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/en-US/admin/settings/post.json b/public/language/en-US/admin/settings/post.json
index f91a065e46..05656c9fba 100644
--- a/public/language/en-US/admin/settings/post.json
+++ b/public/language/en-US/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/en-US/modules.json b/public/language/en-US/modules.json
index 9a50ce2e45..f4dae3a98f 100644
--- a/public/language/en-US/modules.json
+++ b/public/language/en-US/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/en-US/topic.json b/public/language/en-US/topic.json
index c0c7610a61..ae2389ee4e 100644
--- a/public/language/en-US/topic.json
+++ b/public/language/en-US/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Newest to Oldest",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/en-x-pirate/admin/settings/notifications.json b/public/language/en-x-pirate/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/en-x-pirate/admin/settings/notifications.json
+++ b/public/language/en-x-pirate/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/en-x-pirate/admin/settings/post.json b/public/language/en-x-pirate/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/en-x-pirate/admin/settings/post.json
+++ b/public/language/en-x-pirate/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/en-x-pirate/modules.json b/public/language/en-x-pirate/modules.json
index 17783a7fff..a537668b0b 100644
--- a/public/language/en-x-pirate/modules.json
+++ b/public/language/en-x-pirate/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/en-x-pirate/topic.json b/public/language/en-x-pirate/topic.json
index c0c7610a61..ae2389ee4e 100644
--- a/public/language/en-x-pirate/topic.json
+++ b/public/language/en-x-pirate/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Newest to Oldest",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/es/admin/settings/notifications.json b/public/language/es/admin/settings/notifications.json
index 4c327c1085..8d2509d75b 100644
--- a/public/language/es/admin/settings/notifications.json
+++ b/public/language/es/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notificaciones",
 	"welcome-notification": "Notificación de Bienvenida",
 	"welcome-notification-link": "Enlace de Notificación de Bienvenida",
-	"welcome-notification-uid": "Usuario de Notificación de Bienvenida  (UID)"
+	"welcome-notification-uid": "Usuario de Notificación de Bienvenida  (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/es/admin/settings/post.json b/public/language/es/admin/settings/post.json
index 8525d35a08..72e094800d 100644
--- a/public/language/es/admin/settings/post.json
+++ b/public/language/es/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Mostrar pestaña \"Ayuda\"",
 	"composer.enable-plugin-help": "Permitir a plugins añadir contenido a la pestaña de ayuda",
 	"composer.custom-help": "Texto de Ayuda Personalizado",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Seguimiento de IP",
 	"ip-tracking.each-post": "Seguir la IP para cada entrada/respuesta",
 	"enable-post-history": "Activar historial de respuestas"
diff --git a/public/language/es/modules.json b/public/language/es/modules.json
index cc274d90f0..8aae83b906 100644
--- a/public/language/es/modules.json
+++ b/public/language/es/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Tachado",
     "composer.formatting.code": "Código",
     "composer.formatting.link": "Enlace",
-    "composer.formatting.picture": "Foto",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Subir foto",
     "composer.upload-file": "Subir archivo",
     "composer.zen_mode": "Modo Zen",
diff --git a/public/language/es/topic.json b/public/language/es/topic.json
index 092fcbd3aa..84a7e4c422 100644
--- a/public/language/es/topic.json
+++ b/public/language/es/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restaurado por",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Haz click aquí para volver a tu último mensaje leído en este tema",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Más nuevo a más antiguo",
     "most_votes": "Mayor número de Votos",
     "most_posts": "Mayor número de Posts",
+    "most_views": "Most Views",
     "stale.title": "¿Crear un nuevo hilo en su lugar?",
     "stale.warning": "El hilo al que estás respondiendo es muy antiguo. ¿Quieres crear un nuevo hilo en su lugar y añadir una referencia a este en tu mensaje?",
     "stale.create": "Crear un nuevo hilo",
diff --git a/public/language/et/admin/settings/notifications.json b/public/language/et/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/et/admin/settings/notifications.json
+++ b/public/language/et/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/et/admin/settings/post.json b/public/language/et/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/et/admin/settings/post.json
+++ b/public/language/et/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/et/modules.json b/public/language/et/modules.json
index 6fbb878b3a..61a8fbe145 100644
--- a/public/language/et/modules.json
+++ b/public/language/et/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Läbitõmmatud",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Pilt",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Lae pilt üles",
     "composer.upload-file": "Lae fail üles",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/et/topic.json b/public/language/et/topic.json
index db96717384..f99933c619 100644
--- a/public/language/et/topic.json
+++ b/public/language/et/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Vajuta siia, et tagasi minna viimati loetud postituse juurde siin teemas.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Uuematest vanemateni",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Loo uus teema selle asemel?",
     "stale.warning": "Teema, millele vastad on küllaltki vana. Kas sooviksid hoopiski uue teema luua ning viidata sellele sinu vastuses?",
     "stale.create": "Loo uus teema/alapealkiri",
diff --git a/public/language/fa-IR/admin/settings/notifications.json b/public/language/fa-IR/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/fa-IR/admin/settings/notifications.json
+++ b/public/language/fa-IR/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/fa-IR/admin/settings/post.json b/public/language/fa-IR/admin/settings/post.json
index fb0a7ad95e..57fd21e92f 100644
--- a/public/language/fa-IR/admin/settings/post.json
+++ b/public/language/fa-IR/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/fa-IR/modules.json b/public/language/fa-IR/modules.json
index a4a4f3169a..f401a6e6f2 100644
--- a/public/language/fa-IR/modules.json
+++ b/public/language/fa-IR/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "خط خورده",
     "composer.formatting.code": "کد",
     "composer.formatting.link": "پیوند",
-    "composer.formatting.picture": "عکس",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "بارگذاری عکس",
     "composer.upload-file": "بارگذاری فایل",
     "composer.zen_mode": "حالت ذن",
diff --git a/public/language/fa-IR/topic.json b/public/language/fa-IR/topic.json
index 01f7d78bb2..c7e8c2bdb4 100644
--- a/public/language/fa-IR/topic.json
+++ b/public/language/fa-IR/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "برای بازگشت به آخرین پست در این موضوع اینجا را کلیک کنید.",
     "flag-post": "گزارش این پست",
     "flag-user": "گزارش این کاربر",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "جدید‌ترین به قدیمی‌ترین",
     "most_votes": "بیشترین رای ها",
     "most_posts": "بیشترین پست",
+    "most_views": "Most Views",
     "stale.title": "آیا مایلید به جای آن یک موضوع جدید ایجاد کنید؟",
     "stale.warning": "موضوعی که شما در حال پاسخگویی به آن هستید قدیمی می باشد. آیا میلید به جای آن یک موضوع جدید ایجاد کنید و در آن به این موضوع ارجاع دهید؟",
     "stale.create": "ایجاد یک موضوع جدید",
diff --git a/public/language/fi/admin/settings/notifications.json b/public/language/fi/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/fi/admin/settings/notifications.json
+++ b/public/language/fi/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/fi/admin/settings/post.json b/public/language/fi/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/fi/admin/settings/post.json
+++ b/public/language/fi/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/fi/modules.json b/public/language/fi/modules.json
index 465a3a947c..506acc6926 100644
--- a/public/language/fi/modules.json
+++ b/public/language/fi/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/fi/topic.json b/public/language/fi/topic.json
index 593bab2c90..c58b5350f1 100644
--- a/public/language/fi/topic.json
+++ b/public/language/fi/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval →",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klikkaa tästä palataksesi viimeisimpään luettuun viestiin tässä aiheessa",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Uusimmasta vanhimpaan",
     "most_votes": "Eniten ääniä",
     "most_posts": "Eniten viestejä",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "Aihe johon olet vastaamassa on melko vanha. Haluaisitko luoda mieluummin uuden aiheen ja viitata siitä tähän viestissäsi?",
     "stale.create": "Luo uusi aihe",
diff --git a/public/language/fr/admin/manage/privileges.json b/public/language/fr/admin/manage/privileges.json
index 574a6b5040..0d6fac887d 100644
--- a/public/language/fr/admin/manage/privileges.json
+++ b/public/language/fr/admin/manage/privileges.json
@@ -51,13 +51,13 @@
 	"alert.saved": "Changements de privilèges enregistrés et appliqués",
 	"alert.confirm-discard": "Êtes-vous sûr de vouloir annuler vos modifications de privilèges ?",
 	"alert.discarded": "Modifications de privilèges annulés",
-	"alert.confirm-copyToAll": "Are you sure you wish to apply this set of <strong>%1</strong> to <strong>all categories</strong>?",
-	"alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of <strong>%1</strong> to <strong>all categories</strong>?",
-	"alert.confirm-copyToChildren": "Are you sure you wish to apply this set of <strong>%1</strong> to <strong>all descendant (child) categories</strong>?",
-	"alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of <strong>%1</strong> to <strong>all descendant (child) categories</strong>?",
+	"alert.confirm-copyToAll": "Voulez-vous vraiment appliquer cet ensemble de <strong>%1</strong> à <strong>toutes les catégories</strong>?",
+	"alert.confirm-copyToAllGroup": "Voulez-vous vraiment appliquer l'ensemble de <strong>%1</strong> de ce groupe à <strong>toutes les catégories</strong>?",
+	"alert.confirm-copyToChildren": "Voulez-vous vraiment appliquer cet ensemble de <strong>%1</strong> à <strong>toutes les catégories incluses (enfants)</strong>?",
+	"alert.confirm-copyToChildrenGroup": "Voulez-vous vraiment appliquer l'ensemble de <strong>%1</strong> de ce groupe à <strong>toutes les catégories incluses (enfants)</strong>?",
 	"alert.no-undo": "<em>Cette action ne peut pas être annulée.</em>",
 	"alert.admin-warning": "Les administrateurs obtiennent implicitement tous les privilèges",
-  	"alert.copyPrivilegesFrom-title": "Select a category to copy from",
-  	"alert.copyPrivilegesFrom-warning": "This will copy <strong>%1</strong> from the selected category.",
-  	"alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of <strong>%1</strong> from the selected category."
+  	"alert.copyPrivilegesFrom-title": "Sélectionnez une catégorie à copier",
+  	"alert.copyPrivilegesFrom-warning": "Cela copiera <strong>%1</strong> de la catégorie sélectionnée.",
+  	"alert.copyPrivilegesFromGroup-warning": "Cela copiera l'ensemble de <strong>%1</strong> de ce groupe à partir de la catégorie sélectionnée."
 }
\ No newline at end of file
diff --git a/public/language/fr/admin/settings/email.json b/public/language/fr/admin/settings/email.json
index f43b9d4c46..4752787e26 100644
--- a/public/language/fr/admin/settings/email.json
+++ b/public/language/fr/admin/settings/email.json
@@ -37,8 +37,8 @@
 	"subscriptions.hour": "Heure d'envoi",
 	"subscriptions.hour-help": "Veuillez entrer un nombre représentant l'heure à laquelle envoyer les lettres d'activités (c'est à dire <code>0</code> pour minuit, <code>17</code> pour 5:00 pm). Gardez à l'esprit qu'il s'agit de l'heure du serveur, et peut ne pas correspondre à votre heure locale.<br /> L'heure du serveur est : <span id=\"serverTime\"></span><br /> La prochaine lettre d'activités sera envoyée à <span id=\"nextDigestTime\"></span>",
 	"notifications.remove-images": "Supprimer les images des notifications par e-mail",
-	"require-email-address": "Require new users to specify an email address",
-	"require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. <strong>It does not ensure user will enter a real email address, nor even an address they own.</strong>",
+	"require-email-address": "Exiger une adresse e-mail aux nouveaux utilisateurs ",
+	"require-email-address-warning": "Par défaut, les utilisateurs peuvent refuser de saisir une adresse e-mail. L'activation de cette option oblige de renseigner une une adresse e-mail lors de l'inscription. <strong>Ne garantit pas que l'utilisateur entrera adresse e-mail valide, ni même une adresse qu'il possède.</strong>",
 	"include-unverified-emails": "Envoyer des mails aux destinataires qui n'ont pas explicitement confirmé leurs mails",
 	"include-unverified-warning": "Par défaut, les utilisateurs dont les mails sont associés à leur compte ont déjà été vérifiés, mais il existe des situations où ce n'est pas le cas (par exemple, les connexions SSO, les utilisateurs bénéficiant de droits acquis, etc.). <strong>Activez ce paramètre à vos risques et périls </strong>&ndash; l'envoi de mails à des adresses non vérifiées peut constituer une violation des lois anti-spam régionales."
 }
\ No newline at end of file
diff --git a/public/language/fr/admin/settings/notifications.json b/public/language/fr/admin/settings/notifications.json
index 87ee6b31e0..9a0978d167 100644
--- a/public/language/fr/admin/settings/notifications.json
+++ b/public/language/fr/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Notification de bienvenue",
 	"welcome-notification-link": "Lien de notification de bienvenue",
-	"welcome-notification-uid": "Notification de bienvenue de l'utilisateur (UID)"
+	"welcome-notification-uid": "Notification de bienvenue de l'utilisateur (UID)",
+	"post-queue-notification-uid": "File d'attente d l'Utilisateur (UID)"
 }
\ No newline at end of file
diff --git a/public/language/fr/admin/settings/post.json b/public/language/fr/admin/settings/post.json
index b8cbb4c803..8c033dcd9e 100644
--- a/public/language/fr/admin/settings/post.json
+++ b/public/language/fr/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Afficher l'onglet \"Aide\"",
 	"composer.enable-plugin-help": "Autoriser les plugins à modifier l'onglet d'aide",
 	"composer.custom-help": "Message d'aide personnalisé",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Activer les backlinks de sujet",
+	"backlinks.help": "Si un message fait référence à un autre sujet, un lien vers le message sera inséré dans le sujet référencé.",
 	"ip-tracking": "Suivi d'IP",
 	"ip-tracking.each-post": "Suivre l'adresse IP pour chaque message",
 	"enable-post-history": "Activer l'historique des publications"
diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json
index 4e665f520a..d1c547a30c 100644
--- a/public/language/fr/modules.json
+++ b/public/language/fr/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Barré",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Lien",
-    "composer.formatting.picture": "Image",
+    "composer.formatting.picture": "Lien d'image",
     "composer.upload-picture": "Envoyer une image",
     "composer.upload-file": "Envoyer un fichier",
     "composer.zen_mode": "Mode Zen",
diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json
index e93c59cb01..5badf716ca 100644
--- a/public/language/fr/topic.json
+++ b/public/language/fr/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restauré par",
     "moved-from-by": "Déplacé de %1 par",
     "queued-by": "Message en attente d'approbation &rarr;",
+    "backlink": "Référencé par",
     "bookmark_instructions": "Cliquez ici pour retourner au dernier message lu de ce fil.",
     "flag-post": "Signaler ce message",
     "flag-user": "Signaler cet utilisateur",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Du plus récent au plus ancien",
     "most_votes": "Les plus votés",
     "most_posts": "Meilleurs messages",
+    "most_views": "Les plus vues",
     "stale.title": "Créer un nouveau sujet à la place ?",
     "stale.warning": "Le sujet auquel vous répondez est assez ancien. Ne voudriez-vous pas créer un nouveau sujet à la place et placer une référence vers celui-ci dans votre réponse ?",
     "stale.create": "Créer un nouveau sujet",
diff --git a/public/language/gl/admin/settings/notifications.json b/public/language/gl/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/gl/admin/settings/notifications.json
+++ b/public/language/gl/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/gl/admin/settings/post.json b/public/language/gl/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/gl/admin/settings/post.json
+++ b/public/language/gl/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/gl/modules.json b/public/language/gl/modules.json
index af391edcca..ea39947c26 100644
--- a/public/language/gl/modules.json
+++ b/public/language/gl/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Tachado",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Ligazón",
-    "composer.formatting.picture": "Foto",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Subir foto",
     "composer.upload-file": "Subir arquivo",
     "composer.zen_mode": "Modo Zen",
diff --git a/public/language/gl/topic.json b/public/language/gl/topic.json
index da2afd1bef..72e77219fa 100644
--- a/public/language/gl/topic.json
+++ b/public/language/gl/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Pica aquí para volver á última mensaxe lida neste tema ",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Máis novo a máis antigo",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Crear un novo tema no seu lugar?",
     "stale.warning": "O tema no que queres publicar é bastante vello. Queres crear un novo tema no seu lugar e incluir unha referencia a este na túa mensaxe?",
     "stale.create": "Crear un novo tema",
diff --git a/public/language/he/admin/settings/notifications.json b/public/language/he/admin/settings/notifications.json
index 2829c6e2ed..b11b8a00c9 100644
--- a/public/language/he/admin/settings/notifications.json
+++ b/public/language/he/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "התראות",
 	"welcome-notification": "הודעת ברוכים הבאים",
 	"welcome-notification-link": "קישור הודעת ברוכים הבאים",
-	"welcome-notification-uid": "הודעת ברוכים הבאים למשתמש (UID)"
+	"welcome-notification-uid": "הודעת ברוכים הבאים למשתמש (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/he/admin/settings/post.json b/public/language/he/admin/settings/post.json
index ecca8e24f7..f2f1611796 100644
--- a/public/language/he/admin/settings/post.json
+++ b/public/language/he/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "הצג כרטיסיית \"עזרה\"",
 	"composer.enable-plugin-help": "אפשר לתוסיפים להוסיף תוכן ללשונית עזרה",
 	"composer.custom-help": "טקסט עזרה מותאם אישית",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP מעקב",
 	"ip-tracking.each-post": "מעקב אחר כתובת IP על כל הודעה",
 	"enable-post-history": "הפוך היסטוריית פוסטים לזמינה"
diff --git a/public/language/he/modules.json b/public/language/he/modules.json
index a360dc3c26..5ad16e23df 100644
--- a/public/language/he/modules.json
+++ b/public/language/he/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "קו פוסל",
     "composer.formatting.code": "קוד",
     "composer.formatting.link": "לינק",
-    "composer.formatting.picture": "תמונה מהרשת",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "העלה תמונה",
     "composer.upload-file": "העלה קובץ",
     "composer.zen_mode": "מסך מלא",
diff --git a/public/language/he/topic.json b/public/language/he/topic.json
index 7c5a688bbe..964c73d19c 100644
--- a/public/language/he/topic.json
+++ b/public/language/he/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "שוחזר על ידי",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "הפוסט ממתין לאישור &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "לחץ כאן בכדי לחזור לפוסט האחרון שקראת בנושא הזה.",
     "flag-post": "דווח על פוסט זה",
     "flag-user": "דווח על משתמש זה",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "מהחדש לישן",
     "most_votes": "הכי הרבה הצבעות",
     "most_posts": "הכי הרבה פוסטים",
+    "most_views": "Most Views",
     "stale.title": "ליצור נושא חדש במקום זאת?",
     "stale.warning": "הנושא בו אתה מגיב הוא די ישן. האם ברצונך לפתוח נושא חדש, ולהזכיר נושא זה בתגובתך?",
     "stale.create": "צור נושא חדש",
diff --git a/public/language/hr/admin/settings/notifications.json b/public/language/hr/admin/settings/notifications.json
index d70a8007a4..9b33fa7397 100644
--- a/public/language/hr/admin/settings/notifications.json
+++ b/public/language/hr/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Obavijesti",
 	"welcome-notification": "Obavijest dobrodošlice",
 	"welcome-notification-link": "Poveznica objave dobrodošlice",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/hr/admin/settings/post.json b/public/language/hr/admin/settings/post.json
index 4b6d50fc5a..b32357dc51 100644
--- a/public/language/hr/admin/settings/post.json
+++ b/public/language/hr/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Prikaži \"Pomoć\"",
 	"composer.enable-plugin-help": "Dozvoli dodatcima da dodaju sadržaj u \"Pomoć\"",
 	"composer.custom-help": "Tekst \"Pomoć\"",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP praćenje",
 	"ip-tracking.each-post": "Prati IP adresu za svaku objavu",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/hr/modules.json b/public/language/hr/modules.json
index e56b7e30e1..ffec174d2b 100644
--- a/public/language/hr/modules.json
+++ b/public/language/hr/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Precrtano",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Poveznica",
-    "composer.formatting.picture": "Slika",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Učitaj sliku",
     "composer.upload-file": "Učitaj datoteku",
     "composer.zen_mode": "Zen",
diff --git a/public/language/hr/topic.json b/public/language/hr/topic.json
index 6c77001ccc..31110f9f84 100644
--- a/public/language/hr/topic.json
+++ b/public/language/hr/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klikni ovdje za povratak na zadnji pročitani post.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Novije prema Starom",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Otvori novu temu?",
     "stale.warning": "Tema na koju odgovarate je stara. Želite li otvoriti novu temu i postaviti referencu u vašem odgovoru?",
     "stale.create": "Otvori novu temu",
diff --git a/public/language/hu/admin/settings/notifications.json b/public/language/hu/admin/settings/notifications.json
index 46379e19f5..1e19679d4a 100644
--- a/public/language/hu/admin/settings/notifications.json
+++ b/public/language/hu/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Értesítések",
 	"welcome-notification": "Üdvözlő értesítés",
 	"welcome-notification-link": "Üdvözlő értesítés linkje",
-	"welcome-notification-uid": "Felhasználói üdvözlő értesítés (UID)"
+	"welcome-notification-uid": "Felhasználói üdvözlő értesítés (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/hu/admin/settings/post.json b/public/language/hu/admin/settings/post.json
index 3091efd2f2..2bae9b654e 100644
--- a/public/language/hu/admin/settings/post.json
+++ b/public/language/hu/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "\"Segítség\" panel megjelenítése",
 	"composer.enable-plugin-help": "Beépülők hozzáadhassanak saját tartalmaz a segítség panelhoz",
 	"composer.custom-help": "Egyedi szöveg a segítségnél",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP nyomonkövetés",
 	"ip-tracking.each-post": "IP cím követése minden hozzászólásnál",
 	"enable-post-history": "Hozzászólás történetiség engedélyezése"
diff --git a/public/language/hu/modules.json b/public/language/hu/modules.json
index 6ab23fb498..132b399d2a 100644
--- a/public/language/hu/modules.json
+++ b/public/language/hu/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Áthúzás",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Hivatkozás",
-    "composer.formatting.picture": "Kép",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Kép feltöltése",
     "composer.upload-file": "Fájl feltöltése",
     "composer.zen_mode": "Zen mód",
diff --git a/public/language/hu/topic.json b/public/language/hu/topic.json
index 2fe3a8fb93..8e39ba268a 100644
--- a/public/language/hu/topic.json
+++ b/public/language/hu/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Visszaállította",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Hozzászólás jóváhagyásra bejegyezve &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Kattints ide a beszélgetés utolsó hozzászólására ugráshoz.",
     "flag-post": "Jelöld meg ezt a bejegyzést",
     "flag-user": "Jelöld meg ezt a felhasználót",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Újabbak elől",
     "most_votes": "Legtöbb szavazat",
     "most_posts": "Legtöbb bejegyzés",
+    "most_views": "Most Views",
     "stale.title": "Inkább új témakör létrehozása?",
     "stale.warning": "A témakör, melyre válaszolsz, elég régi. Szeretnél helyette inkább új témakört létrehozni, és erre hivatkozni a válaszodban?",
     "stale.create": "Új témakör létrehozása",
diff --git a/public/language/id/admin/settings/notifications.json b/public/language/id/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/id/admin/settings/notifications.json
+++ b/public/language/id/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/id/admin/settings/post.json b/public/language/id/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/id/admin/settings/post.json
+++ b/public/language/id/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/id/modules.json b/public/language/id/modules.json
index 6e137c2752..fe27b6ffc6 100644
--- a/public/language/id/modules.json
+++ b/public/language/id/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/id/topic.json b/public/language/id/topic.json
index c5d0cbdff0..700abd5285 100644
--- a/public/language/id/topic.json
+++ b/public/language/id/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klik di sini untuk kembali ke posting yang terakhir kali dibaca pada topik ini.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Terbaru ke Terlama",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/it/admin/settings/notifications.json b/public/language/it/admin/settings/notifications.json
index 6168c5510c..7eec59d24a 100644
--- a/public/language/it/admin/settings/notifications.json
+++ b/public/language/it/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifiche",
 	"welcome-notification": "Notifica di benvenuto",
 	"welcome-notification-link": "Collegamento a Notifica di benvenuto",
-	"welcome-notification-uid": "Notifica di benvenuto utente (UID)"
+	"welcome-notification-uid": "Notifica di benvenuto utente (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/it/admin/settings/post.json b/public/language/it/admin/settings/post.json
index fa1b9066dc..aaecd61292 100644
--- a/public/language/it/admin/settings/post.json
+++ b/public/language/it/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Mostra la scheda \"Aiuto\"",
 	"composer.enable-plugin-help": "Consenti ai plug-in di aggiungere contenuti alla scheda Guida",
 	"composer.custom-help": "Testo di aiuto personalizzato",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Monitoraggio IP",
 	"ip-tracking.each-post": "Traccia l'indirizzo IP per ogni post",
 	"enable-post-history": "Abilita Cronologia post"
diff --git a/public/language/it/modules.json b/public/language/it/modules.json
index 5b1bb16729..38c7d33724 100644
--- a/public/language/it/modules.json
+++ b/public/language/it/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Barrato",
     "composer.formatting.code": "Codice",
     "composer.formatting.link": "Collegamento",
-    "composer.formatting.picture": "Immagine",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Carica immagine",
     "composer.upload-file": "Carica file",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/it/topic.json b/public/language/it/topic.json
index 8a4a3dc03f..5d695f9a47 100644
--- a/public/language/it/topic.json
+++ b/public/language/it/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Ripristinato da",
     "moved-from-by": "Spostato da %1 da",
     "queued-by": "Post in coda per l'approvazione &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Clicca qui per tornare all'ultimo post letto in questa discussione.",
     "flag-post": "Segnala questo post",
     "flag-user": "Segnala questo utente",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Da Nuovi a Vecchi",
     "most_votes": "Più Voti",
     "most_posts": "Più Post",
+    "most_views": "Most Views",
     "stale.title": "Preferisci creare una nuova discussione?",
     "stale.warning": "Il topic al quale stai rispondendo è abbastanza vecchio. Vorresti piuttosto creare un nuovo topic in riferimento a questo nella tua risposta?",
     "stale.create": "Crea una nuova discussione",
diff --git a/public/language/ja/admin/settings/notifications.json b/public/language/ja/admin/settings/notifications.json
index d69a16d629..9be8707a2b 100644
--- a/public/language/ja/admin/settings/notifications.json
+++ b/public/language/ja/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "通知",
 	"welcome-notification": "ウェルカム通知",
 	"welcome-notification-link": "ウェルカム通知のリンク",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/ja/admin/settings/post.json b/public/language/ja/admin/settings/post.json
index 09cfa068b9..43cd02324a 100644
--- a/public/language/ja/admin/settings/post.json
+++ b/public/language/ja/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "「ヘルプ」タグを表示",
 	"composer.enable-plugin-help": "プラグインがヘルプタブにコンテンツを追加できるようにする",
 	"composer.custom-help": "カスタムヘルプテキスト",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IPトラッキング",
 	"ip-tracking.each-post": "各投稿のトラックIPアドレス",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/ja/modules.json b/public/language/ja/modules.json
index cdfd699752..792e8a33a7 100644
--- a/public/language/ja/modules.json
+++ b/public/language/ja/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "取り消し線",
     "composer.formatting.code": "コード",
     "composer.formatting.link": "リンク",
-    "composer.formatting.picture": "画像",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "画像をアップロード",
     "composer.upload-file": "ファイルをアップロード",
     "composer.zen_mode": "Zen モード",
diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json
index d68eebd84c..21750ccff3 100644
--- a/public/language/ja/topic.json
+++ b/public/language/ja/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "ここをクリックすると、このスレッドの最後に読んでいた投稿へ移動します。",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "新しいものから古い順",
     "most_votes": "最高評価",
     "most_posts": "最大投稿",
+    "most_views": "Most Views",
     "stale.title": "新しいスレッドを作りますか?",
     "stale.warning": "あなたが返信しようとしてるスレッドが古いスレッドです。新しいスレッドを作って、そしてこのスレッドが参考として入れた方を勧めます。そうしますか?",
     "stale.create": "新しいスレッドを作ります。",
diff --git a/public/language/ko/admin/settings/notifications.json b/public/language/ko/admin/settings/notifications.json
index 1973f2e758..54a7241021 100644
--- a/public/language/ko/admin/settings/notifications.json
+++ b/public/language/ko/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "알림",
 	"welcome-notification": "환영 알림",
 	"welcome-notification-link": "환영 알림 링크",
-	"welcome-notification-uid": "환영 알림 사용자 (UID)"
+	"welcome-notification-uid": "환영 알림 사용자 (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/ko/admin/settings/post.json b/public/language/ko/admin/settings/post.json
index 3ca69a289f..475c087504 100644
--- a/public/language/ko/admin/settings/post.json
+++ b/public/language/ko/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "\"도움말\" 탭 표시",
 	"composer.enable-plugin-help": "플러그인의 도움말 탭 내용 추가 허용",
 	"composer.custom-help": "사용자 정의 \"도움말\" 텍스트",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP 추적",
 	"ip-tracking.each-post": "모든 포스트 IP 추적",
 	"enable-post-history": "게시글 편집 기록 활성화"
diff --git a/public/language/ko/modules.json b/public/language/ko/modules.json
index 8233e20d6f..6fa30a1b5e 100644
--- a/public/language/ko/modules.json
+++ b/public/language/ko/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "취소선",
     "composer.formatting.code": "코드",
     "composer.formatting.link": "링크",
-    "composer.formatting.picture": "사진",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "이미지 업로드",
     "composer.upload-file": "파일 업로드",
     "composer.zen_mode": "전체화면",
diff --git a/public/language/ko/topic.json b/public/language/ko/topic.json
index 1804eb712e..7b9c6331d4 100644
--- a/public/language/ko/topic.json
+++ b/public/language/ko/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "복원한 사용자:",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "게시 승인 대기 중 &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "이 쓰레드에서 읽은 마지막 포스트로 이동하려면 여기를 클릭 하세요.",
     "flag-post": "해당 포스트 신고",
     "flag-user": "해당 유저 신고",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "최신순",
     "most_votes": "투표순",
     "most_posts": "포스트순",
+    "most_views": "Most Views",
     "stale.title": "새로운 화제를 생성하시겠습니까?",
     "stale.warning": "현재 답글을 작성 중인 화제는 오래전에 작성 되었습니다. 새로 화제를 생성하고 이 게시물을 인용하시겠습니까?",
     "stale.create": "새로운 화제 작성",
diff --git a/public/language/lt/admin/settings/notifications.json b/public/language/lt/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/lt/admin/settings/notifications.json
+++ b/public/language/lt/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/lt/admin/settings/post.json b/public/language/lt/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/lt/admin/settings/post.json
+++ b/public/language/lt/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/lt/modules.json b/public/language/lt/modules.json
index 3ba39a844e..94ae834e8c 100644
--- a/public/language/lt/modules.json
+++ b/public/language/lt/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json
index 26cfa3d292..731eabcbcb 100644
--- a/public/language/lt/topic.json
+++ b/public/language/lt/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Nuo naujausių iki seniausių",
     "most_votes": "Daugiausiai Balsų",
     "most_posts": "Daugiausiai Įrašų",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Sukurti naują temą",
diff --git a/public/language/lv/admin/settings/notifications.json b/public/language/lv/admin/settings/notifications.json
index bfe6bd745d..d2f44f18d6 100644
--- a/public/language/lv/admin/settings/notifications.json
+++ b/public/language/lv/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Paziņojumi",
 	"welcome-notification": "Sveicināšanas paziņojums",
 	"welcome-notification-link": "Sveicināšanas paziņojuma saite",
-	"welcome-notification-uid": "Sveicināšanas paziņojuma lietotājs (UID)"
+	"welcome-notification-uid": "Sveicināšanas paziņojuma lietotājs (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/lv/admin/settings/post.json b/public/language/lv/admin/settings/post.json
index d38fda1644..51c48dc131 100644
--- a/public/language/lv/admin/settings/post.json
+++ b/public/language/lv/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Rādīt \"palīdzība\" cilni",
 	"composer.enable-plugin-help": "Atļaut spraudņiem pievienot saturu palīdzības cilnei",
 	"composer.custom-help": "Pielāgotais palīdzības teksts",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP adrešu pierakstīšana",
 	"ip-tracking.each-post": "Pierakstīt katra raksta IP adresi",
 	"enable-post-history": "Iespējot rakstu vēsturi"
diff --git a/public/language/lv/modules.json b/public/language/lv/modules.json
index f6340bc53a..f9869bdbc8 100644
--- a/public/language/lv/modules.json
+++ b/public/language/lv/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Svītrotā rakstā",
     "composer.formatting.code": "Koda gabals",
     "composer.formatting.link": "Saite",
-    "composer.formatting.picture": "Bilde",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Augšupielādēt bildi",
     "composer.upload-file": "Augšupielādēt failu",
     "composer.zen_mode": "Zen režīms",
diff --git a/public/language/lv/topic.json b/public/language/lv/topic.json
index 2d7037ae73..958840aa41 100644
--- a/public/language/lv/topic.json
+++ b/public/language/lv/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Noklikšķināt, lai atgrieztos pēdējā lasītā rakstā šajā pavedienā.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "No jaunākā līdz vecākam",
     "most_votes": "Pēc visvairāk balsojumu",
     "most_posts": "Pēc visvairāk rakstu",
+    "most_views": "Most Views",
     "stale.title": "Tā vietā izveidot jaunu tematu?",
     "stale.warning": "Šis temats, uz kuru atbildi, ir diezgan sens. Vai vēlies izveidot jaunu tematu un atsaukties uz šo tematu?",
     "stale.create": "Izveidot jaunu tematu",
diff --git a/public/language/ms/admin/settings/notifications.json b/public/language/ms/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/ms/admin/settings/notifications.json
+++ b/public/language/ms/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/ms/admin/settings/post.json b/public/language/ms/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/ms/admin/settings/post.json
+++ b/public/language/ms/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/ms/modules.json b/public/language/ms/modules.json
index b094428889..cfabba393c 100644
--- a/public/language/ms/modules.json
+++ b/public/language/ms/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json
index 210ced10e5..407624794f 100644
--- a/public/language/ms/topic.json
+++ b/public/language/ms/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Baru ke Lama",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Bukan topik baru?",
     "stale.warning": "Topik yang anda nak balas agak lapuk. Adakah anda ingin buka topik baru dan rujukkan topik ini dalam balasan anda?",
     "stale.create": "Buka topik baru",
diff --git a/public/language/nb/admin/settings/notifications.json b/public/language/nb/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/nb/admin/settings/notifications.json
+++ b/public/language/nb/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/nb/admin/settings/post.json b/public/language/nb/admin/settings/post.json
index f2b9a5e674..30a0e25fed 100644
--- a/public/language/nb/admin/settings/post.json
+++ b/public/language/nb/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/nb/modules.json b/public/language/nb/modules.json
index 02bf041915..1e29146fed 100644
--- a/public/language/nb/modules.json
+++ b/public/language/nb/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json
index b3f5912948..83ca054214 100644
--- a/public/language/nb/topic.json
+++ b/public/language/nb/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Gjenopprettet av",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Innlegg i kø for godkjenning & rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klikk her for å gå tilbake til det siste innlegget i denne tråden.",
     "flag-post": "Flagg denne posten",
     "flag-user": "Flagg denne brukeren",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Nyeste til eldste",
     "most_votes": "Flest stemmer",
     "most_posts": "Flest innlegg",
+    "most_views": "Most Views",
     "stale.title": "Lag en ny tråd i stedet?",
     "stale.warning": "Tråden du svarer på er ganske gammel. Vil du heller lage en ny tråd og refere til denne i den?",
     "stale.create": "Lag en ny tråd",
diff --git a/public/language/nl/admin/settings/notifications.json b/public/language/nl/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/nl/admin/settings/notifications.json
+++ b/public/language/nl/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/nl/admin/settings/post.json b/public/language/nl/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/nl/admin/settings/post.json
+++ b/public/language/nl/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json
index 1411e6e330..0c310bdd0d 100644
--- a/public/language/nl/modules.json
+++ b/public/language/nl/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Doorhalen",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Afbeelding",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload afbeelding",
     "composer.upload-file": "Upload bestand",
     "composer.zen_mode": "Zen-modus",
diff --git a/public/language/nl/topic.json b/public/language/nl/topic.json
index 3097dcf09d..d323b9250e 100644
--- a/public/language/nl/topic.json
+++ b/public/language/nl/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klik hier om terug te keren naar de laatst gelezen post in deze thread.",
     "flag-post": "Rapporteer dit bericht",
     "flag-user": "Rapporteer deze gebruiker",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Meest recente berichten bovenaan",
     "most_votes": "Meeste stemmen",
     "most_posts": "Meeste berichten",
+    "most_views": "Most Views",
     "stale.title": "Een nieuw onderwerp maken in de plaats?",
     "stale.warning": "Het onderwerp waar je op antwoord is vrij oud. Zou je graag een nieuw onderwerp maken met een referentie naar dit onderwerp in je antwoord?",
     "stale.create": "Maak een nieuw onderwerp",
diff --git a/public/language/pl/admin/settings/notifications.json b/public/language/pl/admin/settings/notifications.json
index 2b3dad29ea..6a3a4be2b8 100644
--- a/public/language/pl/admin/settings/notifications.json
+++ b/public/language/pl/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Powiadomienia",
 	"welcome-notification": "Powiadomienie powitalne",
 	"welcome-notification-link": "Łącze do komunikatu powitalnego",
-	"welcome-notification-uid": "Powiadomienie powitalne użytkownika (UID)"
+	"welcome-notification-uid": "Powiadomienie powitalne użytkownika (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/pl/admin/settings/post.json b/public/language/pl/admin/settings/post.json
index 4fc718bd4c..7659b24546 100644
--- a/public/language/pl/admin/settings/post.json
+++ b/public/language/pl/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Pokazuj zakładkę „Pomoc”",
 	"composer.enable-plugin-help": "Zezwalaj wtyczkom na dodawanie zawartości do zakładki pomocy",
 	"composer.custom-help": "Własny tekst pomocy",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Śledzenie IP",
 	"ip-tracking.each-post": "Śledź adres IP dla każdego postu",
 	"enable-post-history": "Włącz historię wpisu"
diff --git a/public/language/pl/modules.json b/public/language/pl/modules.json
index 336f920573..9a3bd5980e 100644
--- a/public/language/pl/modules.json
+++ b/public/language/pl/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Przekreślenie",
     "composer.formatting.code": "Kod",
     "composer.formatting.link": "Odnośnik",
-    "composer.formatting.picture": "Obraz",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Wyślij obraz",
     "composer.upload-file": "Wyślij plik",
     "composer.zen_mode": "Tryb Zen",
diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json
index 948784b2bb..a5232eb3fe 100644
--- a/public/language/pl/topic.json
+++ b/public/language/pl/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Kliknij tutaj, by powrócić do ostatniego przeczytanego postu w tym temacie.",
     "flag-post": "Zgłoś ten post",
     "flag-user": "Zgłoś tego użytkownika",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Najpierw najnowsze",
     "most_votes": "Najwięcej głosów",
     "most_posts": "Najwięcej postów",
+    "most_views": "Most Views",
     "stale.title": "Stworzyć nowy temat?",
     "stale.warning": "Temat, na który chcesz udzielić odpowiedzi, jest dość stary. Czy nie wolisz utworzyć nowego tematu i jedynie odnieść się do tego?",
     "stale.create": "Stwórz nowy temat",
diff --git a/public/language/pt-BR/admin/settings/notifications.json b/public/language/pt-BR/admin/settings/notifications.json
index 1734b8890b..9a78c28825 100644
--- a/public/language/pt-BR/admin/settings/notifications.json
+++ b/public/language/pt-BR/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notificações",
 	"welcome-notification": "Notificação de Boas-vindas",
 	"welcome-notification-link": "Link da Notificação de Boas-vindas",
-	"welcome-notification-uid": "Usuário de Notificação de Boas-vindas (UID)"
+	"welcome-notification-uid": "Usuário de Notificação de Boas-vindas (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/pt-BR/admin/settings/post.json b/public/language/pt-BR/admin/settings/post.json
index 764ec57c0a..6bfc361519 100644
--- a/public/language/pt-BR/admin/settings/post.json
+++ b/public/language/pt-BR/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Mostrar aba \"Ajuda\"",
 	"composer.enable-plugin-help": "Permitir plugins de adicionar conteúdo à aba ajuda",
 	"composer.custom-help": "Texto de Ajuda Personalizado",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Rastreamento de IP",
 	"ip-tracking.each-post": "Rastrear Endereço IP para cada post",
 	"enable-post-history": "Ativar o Histórico de Postagem"
diff --git a/public/language/pt-BR/modules.json b/public/language/pt-BR/modules.json
index 570b9f3eb7..3f2c02c188 100644
--- a/public/language/pt-BR/modules.json
+++ b/public/language/pt-BR/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Riscado",
     "composer.formatting.code": "Código",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Imagem",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Fazer upload de Imagem",
     "composer.upload-file": "Fazer upload de Arquivo",
     "composer.zen_mode": "Modo Zen",
diff --git a/public/language/pt-BR/topic.json b/public/language/pt-BR/topic.json
index 92b525a5e2..7af51142e4 100644
--- a/public/language/pt-BR/topic.json
+++ b/public/language/pt-BR/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Recuperado por",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post aguardando aprovação &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Clique aqui para retornar ao último post lido neste tópico.",
     "flag-post": "Marque este post",
     "flag-user": "Marque este usuário",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Mais Recente para Mais Antigo",
     "most_votes": "Mais Votados",
     "most_posts": "Mais Postagens",
+    "most_views": "Most Views",
     "stale.title": "Criar um novo tópico ao invés disso?",
     "stale.warning": "O tópico que você está respondendo é bem antigo. Você gostaria de criar um novo tópico ao invés disso, e referenciá-lo em sua resposta?",
     "stale.create": "Criar um novo tópico",
diff --git a/public/language/pt-PT/admin/settings/notifications.json b/public/language/pt-PT/admin/settings/notifications.json
index 03fffd971e..0ce9737e1f 100644
--- a/public/language/pt-PT/admin/settings/notifications.json
+++ b/public/language/pt-PT/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notificações",
 	"welcome-notification": "Notificação de Boas-vindas",
 	"welcome-notification-link": "Link da Notificação de Boas-vindas",
-	"welcome-notification-uid": "Notificação de boas-vindas ao utilizador (UID)"
+	"welcome-notification-uid": "Notificação de boas-vindas ao utilizador (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/pt-PT/admin/settings/post.json b/public/language/pt-PT/admin/settings/post.json
index 44ff5a2c1d..7e04a1cf14 100644
--- a/public/language/pt-PT/admin/settings/post.json
+++ b/public/language/pt-PT/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Mostrar separador \"Ajuda\"",
 	"composer.enable-plugin-help": "Permitir aos plugins adicionarem conteúdo ao separador de ajuda",
 	"composer.custom-help": "Texto de ajuda personalizado",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Ativar histórico de publicações"
diff --git a/public/language/pt-PT/modules.json b/public/language/pt-PT/modules.json
index 69fe9154b6..851361e0b4 100644
--- a/public/language/pt-PT/modules.json
+++ b/public/language/pt-PT/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Riscado",
     "composer.formatting.code": "Código",
     "composer.formatting.link": "Hiperligação",
-    "composer.formatting.picture": "Imagem",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Enviar imagem",
     "composer.upload-file": "Enviar um ficheiro",
     "composer.zen_mode": "Modo Zen",
diff --git a/public/language/pt-PT/topic.json b/public/language/pt-PT/topic.json
index 395192641d..65aa9a3bf2 100644
--- a/public/language/pt-PT/topic.json
+++ b/public/language/pt-PT/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Carrega aqui para voltares à última publicação lide assunto.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Mais recente para mais antigo",
     "most_votes": "Mais votos",
     "most_posts": "Mais publicações",
+    "most_views": "Most Views",
     "stale.title": "Em vez disso, criar novo tópico?",
     "stale.warning": "O tópico ao qual estás a responder é bastante antigo. Gostarias antes de criar um novo tópico e referir este na tua resposta?",
     "stale.create": "Criar um novo tópico",
diff --git a/public/language/ro/admin/settings/notifications.json b/public/language/ro/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/ro/admin/settings/notifications.json
+++ b/public/language/ro/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/ro/admin/settings/post.json b/public/language/ro/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/ro/admin/settings/post.json
+++ b/public/language/ro/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/ro/modules.json b/public/language/ro/modules.json
index b77a9ec31e..84c4c9b8ec 100644
--- a/public/language/ro/modules.json
+++ b/public/language/ro/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/ro/topic.json b/public/language/ro/topic.json
index a8dd3ac675..e418c2667c 100644
--- a/public/language/ro/topic.json
+++ b/public/language/ro/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Noi la Vechi",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/ru/admin/settings/notifications.json b/public/language/ru/admin/settings/notifications.json
index e5593f26c7..d6a0478bc5 100644
--- a/public/language/ru/admin/settings/notifications.json
+++ b/public/language/ru/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Уведомления",
 	"welcome-notification": "Приветственное уведомление",
 	"welcome-notification-link": "Ссылка в уведомлении",
-	"welcome-notification-uid": "UID отправителя"
+	"welcome-notification-uid": "UID отправителя",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/ru/admin/settings/post.json b/public/language/ru/admin/settings/post.json
index 4c6345eb37..86f7ee90a0 100644
--- a/public/language/ru/admin/settings/post.json
+++ b/public/language/ru/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Показывать вкладку с подсказками",
 	"composer.enable-plugin-help": "Разрешить плагинам добавлять подсказки на вкладку",
 	"composer.custom-help": "Пользовательский текст для вкладки с подсказками",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Отслеживание IP",
 	"ip-tracking.each-post": "Отслеживать IP для каждого сообщения",
 	"enable-post-history": "Включить историю правок"
diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json
index 3ed9b99f97..6c5e9e2bb3 100644
--- a/public/language/ru/modules.json
+++ b/public/language/ru/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Зачеркнуть",
     "composer.formatting.code": "Код",
     "composer.formatting.link": "Ссылка",
-    "composer.formatting.picture": "Изображение",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Загрузить изображение",
     "composer.upload-file": "Загрузить файл",
     "composer.zen_mode": "Полноэкранный режим",
diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json
index 141f7b2d9e..ab3da55d0d 100644
--- a/public/language/ru/topic.json
+++ b/public/language/ru/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Восстановлено",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Сообщение поставлено в очередь на утверждение;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Нажмите здесь, чтобы вернуться к последнему прочитанному сообщению в этой теме.",
     "flag-post": "Пожаловаться на это сообщение",
     "flag-user": "Пожаловаться на этого пользователя",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Сначала новые",
     "most_votes": "По количеству голосов",
     "most_posts": "По количеству сообщений",
+    "most_views": "Most Views",
     "stale.title": "Создать новую тему вместо этой?",
     "stale.warning": "Тема, в которую вы собираетесь написать, очень старая. Может, стоит создать новую, а про эту просто напомнить к случаю?",
     "stale.create": "Создать новую тему",
diff --git a/public/language/rw/admin/settings/notifications.json b/public/language/rw/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/rw/admin/settings/notifications.json
+++ b/public/language/rw/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/rw/admin/settings/post.json b/public/language/rw/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/rw/admin/settings/post.json
+++ b/public/language/rw/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/rw/modules.json b/public/language/rw/modules.json
index 83bfc3d41d..3438a159da 100644
--- a/public/language/rw/modules.json
+++ b/public/language/rw/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/rw/topic.json b/public/language/rw/topic.json
index 925eb85ae9..6de704a7b1 100644
--- a/public/language/rw/topic.json
+++ b/public/language/rw/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Ibya Vuba Ujya ku bya Kera",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Urashaka gutangiza ahubwo ikiganiro gishya?",
     "stale.warning": "Ikiganiro ushaka kuvugaho cyarashaje. Wahitamo gutangiza ikiganiro gishya ariko wenda ukagaragaza kino mu gisubizo uza gushyiraho?",
     "stale.create": "Tangiza ikiganiro gishya",
diff --git a/public/language/sc/admin/settings/notifications.json b/public/language/sc/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/sc/admin/settings/notifications.json
+++ b/public/language/sc/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/sc/admin/settings/post.json b/public/language/sc/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/sc/admin/settings/post.json
+++ b/public/language/sc/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/sc/modules.json b/public/language/sc/modules.json
index c356c23ef2..9b26a1f13a 100644
--- a/public/language/sc/modules.json
+++ b/public/language/sc/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Strikethrough",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Link",
-    "composer.formatting.picture": "Picture",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Upload Image",
     "composer.upload-file": "Upload File",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/sc/topic.json b/public/language/sc/topic.json
index 16d6bebb47..4fdfd25116 100644
--- a/public/language/sc/topic.json
+++ b/public/language/sc/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Click here to return to the last read post in this thread.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Newest to Oldest",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "Create new topic instead?",
     "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
     "stale.create": "Create a new topic",
diff --git a/public/language/sk/admin/settings/notifications.json b/public/language/sk/admin/settings/notifications.json
index 188130c185..a873f10b02 100644
--- a/public/language/sk/admin/settings/notifications.json
+++ b/public/language/sk/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Oznámenia",
 	"welcome-notification": "Uvítacie oznámenie",
 	"welcome-notification-link": "Odkaz na uvítanie",
-	"welcome-notification-uid": "Uvítanie používateľa (UID)"
+	"welcome-notification-uid": "Uvítanie používateľa (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/sk/admin/settings/post.json b/public/language/sk/admin/settings/post.json
index a99c0684e0..8ebf0cc3d1 100644
--- a/public/language/sk/admin/settings/post.json
+++ b/public/language/sk/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Zobraziť záložku „Nápoveda”",
 	"composer.enable-plugin-help": "Povoliť zásuvné moduly pre pridanie obsahu do záložky nápovedy",
 	"composer.custom-help": "Používateľský text nápovedy",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Sledovanie IP adresy",
 	"ip-tracking.each-post": "Sledovať IP adresu pri každom príspevku",
 	"enable-post-history": "Povoliť históriu príspevkov"
diff --git a/public/language/sk/modules.json b/public/language/sk/modules.json
index 9df58064b4..53a270124f 100644
--- a/public/language/sk/modules.json
+++ b/public/language/sk/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Prečiarknuté",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Odkaz",
-    "composer.formatting.picture": "Obrázok",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Nahrať obrázok",
     "composer.upload-file": "Nahrať súbor",
     "composer.zen_mode": "Režim Zen",
diff --git a/public/language/sk/topic.json b/public/language/sk/topic.json
index 5b12c39a1b..9f742f4009 100644
--- a/public/language/sk/topic.json
+++ b/public/language/sk/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Kliknite sem pre návrat k poslednému prečítanému príspevku vo vlákne.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Od najnovších po najstaršie",
     "most_votes": "S najviac hlasmi",
     "most_posts": "S najviac príspevkami",
+    "most_views": "Most Views",
     "stale.title": "Vytvoriť novú tému namiesto?",
     "stale.warning": "Téma na ktorú odpovedáte je pomerne stará. Chceli by ste vytvoriť novú tému namiesto tejto, a odkazovať na ňu vo Vašej odpovedi?",
     "stale.create": "Vytvoriť novú tému",
diff --git a/public/language/sl/admin/advanced/errors.json b/public/language/sl/admin/advanced/errors.json
index 2cbbbce7ce..4e5a5ae888 100644
--- a/public/language/sl/admin/advanced/errors.json
+++ b/public/language/sl/admin/advanced/errors.json
@@ -1,5 +1,5 @@
 {
-	"figure-x": "Figure %1",
+	"figure-x": "Slika %1",
 	"error-events-per-day": "<code>%1</code> dogodkov na dan",
 	"error.404": "4040 ni najdeno",
 	"error.503": "503 storitev ni na voljo",
diff --git a/public/language/sl/admin/dashboard.json b/public/language/sl/admin/dashboard.json
index c334df9b0f..366af71742 100644
--- a/public/language/sl/admin/dashboard.json
+++ b/public/language/sl/admin/dashboard.json
@@ -62,17 +62,17 @@
 
 	"user-presence": "Prisotnost uporabnikov",
 	"on-categories": "On categories list",
-	"reading-posts": "Reading posts",
-	"browsing-topics": "Browsing topics",
+	"reading-posts": "Branje objav",
+	"browsing-topics": "Brskanje po temah",
 	"recent": "Nedavno",
 	"unread": "Neprebrano",
 
-	"high-presence-topics": "High Presence Topics",
+	"high-presence-topics": "Teme z visoko prisotnostjo",
 
 	"graphs.page-views": "Ogledov strani",
-	"graphs.page-views-registered": "Page Views Registered",
-	"graphs.page-views-guest": "Page Views Guest",
-	"graphs.page-views-bot": "Page Views Bot",
+	"graphs.page-views-registered": "Ogledov strani-registrirani",
+	"graphs.page-views-guest": "Ogledov strani-gosti",
+	"graphs.page-views-bot": "Ogledov strani-robot",
 	"graphs.unique-visitors": "Edinstveni obiskovalci",
 	"graphs.registered-users": "Registrirani uporabniki",
 	"graphs.anonymous-users": "Anonimni uporabniki",
diff --git a/public/language/sl/admin/development/info.json b/public/language/sl/admin/development/info.json
index 10d4238806..7187011bb0 100644
--- a/public/language/sl/admin/development/info.json
+++ b/public/language/sl/admin/development/info.json
@@ -1,5 +1,5 @@
 {
-	"you-are-on": "You are on <strong>%1:%2</strong>",
+	"you-are-on": "Ste na <strong>%1:%2</strong>",
 	"ip": "IP <strong>%1</strong>",
 	"nodes-responded": "%1 vozlišč se je odzvalo v %2ms!",
 	"host": "gostitelj",
diff --git a/public/language/sl/admin/development/logger.json b/public/language/sl/admin/development/logger.json
index 6ab9558149..ce73544633 100644
--- a/public/language/sl/admin/development/logger.json
+++ b/public/language/sl/admin/development/logger.json
@@ -1,12 +1,12 @@
 {
-	"logger-settings": "Logger Settings",
-	"description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.",
-	"explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.",
-	"enable-http": "Enable HTTP logging",
-	"enable-socket": "Enable socket.io event logging",
-	"file-path": "Path to log file",
+	"logger-settings": "Nastavitve beleženja",
+	"description": "Če omogočite potrditvena polja, boste prejemali dnevnike na svoj terminal. Če določite pot, se bodo dnevniki namesto tega shranili v datoteko. Zapisovanje HTTP je uporabno za zbiranje statističnih podatkov o tem, kdo, kdaj in do česa ljudje dostopajo na vašem forumu. Poleg beleženja zahtev HTTP lahko beležimo tudi dogodke socket.io. Zapisovanje Socket.io v kombinaciji z monitorjem redis-cli je lahko v veliko pomoč pri učenju notranjosti NodeBB.",
+	"explanation": "Preprosto preverite/počistite nastavitve beleženja, če želite omogočiti ali onemogočiti sprotno beleženje. Ponovni zagon ni potreben.",
+	"enable-http": "Omogoči HTTP prijave",
+	"enable-socket": "Omogoči beleženje dogodkov socket.io",
+	"file-path": "Pot do datoteke dnevnika",
 	"file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal",
 
-	"control-panel": "Logger Control Panel",
-	"update-settings": "Update Logger Settings"
+	"control-panel": "Nadzorna plošča beleženja",
+	"update-settings": "Posodobi nastavitve beleženja"
 }
\ No newline at end of file
diff --git a/public/language/sl/admin/extend/plugins.json b/public/language/sl/admin/extend/plugins.json
index a6e276dff7..648edefde3 100644
--- a/public/language/sl/admin/extend/plugins.json
+++ b/public/language/sl/admin/extend/plugins.json
@@ -13,11 +13,11 @@
 	"submit-anonymous-usage": "Submit anonymous plugin usage data.",
 	"reorder-plugins": "Re-order Plugins",
 	"order-active": "Order Active Plugins",
-	"dev-interested": "Interested in writing plugins for NodeBB?",
-	"docs-info": "Full documentation regarding plugin authoring can be found in the <a target=\"_blank\" href=\"https://docs.nodebb.org/development/plugins/\">NodeBB Docs Portal</a>.",
+	"dev-interested": "Vas zanima pisanje vtičnikov za NodeBB?",
+	"docs-info": "Celotno dokumentacijo o ustvarjanju vtičnikov najdete v <a target=\"_blank\" href=\"https://docs.nodebb.org/development/plugins/\">NodeBB dokumentnem portalu</a>.",
 
-	"order.description": "Certain plugins work ideally when they are initialised before/after other plugins.",
-	"order.explanation": "Plugins load in the order specified here, from top to bottom",
+	"order.description": "Nekateri vtičniki delujejo idealno, če so inicializirani pred/po drugih vtičnikih.",
+	"order.explanation": "Vtičniki se nalagajo po vrstnem redu, ki je tukaj naveden, od zgoraj navzdol",
 
 	"plugin-item.themes": "Teme",
 	"plugin-item.deactivate": "Deaktiviraj",
@@ -30,9 +30,9 @@
 	"plugin-item.upgrade": "Posodobi",
 	"plugin-item.more-info": "Za več informacij:",
 	"plugin-item.unknown": "Neznano",
-	"plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.",
+	"plugin-item.unknown-explanation": "Stanja tega vtičnika ni bilo mogoče določiti, morda zaradi napačne konfiguracije.",
 	"plugin-item.compatible": "Ta vtičnik deluje na NodeBB %1",
-	"plugin-item.not-compatible": "This plugin has no compatibility data, make sure it works before installing on your production environment.",
+	"plugin-item.not-compatible": "Ta vtičnik nima podatkov o združljivosti, preden ga namestite v svoje produkcijsko okolje, se prepričajte, da deluje.",
 
 	"alert.enabled": "Vtičnik omogočen",
 	"alert.disabled": "Vtičnik onemogočen",
@@ -44,14 +44,14 @@
 	"alert.upgrade-success": "Za popolno nadgradnjo tega vtičnika obnovite in ponovno zaženete vaš NodeB.",
 	"alert.install-success": "Vtičnik je uspešno nameščen, aktivirajte ga.",
 	"alert.uninstall-success": "Vtičnik je bil uspešno deaktiviran in odstranjen.",
-	"alert.suggest-error": "<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class=\"alert alert-danger\"><strong>Server returned (%1)</strong>: %2</div>",
-	"alert.package-manager-unreachable": "<p>NodeBB could not reach the package manager, an upgrade is not suggested at this time.</p>",
-	"alert.incompatible": "<p>Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.</p>",
-	"alert.possibly-incompatible": "<div class=\"alert alert-warning\"><p><strong>No Compatibility Information Found</strong></p><p>This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.</p></div><p>In the event that NodeBB cannot boot properly:</p><pre><code>$ ./nodebb reset plugin=\"%1\"</code></pre><p>Continue installation of latest version of this plugin?</p>",
-	"alert.reorder": "Plugins Re-ordered",
-	"alert.reorder-success": "Please rebuild and restart your NodeBB to fully complete the process.",
+	"alert.suggest-error": "<p>NodeBB ni mogel doseči upravitelja paketov, naj nadaljuje z namestitvijo najnovejše različice?</p><div class=\"alert alert-danger\"><strong>Strežnik je našel (%1)</strong>: %2</div>",
+	"alert.package-manager-unreachable": "<p>NodeBB ni mogel doseči upravitelja paketov, posodobitev v tem trenutku ni priporočena.</p>",
+	"alert.incompatible": "Vaša različica NodeBB (v%1) dovoljuje nadgradnjo tega vtičnika samo na različico v%2. Če želite namestiti novejšo različico tega vtičnika, posodobite svoj NodeBB.</p>",
+	"alert.possibly-incompatible": "<div class=\"alert alert-warning\">Podatke o združljivosti ni mogoče najti<strong><p><p>Ta vtičnik ni določil posebne različice za namestitev glede na vašo različico NodeBB. Popolne združljivosti ni mogoče zagotoviti, zato se lahko vaš NodeBB ne zažene več pravilno.</div><p>V primeru, da se NodeBB ne zažene pravilno:</p><pre><code>$ ./nodebb reset plugin=\"%1\"</code></pre><p>Ali želite nadaljevati z namestitvijo najnovejše različice tega vtičnika?</p>",
+	"alert.reorder": "Vtičniki preurejeni",
+	"alert.reorder-success": "Prosimo, da za dokončanje postopka v celoti obnovite in znova zaženete NodeBB.",
 
-	"license.title": "Plugin License Information",
-	"license.intro": "The plugin <strong>%1</strong> is licensed under the %2. Please read and understand the license terms prior to activating this plugin.",
+	"license.title": "Informacija o licenci vtičnika",
+	"license.intro": "Vtičnik <strong>%1</strong> je licenciran pod %2. Preden aktivirate ta vtičnik, preberite in razumite licenčne pogoje.",
 	"license.cta": "Ali želite nadaljevati z aktiviranjem tega vtičnika?"
 }
diff --git a/public/language/sl/admin/extend/rewards.json b/public/language/sl/admin/extend/rewards.json
index ab9b5bc650..48c9762b23 100644
--- a/public/language/sl/admin/extend/rewards.json
+++ b/public/language/sl/admin/extend/rewards.json
@@ -1,10 +1,10 @@
 {
 	"rewards": "Nagrade",
 	"condition-if-users": "If User's",
-	"condition-is": "Is:",
-	"condition-then": "Then:",
+	"condition-is": "Je:",
+	"condition-then": "Tedaj:",
 	"max-claims": "Kolikokrat je mogoče zahtevati nagrado",
-	"zero-infinite": "Enter 0 for infinite",
+	"zero-infinite": "Vnesite 0 za neskončno",
 	"delete": "Izbriši",
 	"enable": "Omogoči",
 	"disable": "Onemogoči",
diff --git a/public/language/sl/admin/extend/widgets.json b/public/language/sl/admin/extend/widgets.json
index 5564f020d0..6af766bf52 100644
--- a/public/language/sl/admin/extend/widgets.json
+++ b/public/language/sl/admin/extend/widgets.json
@@ -1,9 +1,9 @@
 {
 	"available": "Razpoložljivi pripomočki",
 	"explanation": "V spustnem meniju izberite pripomoček in ga povlecite in spustite v območje gradnikov predloge na levi.",
-	"none-installed": "No widgets found! Activate the widget essentials plugin in the <a href=\"%1\">plugins</a> control panel.",
+	"none-installed": "Pripomočki niso najdeni! Aktivirajte vtičnik za osnove pripomočkov na nadzorni plošči<a href=\"%1\">vtičnikov</a>.",
 	"clone-from": "Klonirajte pripomočke iz",
-	"containers.available": "Available Containers",
+	"containers.available": "Razpoložljivi vsebniki",
 	"containers.explanation": "Povlecite in spustite na kateri koli aktivni gradnik",
 	"containers.none": "Brez",
 	"container.well": "Well",
@@ -21,10 +21,10 @@
 	"error.select-clone": "Izberite stran, s katere želite klonirati",
 
 	"title": "Naslov",
-	"title.placeholder": "Title (only shown on some containers)",
-	"container": "Container",
+	"title.placeholder": "Naslov (vidno le v nekaterih vsebnikih)",
+	"container": "Vsebnik",
 	"container.placeholder": "Drag and drop a container or enter HTML here.",
-	"show-to-groups": "Show to groups",
-	"hide-from-groups": "Hide from groups",
+	"show-to-groups": "Prikaži skupinam",
+	"hide-from-groups": "Skrij skupinam",
 	"hide-on-mobile": "Hide on mobile"
 }
\ No newline at end of file
diff --git a/public/language/sl/admin/manage/admins-mods.json b/public/language/sl/admin/manage/admins-mods.json
index e0f39ed5d4..7009fad3a0 100644
--- a/public/language/sl/admin/manage/admins-mods.json
+++ b/public/language/sl/admin/manage/admins-mods.json
@@ -1,10 +1,10 @@
 {
-    "administrators": "Administrators",
-    "global-moderators": "Global Moderators",
-    "no-global-moderators": "No Global Moderators",
-    "moderators-of-category": "%1 Moderators",
-    "no-moderators": "No Moderators",
-    "add-administrator": "Add Administrator",
-    "add-global-moderator": "Add Global Moderator",
-    "add-moderator": "Add Moderator"
+    "administrators": "Skrbnik",
+    "global-moderators": "Globalni moderatorji",
+    "no-global-moderators": "Ni globalnih moderatorjev",
+    "moderators-of-category": "%1 moderator(jev)",
+    "no-moderators": "Ni moderatorjev",
+    "add-administrator": "Dodaj skrbnika",
+    "add-global-moderator": "Dodaj globalnega moderatorja",
+    "add-moderator": "Dodaj moderatorja"
 }
\ No newline at end of file
diff --git a/public/language/sl/admin/manage/categories.json b/public/language/sl/admin/manage/categories.json
index 36eb291256..d2bba96e4b 100644
--- a/public/language/sl/admin/manage/categories.json
+++ b/public/language/sl/admin/manage/categories.json
@@ -1,92 +1,92 @@
 {
 	"settings": "Nastavitve kategorije",
-	"privileges": "Privileges",
+	"privileges": "Privilegiji",
 
 	"name": "Ime kategorije",
 	"description": "Opis kategorije",
 	"bg-color": "Barva ozadja",
 	"text-color": "Barva besedila",
-	"bg-image-size": "Background Image Size",
-	"custom-class": "Custom Class",
-	"num-recent-replies": "# of Recent Replies",
+	"bg-image-size": "Velikost slike ozadja",
+	"custom-class": "Razred po meri",
+	"num-recent-replies": "# nedavnih odgovorov",
 	"ext-link": "Zunanja povezava",
 	"subcategories-per-page": "Podkategorij na stran",
-	"is-section": "Treat this category as a section",
+	"is-section": "Obravnavaj to kategorijo kot sekcijo",
 	"post-queue": "Čakalna vrsta objav",
-	"tag-whitelist": "Tag Whitelist",
+	"tag-whitelist": "Bela lista oznak",
 	"upload-image": "Naloži sliko",
 	"delete-image": "Odstrani",
 	"category-image": "Slika kategorije",
-	"parent-category": "Parent Category",
-	"optional-parent-category": "(Optional) Parent Category",
-	"top-level": "Top Level",
-	"parent-category-none": "(None)",
+	"parent-category": "Nadrejena kategorija",
+	"optional-parent-category": "(Izbirno) Nadrejena kategorija",
+	"top-level": "Vrhnja raven",
+	"parent-category-none": "(Brez)",
 	"copy-parent": "Copy Parent",
-	"copy-settings": "Copy Settings From",
-	"optional-clone-settings": "(Optional) Clone Settings From Category",
+	"copy-settings": "Kopiraj nastavitve iz",
+	"optional-clone-settings": "(Izbirno) Kloniraj nastavitve iz kategorije",
 	"clone-children": "Clone Children Categories And Settings",
-	"purge": "Purge Category",
+	"purge": "Počisti kategorijo",
 
 	"enable": "Omogoči",
 	"disable": "Onemogoči",
-	"edit": "Edit",
-	"analytics": "Analytics",
+	"edit": "Uredi",
+	"analytics": "Analitika",
 	"view-category": "Poglej kategorijo",
 	"set-order": "Nastavi vrstni red",
 	"set-order-help": "Če nastavite vrstni red kategorije, se bo ta kategorija premaknila in po potrebi posodobila vrstni red drugih kategorij. Najmanjša št. vrstnega reda je 1, kar kategorijo postavlja na vrh.",
 
 	"select-category": "Izberi kategorijo",
-	"set-parent-category": "Set Parent Category",
+	"set-parent-category": "Nastavi nadrejeno kategorijo",
 
-	"privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.",
-	"privileges.category-selector": "Configuring privileges for ",
-	"privileges.warning": "<strong>Note</strong>: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.",
-	"privileges.section-viewing": "Viewing Privileges",
-	"privileges.section-posting": "Posting Privileges",
-	"privileges.section-moderation": "Moderation Privileges",
-	"privileges.section-other": "Other",
+	"privileges.description": "V tem razdelku lahko konfigurirate pravice za nadzor dostopa za dele spletnega mesta. Privilegiji se lahko podelijo uporabniku ali skupini. V spodnjem spustnem meniju izberite področje učinka.",
+	"privileges.category-selector": "Konfiguriranje privilegijev za",
+	"privileges.warning": "<strong>Opomba</strong>: Nastavitve privilegijev pričnejo učinkovati takoj. Po prilagoditvi teh nastavitev kategorije ni potrebno shraniti.",
+	"privileges.section-viewing": "Pravice ogleda",
+	"privileges.section-posting": "Pravice za objavo",
+	"privileges.section-moderation": "Pravice spreminjanja",
+	"privileges.section-other": "Drugo",
 	"privileges.section-user": "Uporabnik",
 	"privileges.search-user": "Dodaj uporabnika",
-	"privileges.no-users": "No user-specific privileges in this category.",
+	"privileges.no-users": "V tej kategoriji ni uporabniških pravic.",
 	"privileges.section-group": "Skupina",
 	"privileges.group-private": "Ta skupina je zasebna",
-  	"privileges.inheritance-exception": "This group does not inherit privileges from registered-users group",
-  	"privileges.banned-user-inheritance": "Banned users inherit privileges from banned-users group",
+  	"privileges.inheritance-exception": "Ta skupina ne deduje pravic od skupine registriranih uporabnikov",
+  	"privileges.banned-user-inheritance": "Prepovedani uporabniki dedujejo pravice od skupine prepovedanih uporabnikov",
 	"privileges.search-group": "Dodaj skupino",
 	"privileges.copy-to-children": "Copy to Children",
 	"privileges.copy-from-category": "Kopiraj iz kategorije",
 	"privileges.copy-privileges-to-all-categories": "Kopiraj v vse kategorije",
 	"privileges.copy-group-privileges-to-children": "Copy this group's privileges to the children of this category.",
-	"privileges.copy-group-privileges-to-all-categories": "Copy this group's privileges to all categories.",
-	"privileges.copy-group-privileges-from": "Copy this group's privileges from another category.",
-	"privileges.inherit": "If the <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> user group, and so, privileges for additional groups need not be explicitly granted.",
-	"privileges.copy-success": "Privileges copied!",
+	"privileges.copy-group-privileges-to-all-categories": "Kopiraj pravice te skupine v vse kategorije.",
+	"privileges.copy-group-privileges-from": "Kopiraj pravice te skupine iz druge kategorije.",
+	"privileges.inherit": "Če je skupini <code>registeriranih uporabnikov</code> dodeljena posebna pravica, prejmejo vse druge skupine <strong>implicitno pravico</strong>, čeprav niso eksplicitno navedene/označene. Ta implicitna pravica se vam prikaže, ker so vsi uporabniki del skupine <code>registriranih uporabnikov</code>, zato pravic za dodatne skupine ni treba izrecno podeliti.",
+	"privileges.copy-success": "Pravice so kopirane!",
 
 	"analytics.back": "Nazaj na seznam kategorij",
-	"analytics.title": "Analytics for \"%1\" category",
-	"analytics.pageviews-hourly": "<strong>Figure 1</strong> &ndash; Hourly page views for this category</small>",
-	"analytics.pageviews-daily": "<strong>Figure 2</strong> &ndash; Daily page views for this category</small>",
-	"analytics.topics-daily": "<strong>Figure 3</strong> &ndash; Daily topics created in this category</small>",
-	"analytics.posts-daily": "<strong>Figure 4</strong> &ndash; Daily posts made in this category</small>",
+	"analytics.title": "Analitika za kategorijo \"%1\"",
+	"analytics.pageviews-hourly": "</strong>Slika 1</strong> &ndash; Urni ogledi strani za to kategorijo</small>",
+	"analytics.pageviews-daily": "</strong>Slika 2</strong> &ndash; Dnevni ogledi strani za to kategorijo</small>",
+	"analytics.topics-daily": "<strong>Slika 3</strong> &ndash; Dnevno ustvarjene teme v tej kategoriji</small>",
+	"analytics.posts-daily": "<strong>Slika 4</strong> &ndash; Dnevne objave v tej kategoriji</small>",
 
 	"alert.created": "Ustvarjeno",
 	"alert.create-success": "Kategorija je uspešno ustvarjena!",
 	"alert.none-active": "Nimate aktivnih kategorij.",
 	"alert.create": "Ustvari kategorijo",
 	"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
-	"alert.purge-success": "Category purged!",
-	"alert.copy-success": "Settings Copied!",
-	"alert.set-parent-category": "Set Parent Category",
+	"alert.purge-success": "Kategorija je počiščena!",
+	"alert.copy-success": "Nastavitve so kopirane!",
+	"alert.set-parent-category": "Nastavi nadrejeno kategorijo",
 	"alert.updated": "Posodobljene kategorije",
-	"alert.updated-success": "Category IDs %1 successfully updated.",
+	"alert.updated-success": "ID -ji kategorij %1 so uspešno posodobljeni.",
 	"alert.upload-image": "Naloži sliko kategorije",
 	"alert.find-user": "Poišči uporabnika",
-	"alert.user-search": "Search for a user here...",
+	"alert.user-search": "Išči uporabnika tukaj...",
 	"alert.find-group": "Poišči skupino",
-	"alert.group-search": "Search for a group here...",
-	"alert.not-enough-whitelisted-tags": "Whitelisted tags are less than minimum tags, you need to create more whitelisted tags!",
+	"alert.group-search": "Išči skupino tukaj...",
+	"alert.not-enough-whitelisted-tags": "Oznak na beli listi je manj od dovoljene spodnje meje, na belo listo dodajte več oznak!",
 	"collapse-all": "Strni vse",
 	"expand-all": "Razširi vse",
-	"disable-on-create": "Disable on create",
+	"disable-on-create": "Onemogoči pri ustvarjanju",
 	"no-matches": "Ni zadetkov"
 }
\ No newline at end of file
diff --git a/public/language/sl/admin/manage/privileges.json b/public/language/sl/admin/manage/privileges.json
index c7b819fe2e..de7c226776 100644
--- a/public/language/sl/admin/manage/privileges.json
+++ b/public/language/sl/admin/manage/privileges.json
@@ -1,6 +1,6 @@
 {
 	"global": "Global",
-	"admin": "Admin",
+	"admin": "Administrator",
 	"group-privileges": "Group Privileges",
 	"user-privileges": "User Privileges",
 	"edit-privileges": "Edit Privileges",
diff --git a/public/language/sl/admin/settings/notifications.json b/public/language/sl/admin/settings/notifications.json
index 9d3be6c1de..15cc81a25c 100644
--- a/public/language/sl/admin/settings/notifications.json
+++ b/public/language/sl/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Obvestila",
 	"welcome-notification": "Obvestilo o dobrodošlici",
 	"welcome-notification-link": "Povezava do obvestila o dobrodošlici",
-	"welcome-notification-uid": "Obvestilo o dobrodošlici za uporabnika (UID)"
+	"welcome-notification-uid": "Obvestilo o dobrodošlici za uporabnika (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/sl/admin/settings/post.json b/public/language/sl/admin/settings/post.json
index 28b28bde7e..424104587e 100644
--- a/public/language/sl/admin/settings/post.json
+++ b/public/language/sl/admin/settings/post.json
@@ -51,11 +51,14 @@
 	"signature.no-links": "Onemogoči povezave v podpisih",
 	"signature.no-images": "Onemogoči slike v podpisih",
 	"signature.max-length": "Največja dolžina podpisa",
-	"composer": "Composer Settings",
+	"composer": "Nastavitve sestavljalnika",
 	"composer-help": "The following settings govern the functionality and/or appearance of the post composer shown\n\t\t\t\tto users when they create new topics, or reply to existing topics.",
 	"composer.show-help": "Prikaži zavihek \"Pomoč\"",
 	"composer.enable-plugin-help": "Dovoli vtičnikom dodajanje vsebine na zavihek za pomoč",
 	"composer.custom-help": "Besedilo pomoči po meri",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP sledenje",
 	"ip-tracking.each-post": "Sledi IP naslov za vsako objavo",
 	"enable-post-history": "Omogoči zgodovino objav"
diff --git a/public/language/sl/flags.json b/public/language/sl/flags.json
index 5bd46b6b1d..be4fdfcc04 100644
--- a/public/language/sl/flags.json
+++ b/public/language/sl/flags.json
@@ -5,7 +5,7 @@
 	"no-flags": "Hooray! No flags found.",
 	"assignee": "Assignee",
 	"update": "Update",
-	"updated": "Updated",
+	"updated": "Posodobljeno",
 	"resolved": "Resolved",
 	"target-purged": "The content this flag referred to has been purged and is no longer available.",
 
@@ -13,61 +13,61 @@
 	"quick-filters": "Quick Filters",
 	"filter-active": "There are one or more filters active in this list of flags",
 	"filter-reset": "Remove Filters",
-	"filters": "Filter Options",
+	"filters": "Možnosti filtra",
 	"filter-reporterId": "Reporter UID",
 	"filter-targetUid": "Flagged UID",
 	"filter-type": "Flag Type",
 	"filter-type-all": "All Content",
-	"filter-type-post": "Post",
-	"filter-type-user": "User",
+	"filter-type-post": "Objava",
+	"filter-type-user": "Uporabnik",
 	"filter-state": "State",
 	"filter-assignee": "Assignee UID",
-	"filter-cid": "Category",
-	"filter-quick-mine": "Assigned to me",
-	"filter-cid-all": "All categories",
-	"apply-filters": "Apply Filters",
-	"more-filters": "More Filters",
-	"fewer-filters": "Fewer Filters",
+	"filter-cid": "Kategorija",
+	"filter-quick-mine": "Dodeljeno meni",
+	"filter-cid-all": "Vse kategorije",
+	"apply-filters": "Uveljavi filtre",
+	"more-filters": "Več filtrov",
+	"fewer-filters": "Manj filtrov",
 
-	"quick-actions": "Quick Actions",
+	"quick-actions": "Hitra dejanja",
 	"flagged-user": "Flagged User",
-	"view-profile": "View Profile",
-	"start-new-chat": "Start New Chat",
+	"view-profile": "Poglej profil",
+	"start-new-chat": "Začni nov klepet",
 	"go-to-target": "View Flag Target",
-	"assign-to-me": "Assign To Me",
-	"delete-post": "Delete Post",
+	"assign-to-me": "Dodeli meni",
+	"delete-post": "Izbriši objavo",
 	"purge-post": "Purge Post",
-	"restore-post": "Restore Post",
+	"restore-post": "Obnovi objavo",
 
-	"user-view": "View Profile",
-	"user-edit": "Edit Profile",
+	"user-view": "Poglej profil",
+	"user-edit": "Uredi profil",
 
 	"notes": "Flag Notes",
-	"add-note": "Add Note",
-	"no-notes": "No shared notes.",
+	"add-note": "Dodaj opombo",
+	"no-notes": "Ni deljenih opomb.",
 	"delete-note-confirm": "Are you sure you want to delete this flag note?",
-	"note-added": "Note Added",
-	"note-deleted": "Note Deleted",
+	"note-added": "Opomba dodana",
+	"note-deleted": "Opomba izbrisana",
 
 	"history": "Account &amp; Flag History",
 	"no-history": "No flag history.",
 
 	"state-all": "All states",
-	"state-open": "New/Open",
+	"state-open": "Nov/Odpri",
 	"state-wip": "Work in Progress",
 	"state-resolved": "Resolved",
-	"state-rejected": "Rejected",
-	"no-assignee": "Not Assigned",
+	"state-rejected": "Zavrnjeno",
+	"no-assignee": "Ni dodeljeno",
 
-	"sort": "Sort by",
+	"sort": "Razvrsti po",
 	"sort-newest": "Newest first",
 	"sort-oldest": "Oldest first",
 	"sort-reports": "Most reports",
 	"sort-all": "All flag types...",
 	"sort-posts-only": "Posts only...",
-	"sort-downvotes": "Most downvotes",
-	"sort-upvotes": "Most upvotes",
-	"sort-replies": "Most replies",
+	"sort-downvotes": "Največ glasov proti",
+	"sort-upvotes": "Največ glasov za",
+	"sort-replies": "Največ odgovorov",
 
 	"modal-title": "Report Content",
 	"modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.",
diff --git a/public/language/sl/ip-blacklist.json b/public/language/sl/ip-blacklist.json
index c038769e2f..35baff0c52 100644
--- a/public/language/sl/ip-blacklist.json
+++ b/public/language/sl/ip-blacklist.json
@@ -1,19 +1,19 @@
 {
 	"lead": "Tu nastavite svoj črni seznam IP.",
-	"description": "Occasionally, a user account ban is not enough of a deterrant. Other times, restricting access to the forum to a specific IP or a range of IPs is the best way to protect a forum. In these scenarios, you can add troublesome IP addresses or entire CIDR blocks to this blacklist, and they will be prevented from logging in to or registering a new account.",
+	"description": "Včasih prepoved uporabniškega računa ni dovolj odvračilna. V drugih primerih je najboljši način za zaščito foruma omejitev dostopa do foruma za določen IP ali vrsto IP-jev. V teh scenarijih lahko na ta črni seznam dodate problematične naslove IP ali celotne bloke CIDR, pri čemer se jim prepreči prijava ali registracija novega računa.",
 	"active-rules": "Aktivna pravila",
-	"validate": "Validate Blacklist",
-	"apply": "Apply Blacklist",
-	"hints": "Syntax Hints",
-	"hint-1": "Define a single IP addresses per line. You can add IP blocks as long as they follow the CIDR format (e.g. <code>192.168.100.0/22</code>).",
-	"hint-2": "You can add in comments by starting lines with the <code>#</code> symbol.",
+	"validate": "Potrdi črno listo",
+	"apply": "Uveljavi črno listo",
+	"hints": "namigi za sintakso",
+	"hint-1": "Določite posamezne naslove IP na vrstico. Bloke IP lahko dodate, dokler sledijo formatu CIDR (e.g. <code>192.168.100.0/22</code>).",
+	"hint-2": "Komentarje lahko dodajate tako, da vrstice začnete z znakom <code>#</code>.",
 
-	"validate.x-valid": "<strong>%1</strong> out of <strong>%2</strong> rule(s) valid.",
-	"validate.x-invalid": "The following <strong>%1</strong> rules are invalid:",
+	"validate.x-valid": "<strong>%1</strong> od <strong>%2</strong> pravil je neveljavnih.",
+	"validate.x-invalid": "Naslednjih <strong>%1</strong> pravil je neveljavnih:",
 
-	"alerts.applied-success": "Blacklist Applied",
+	"alerts.applied-success": "Črna lista je uveljavljena",
 
-	"analytics.blacklist-hourly": "<strong>Figure 1</strong> &ndash; Blacklist hits per hour",
-	"analytics.blacklist-daily": "<strong>Figure 2</strong> &ndash; Blacklist hits per day",
-	"ip-banned": "IP banned"
+	"analytics.blacklist-hourly": "<strong>Slika 1</strong> &ndash; Zadetki na črni listi na uro",
+	"analytics.blacklist-daily": "<strong>Slika 2</strong> &ndash; Zadetki na črni listi na dan",
+	"ip-banned": "Prepovedan IP"
 }
\ No newline at end of file
diff --git a/public/language/sl/modules.json b/public/language/sl/modules.json
index 3032028b52..b236194486 100644
--- a/public/language/sl/modules.json
+++ b/public/language/sl/modules.json
@@ -13,28 +13,28 @@
     "chat.recent-chats": "Zadnji klepeti",
     "chat.contacts": "Stiki",
     "chat.message-history": "Zgodovina klepeta",
-    "chat.message-deleted": "Message Deleted",
-    "chat.options": "Chat options",
+    "chat.message-deleted": "Sporočilo izbrisano",
+    "chat.options": "Možnosti klepeta",
     "chat.pop-out": "Klepet v novem oknu",
-    "chat.minimize": "Minimize",
-    "chat.maximize": "Povečaj",
+    "chat.minimize": "Minimiziraj",
+    "chat.maximize": "Maksimiraj",
     "chat.seven_days": "7 dni",
     "chat.thirty_days": "30 dni",
     "chat.three_months": "3 meseci",
     "chat.delete_message_confirm": "Ali ste prepričani, da želite izbrisati to sporočilo?",
-    "chat.retrieving-users": "Retrieving users...",
-    "chat.manage-room": "Manage Chat Room",
+    "chat.retrieving-users": "Pridobivanje uporabnikov...",
+    "chat.manage-room": "Upravljaj sobo klepeta",
     "chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation. Only room owners (<i class=\"fa fa-star text-warning\"></i>) may remove users from chat rooms.",
     "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?",
     "chat.rename-room": "Rename Room",
     "chat.rename-placeholder": "Enter your room name here",
     "chat.rename-help": "The room name set here will be viewable by all participants in the room.",
-    "chat.leave": "Leave Chat",
-    "chat.leave-prompt": "Are you sure you wish to leave this chat?",
-    "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.",
+    "chat.leave": "Zapusti klepet",
+    "chat.leave-prompt": "Ste prepričani, da želite zapustiti ta klepet?",
+    "chat.leave-help": "Če zapustite ta klepet boste izključeni iz prihodnje korespondence v tem klepetu. Če boste v prihodnosti v klepet znova dodani, ne boste videli zgodovine klepeta pred ponovno pridružitvijo.",
     "chat.in-room": "In this room",
     "chat.kick": "Kick",
-    "chat.show-ip": "Show IP",
+    "chat.show-ip": "Pokaži IP",
     "chat.owner": "Room Owner",
     "chat.system.user-join": "%1 has joined the room",
     "chat.system.user-leave": "%1 has left the room",
@@ -54,17 +54,17 @@
     "composer.formatting.strikethrough": "Prečrtano",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "Povezava",
-    "composer.formatting.picture": "Slika",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Prenesi sliko",
     "composer.upload-file": "Prenesi datoteko",
     "composer.zen_mode": "Zen način",
-    "composer.select_category": "Select a category",
-    "composer.textarea.placeholder": "Enter your post content here, drag and drop images",
+    "composer.select_category": "Izberi kategorijo",
+    "composer.textarea.placeholder": "Tukaj vnesite vsebino objave, povlecite in spustite slike",
     "composer.schedule-for": "Schedule topic for",
-    "composer.schedule-date": "Date",
-    "composer.schedule-time": "Time",
+    "composer.schedule-date": "Datum",
+    "composer.schedule-time": "Čas",
     "composer.cancel-scheduling": "Cancel Scheduling",
-    "composer.set-schedule-date": "Set Date",
+    "composer.set-schedule-date": "Nastavi datum",
     "bootbox.ok": "V redu",
     "bootbox.cancel": "Prekliči",
     "bootbox.confirm": "Potrdi",
diff --git a/public/language/sl/topic.json b/public/language/sl/topic.json
index 2987a75dfb..bfa42068c4 100644
--- a/public/language/sl/topic.json
+++ b/public/language/sl/topic.json
@@ -1,6 +1,6 @@
 {
     "topic": "Tema",
-    "title": "Title",
+    "title": "Naslov",
     "no_topics_found": "Ni najdenih tem!",
     "no_posts_found": "Ni najdenih objav!",
     "post_is_deleted": "Ta objava je izbrisana!",
@@ -12,41 +12,42 @@
     "notify_me": "Bodi obveščen o novih odgovorih na to temo",
     "quote": "Citiraj",
     "reply": "Odgovori",
-    "replies_to_this_post": "%1 Replies",
-    "one_reply_to_this_post": "1 Reply",
-    "last_reply_time": "Last reply",
+    "replies_to_this_post": "Št. odogvorov: %1",
+    "one_reply_to_this_post": "1 odgovor",
+    "last_reply_time": "Zadnji odgovor",
     "reply-as-topic": "Odgovori s temo",
     "guest-login-reply": "Prijavi se za odgovor",
     "login-to-view": "🔒 Log in to view",
     "edit": "Uredi",
     "delete": "Izbriši",
-    "delete-event": "Delete Event",
-    "delete-event-confirm": "Are you sure you want to delete this event?",
+    "delete-event": "Izbriši dogodek",
+    "delete-event-confirm": "Ste prepričani, da želite izbrisati ta dogodek?",
     "purge": "Očisti",
-    "restore": "Razveljavi",
+    "restore": "Obnovi",
     "move": "Premakni",
-    "change-owner": "Change Owner",
+    "change-owner": "Spremeni lastnika",
     "fork": "Razcepi",
     "link": "Povezava",
     "share": "Deli",
     "tools": "Orodja",
     "locked": "Zaklenjeno",
     "pinned": "Pripeto",
-    "pinned-with-expiry": "Pinned until %1",
+    "pinned-with-expiry": "Pripeto do %1",
     "scheduled": "Scheduled",
     "moved": "Premaknjeno",
     "moved-from": "Moved from %1",
-    "copy-ip": "Copy IP",
-    "ban-ip": "Ban IP",
-    "view-history": "Edit History",
-    "locked-by": "Locked by",
-    "unlocked-by": "Unlocked by",
-    "pinned-by": "Pinned by",
-    "unpinned-by": "Unpinned by",
-    "deleted-by": "Deleted by",
-    "restored-by": "Restored by",
+    "copy-ip": "Kopiraj IP",
+    "ban-ip": "Prepovej IP",
+    "view-history": "Uredi zgodovino",
+    "locked-by": "Zaklenil/a",
+    "unlocked-by": "Odklenil/a",
+    "pinned-by": "Pripel/a",
+    "unpinned-by": "Odpel/a",
+    "deleted-by": "Izbrisal/a",
+    "restored-by": "Povrnil/a",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klikni tukaj za vrnitev na zadnje prebrano objavo v tej niti",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -82,8 +83,8 @@
     "thread_tools.move": "Premakni temo",
     "thread_tools.move-posts": "Premakni objave",
     "thread_tools.move_all": "Premakni vse",
-    "thread_tools.change_owner": "Change Owner",
-    "thread_tools.select_category": "Select Category",
+    "thread_tools.change_owner": "Spremeni lastnika",
+    "thread_tools.select_category": "Izberi kategorijo",
     "thread_tools.fork": "Razcepi temo",
     "thread_tools.delete": "Izbriši temo",
     "thread_tools.delete-posts": "Izbriši objave",
@@ -92,23 +93,23 @@
     "thread_tools.restore_confirm": "Ste prepričani, da želite obnoviti to temo?",
     "thread_tools.purge": "Očisti temo",
     "thread_tools.purge_confirm": "Ste prepričani, da želite očistiti to temo?",
-    "thread_tools.merge_topics": "Merge Topics",
-    "thread_tools.merge": "Merge",
-    "topic_move_success": "This topic will be moved to \"%1\" shortly. Click here to undo.",
-    "topic_move_multiple_success": "These topics will be moved to \"%1\" shortly. Click here to undo.",
-    "topic_move_all_success": "All topics will be moved to \"%1\" shortly. Click here to undo.",
-    "topic_move_undone": "Topic move undone",
-    "topic_move_posts_success": "Posts will be moved shortly. Click here to undo.",
-    "topic_move_posts_undone": "Post move undone",
+    "thread_tools.merge_topics": "Združi teme",
+    "thread_tools.merge": "Združi",
+    "topic_move_success": "Ta tema bo kmalu premaknjena v \"%1\". Kliknite tukaj, če želite razveljaviti.",
+    "topic_move_multiple_success": "Te teme bodo kmalu premaknjene v \"%1\". Kliknite tukaj, če želite razveljaviti.",
+    "topic_move_all_success": "Vse teme bodo kmalu premaknjene v \"%1\". Kliknite tukaj, če želite razveljaviti.",
+    "topic_move_undone": "Premik teme razveljavljen",
+    "topic_move_posts_success": "Objave bodo kmalu premaknjene. Kliknite tukaj, če želite razveljaviti.",
+    "topic_move_posts_undone": "Premik objav razveljavljen",
     "post_delete_confirm": "Ste prepričani, da želite izbrisati to objavo?",
-    "post_restore_confirm": "Ste prepričani, da želite razveljaviti to objavo?",
+    "post_restore_confirm": "Ste prepričani, da želite obnoviti to objavo?",
     "post_purge_confirm": "Ste prepričani, da želite očistiti to objavo?",
-    "pin-modal-expiry": "Expiration Date",
+    "pin-modal-expiry": "Datum poteka veljavnosti",
     "pin-modal-help": "You can optionally set an expiration date for the pinned topic(s) here. Alternatively, you can leave this field blank to have the topic stay pinned until it is manually unpinned.",
     "load_categories": "Nalagam kategorije",
     "confirm_move": "Premakni",
     "confirm_fork": "Razcepi",
-    "bookmark": "Bookmark",
+    "bookmark": "Zaznamek",
     "bookmarks": "Zaznamki",
     "bookmarks.has_no_bookmarks": "Zaznamovali še niste nobenih objav.",
     "loading_more_posts": "Nalagam več objav",
@@ -117,23 +118,23 @@
     "move_post": "Premakni objavo",
     "post_moved": "Objava premaknjena!",
     "fork_topic": "Razcepi temo",
-    "enter-new-topic-title": "Enter new topic title",
+    "enter-new-topic-title": "Vnesite nov naslov teme",
     "fork_topic_instruction": "Klikni na objavo, ki o želiš odcepiti",
     "fork_no_pids": "Ni izbranih objav!",
-    "no-posts-selected": "No posts selected!",
-    "x-posts-selected": "%1 post(s) selected",
+    "no-posts-selected": "Ni izbranih objav!",
+    "x-posts-selected": "Izbranih objav: %1 ",
     "x-posts-will-be-moved-to-y": "%1 post(s) will be moved to \"%2\"",
     "fork_pid_count": "Izbranih objav: %1 ",
     "fork_success": "Uspešno ste razcepili temo! Klikni tu za ogled te teme.",
     "delete_posts_instruction": "Kliknite na teme, ki jih želite izbrisati/očistiti ",
-    "merge_topics_instruction": "Click the topics you want to merge or search for them",
-    "merge-topic-list-title": "List of topics to be merged",
-    "merge-options": "Merge options",
-    "merge-select-main-topic": "Select the main topic",
-    "merge-new-title-for-topic": "New title for topic",
-    "topic-id": "Topic ID",
-    "move_posts_instruction": "Click the posts you want to move then enter a topic ID or go to the target topic",
-    "change_owner_instruction": "Click the posts you want to assign to another user",
+    "merge_topics_instruction": "Kliknite teme, ki jih želite združiti, ali jih poiščite",
+    "merge-topic-list-title": "Seznam tem za združevanje",
+    "merge-options": "Možnosti združevanja",
+    "merge-select-main-topic": "Izberi glavno temo",
+    "merge-new-title-for-topic": "Nov naslov teme",
+    "topic-id": "ID teme",
+    "move_posts_instruction": "Kliknite objave, ki jih želite premakniti, nato vnesite ID teme ali pojdite na ciljno temo",
+    "change_owner_instruction": "Kliknite objave, ki jih želite dodeliti drugemu uporabniku",
     "composer.title_placeholder": "Vpiši naslov teme...",
     "composer.handle_placeholder": "Enter your name/handle here",
     "composer.discard": "Zavrzi",
@@ -141,7 +142,7 @@
     "composer.schedule": "Schedule",
     "composer.replying_to": "Odgovor na %1",
     "composer.new_topic": "Nova tema",
-    "composer.editing": "Editing",
+    "composer.editing": "Urejanje",
     "composer.uploading": "nalagam...",
     "composer.thumb_url_label": "Prilepite URL sličice teme",
     "composer.thumb_title": "Dodajte sličico tej temi",
@@ -158,24 +159,25 @@
     "newest_to_oldest": "Od novejšega do starejšega",
     "most_votes": "Največ glasov",
     "most_posts": "Največ objav",
+    "most_views": "Največ ogledov",
     "stale.title": "Raje ustvari novo temo?",
     "stale.warning": "Tema na katero odgovarjaš je precej stara. A ne bi raje ustvaril novo temo namesto te, z sklicem na to v tvojem odgovoru?",
     "stale.create": "Ustvari novo temo",
     "stale.reply_anyway": "Vseeno odgovori na to temo",
     "link_back": "Odg: [%1](%2)",
-    "diffs.title": "Post Edit History",
+    "diffs.title": "Zgodovina urejanja objav",
     "diffs.description": "This post has <strong>%1</strong> revisions. Click one of the revisions below to see the post content at that point in time.",
     "diffs.no-revisions-description": "This post has <strong>%1</strong> revisions.",
-    "diffs.current-revision": "current revision",
-    "diffs.original-revision": "original revision",
-    "diffs.restore": "Restore this revision",
-    "diffs.restore-description": "A new revision will be appended to this post's edit history after restoring.",
-    "diffs.post-restored": "Post successfully restored to earlier revision",
-    "diffs.delete": "Delete this revision",
-    "diffs.deleted": "Revision deleted",
+    "diffs.current-revision": "trenutna različica",
+    "diffs.original-revision": "izvirna različica",
+    "diffs.restore": "Obnovi to različico",
+    "diffs.restore-description": "Po obnovitvi bo v zgodovino urejanj te objave dodana nova različica.",
+    "diffs.post-restored": "Objava je bila uspešno obnovljena na prejšnjo različico",
+    "diffs.delete": "Izbriši to različico",
+    "diffs.deleted": "Različica izbrisana",
     "timeago_later": "%1 later",
     "timeago_earlier": "%1 earlier",
-    "first-post": "First post",
-    "last-post": "Last post",
-    "post-quick-reply": "Post quick reply"
+    "first-post": "Prva objava",
+    "last-post": "Zadnja obava",
+    "post-quick-reply": "Objavi hitri odgovor"
 }
\ No newline at end of file
diff --git a/public/language/sr/admin/settings/notifications.json b/public/language/sr/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/sr/admin/settings/notifications.json
+++ b/public/language/sr/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/sr/admin/settings/post.json b/public/language/sr/admin/settings/post.json
index f3ae9fccb5..c1a2a7003b 100644
--- a/public/language/sr/admin/settings/post.json
+++ b/public/language/sr/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Prikaži tab \"Pomoć\"",
 	"composer.enable-plugin-help": "Dozvoli plugin-ovima da dodaju sadržaj na tab-u \"pomoć\"",
 	"composer.custom-help": "Prilagođen tekst za pomoć",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Praćenje IP adrese",
 	"ip-tracking.each-post": "Prati IP Adresu za svaki post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/sr/modules.json b/public/language/sr/modules.json
index 436407a0db..2478273950 100644
--- a/public/language/sr/modules.json
+++ b/public/language/sr/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Прецртано",
     "composer.formatting.code": "Код",
     "composer.formatting.link": "Веза",
-    "composer.formatting.picture": "Слика",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Отпреми слику",
     "composer.upload-file": "Отпреми датотеку",
     "composer.zen_mode": "Цео екран",
diff --git a/public/language/sr/topic.json b/public/language/sr/topic.json
index f2265717a1..798f6d2dca 100644
--- a/public/language/sr/topic.json
+++ b/public/language/sr/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Вратио",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Порука је на чекању за одобрење &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Кликните овде за повратак на последњу прочитану поруку у овој теми.",
     "flag-post": "Обележи заставицом поруку",
     "flag-user": "Обележи заставицом корисника",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Од новијих ка старијим",
     "most_votes": "Највише гласова",
     "most_posts": "Највише порука",
+    "most_views": "Most Views",
     "stale.title": "Креирати нову тему уместо тога?",
     "stale.warning": "Тема у којој желите да одговорите је сувише стара. Да ли желите да уместо тога креирате нову тему и упутите на ову у вашем одговору?",
     "stale.create": "Креирај нову тему",
diff --git a/public/language/sv/admin/settings/notifications.json b/public/language/sv/admin/settings/notifications.json
index da6c9680a3..c6d8b928ce 100644
--- a/public/language/sv/admin/settings/notifications.json
+++ b/public/language/sv/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Notifications",
 	"welcome-notification": "Welcome Notification",
 	"welcome-notification-link": "Welcome Notification Link",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/sv/admin/settings/post.json b/public/language/sv/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/sv/admin/settings/post.json
+++ b/public/language/sv/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/sv/modules.json b/public/language/sv/modules.json
index 66bb0a23a3..628c4de96b 100644
--- a/public/language/sv/modules.json
+++ b/public/language/sv/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Genomstrykning",
     "composer.formatting.code": "Kod",
     "composer.formatting.link": "Länk",
-    "composer.formatting.picture": "Bild",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Ladda upp bild",
     "composer.upload-file": "Ladda upp fil",
     "composer.zen_mode": "Zen Mode",
diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json
index e29fc805c1..f88cf9d472 100644
--- a/public/language/sv/topic.json
+++ b/public/language/sv/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Klicka här för att återgå till senast lästa inlägg i detta ämne.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Nyaste till äldst",
     "most_votes": "Flest röster",
     "most_posts": "Flest inlägg",
+    "most_views": "Most Views",
     "stale.title": "Skapa nytt ämne istället?",
     "stale.warning": "Ämnet du svarar på är ganska gammalt. Vill du skapa ett nytt ämne istället och inkludera en referens till det här ämnet i ditt inlägg?",
     "stale.create": "Skapa nytt ämne",
diff --git a/public/language/th/admin/settings/notifications.json b/public/language/th/admin/settings/notifications.json
index c2f5b8b9b9..0d8e4ec3c1 100644
--- a/public/language/th/admin/settings/notifications.json
+++ b/public/language/th/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "การแจ้งเตือน",
 	"welcome-notification": "การยินดีต้อนรับแจ้งเตือน",
 	"welcome-notification-link": "ลิงค์การยินดีต้อนรับแจ้งเตือน",
-	"welcome-notification-uid": "Welcome Notification User (UID)"
+	"welcome-notification-uid": "Welcome Notification User (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/th/admin/settings/post.json b/public/language/th/admin/settings/post.json
index 27493aafbd..00baa56fc1 100644
--- a/public/language/th/admin/settings/post.json
+++ b/public/language/th/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Show \"Help\" tab",
 	"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
 	"composer.custom-help": "Custom Help Text",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP Tracking",
 	"ip-tracking.each-post": "Track IP Address for each post",
 	"enable-post-history": "Enable Post History"
diff --git a/public/language/th/modules.json b/public/language/th/modules.json
index 7e66c6fbbc..7c8d925289 100644
--- a/public/language/th/modules.json
+++ b/public/language/th/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "ขีดเส้นใต้",
     "composer.formatting.code": "Code",
     "composer.formatting.link": "ลิ้งค์",
-    "composer.formatting.picture": "รูปภาพ",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "อัพโหลดรูปภาพ",
     "composer.upload-file": "อัพโหลดไฟล์",
     "composer.zen_mode": "เซ็นโหมด",
diff --git a/public/language/th/topic.json b/public/language/th/topic.json
index 94810b78fc..5af3eb6a83 100644
--- a/public/language/th/topic.json
+++ b/public/language/th/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "คลิกที่นี่เพื่อกลับไปยังโพสต์ล่าสุดในหัวข้อนี้",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "ใหม่สุดไปยังเก่าสุด",
     "most_votes": "Most Votes",
     "most_posts": "Most Posts",
+    "most_views": "Most Views",
     "stale.title": "ตั้งกระทู้ใหม่แทนไหม?",
     "stale.warning": "กระทู้ที่คุณกำลังตอบเก่าไปหน่อยนะ อยากจะลองตั้งกระทู้ใหม่แทนไหมล่ะ? แล้วก็อ้างอิงกระทู้นี้ไปยังคำตอบของคุณ",
     "stale.create": "ตั้งกระทู้ใหม่",
diff --git a/public/language/tr/admin/settings/notifications.json b/public/language/tr/admin/settings/notifications.json
index 6aa0a327c9..a4b20aa0d8 100644
--- a/public/language/tr/admin/settings/notifications.json
+++ b/public/language/tr/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Bildirimler",
 	"welcome-notification": "Hoş Geldin Bildirimi",
 	"welcome-notification-link": "Hoş Geldin Bildirimi Bağlantısı",
-	"welcome-notification-uid": "Kullanıcı Hoş Geldiniz Bildirimi  (UID)"
+	"welcome-notification-uid": "Kullanıcı Hoş Geldiniz Bildirimi  (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/tr/admin/settings/post.json b/public/language/tr/admin/settings/post.json
index 8122ec2fd1..dee17e7dc2 100644
--- a/public/language/tr/admin/settings/post.json
+++ b/public/language/tr/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "\"Yardım\" sekmesini göster",
 	"composer.enable-plugin-help": "Eklentilerin yardım sekmesine içerik eklemesine izin ver",
 	"composer.custom-help": "Özel Yardım Metni",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP İzleme",
 	"ip-tracking.each-post": "Her ileti için IP Adresini takip et",
 	"enable-post-history": "Gönderi Geçmişini Etkinleştir"
diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json
index 563ef8c187..fdc0ff7dac 100644
--- a/public/language/tr/modules.json
+++ b/public/language/tr/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Üstüçizili",
     "composer.formatting.code": "Kod",
     "composer.formatting.link": "Bağlantı",
-    "composer.formatting.picture": "Görsel",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Görsel Yükle",
     "composer.upload-file": "Dosya Yükle",
     "composer.zen_mode": "Tam ekran modu",
diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json
index 10c0c7322c..172e29cdbb 100644
--- a/public/language/tr/topic.json
+++ b/public/language/tr/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Tekrar Yüklendi",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "İleti onay için sıraya alındı &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Bu konuda en son kaldığın yere dönmek için tıkla.",
     "flag-post": "Bu iletiyi şikayet et",
     "flag-user": "Bu kullanıcıyı şikayet et",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "En yeniden en eskiye",
     "most_votes": "En çok oylanan",
     "most_posts": "En çok ileti yazılan",
+    "most_views": "Most Views",
     "stale.title": "Bunun yerine yeni bir başlık oluşturun?",
     "stale.warning": "Yanıtlamak istediğiniz başlık oldukça eski. Bu başlığa referans oluşturacak yeni bir başlık oluşturmak ister misiniz?",
     "stale.create": "Yeni bir başlık oluştur",
diff --git a/public/language/uk/admin/settings/notifications.json b/public/language/uk/admin/settings/notifications.json
index 55f71dae63..2322bf6836 100644
--- a/public/language/uk/admin/settings/notifications.json
+++ b/public/language/uk/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Сповіщення",
 	"welcome-notification": "Сповіщення \"Ласкаво просимо\"",
 	"welcome-notification-link": "Посилання для сповіщення \"Ласкаво просимо\"",
-	"welcome-notification-uid": "Сповіщення \"Ласкаво просимо\" для користувача (UID)"
+	"welcome-notification-uid": "Сповіщення \"Ласкаво просимо\" для користувача (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/uk/admin/settings/post.json b/public/language/uk/admin/settings/post.json
index f59375f68f..5219e9aded 100644
--- a/public/language/uk/admin/settings/post.json
+++ b/public/language/uk/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Показувати вкладку \"Довідка\"",
 	"composer.enable-plugin-help": "Дозволити плагінам додавати зміст довідки",
 	"composer.custom-help": "Користувацький текст довідки",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "Відстеження IP",
 	"ip-tracking.each-post": "Відстежувати IP адреси для кожного посту",
 	"enable-post-history": "Увімкнути Історію Постів"
diff --git a/public/language/uk/modules.json b/public/language/uk/modules.json
index 44bdbf848b..ef5bff16fe 100644
--- a/public/language/uk/modules.json
+++ b/public/language/uk/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Закреслений",
     "composer.formatting.code": "Код",
     "composer.formatting.link": "Посилання",
-    "composer.formatting.picture": "Зображення",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Завантажити зображення",
     "composer.upload-file": "Завантажити файл",
     "composer.zen_mode": "Режим Дзен",
diff --git a/public/language/uk/topic.json b/public/language/uk/topic.json
index aa7b3e1d9e..732286d2ce 100644
--- a/public/language/uk/topic.json
+++ b/public/language/uk/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "Натисніть тут, щоб повернутися до останнього прочитаного посту у цій темі.",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Нові > Старі",
     "most_votes": "Найбільше Голосів",
     "most_posts": "Найбільше Постів",
+    "most_views": "Most Views",
     "stale.title": "Створити натомість нову тему?",
     "stale.warning": "Тема на котру ви відповідаєте досить стара. Не бажаєте натомість створити новую тему і зіслатися на цю у вашій відповіді?",
     "stale.create": "Так, створити нову тему",
diff --git a/public/language/vi/admin/settings/notifications.json b/public/language/vi/admin/settings/notifications.json
index 407bc55bc3..76e4a8a1dc 100644
--- a/public/language/vi/admin/settings/notifications.json
+++ b/public/language/vi/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "Thông báo",
 	"welcome-notification": "Thông Báo Chào Mừng",
 	"welcome-notification-link": "Liên Kết Thông Báo Chào Mừng",
-	"welcome-notification-uid": "Thông Báo Chào Mừng Người Dùng (UID)"
+	"welcome-notification-uid": "Thông Báo Chào Mừng Người Dùng (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/vi/admin/settings/post.json b/public/language/vi/admin/settings/post.json
index 1a9a24f454..1db0f903f1 100644
--- a/public/language/vi/admin/settings/post.json
+++ b/public/language/vi/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "Hiển thị tab \"Trợ giúp\"",
 	"composer.enable-plugin-help": "Cho phép các plugin thêm nội dung vào tab trợ giúp",
 	"composer.custom-help": "Văn Bản Trợ Giúp Tùy Chỉnh",
+	"backlinks": "Liên kết ngược",
+	"backlinks.enabled": "Bật liên kết ngược chủ đề",
+	"backlinks.help": "Nếu một bài đăng tham chiếu đến chủ đề khác, một liên kết quay lại bài đăng sẽ được chèn vào chủ đề được tham chiếu tại thời điểm đó.",
 	"ip-tracking": "Theo dõi IP",
 	"ip-tracking.each-post": "Theo dõi Địa chỉ IP mỗi bài đăng",
 	"enable-post-history": "Bật Lịch Sử Bài Đăng"
diff --git a/public/language/vi/modules.json b/public/language/vi/modules.json
index 3fbf9c8f69..86b2e7342e 100644
--- a/public/language/vi/modules.json
+++ b/public/language/vi/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "Gạch ngang",
     "composer.formatting.code": "Mã",
     "composer.formatting.link": "Liên kết",
-    "composer.formatting.picture": "Hình ảnh",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "Tải ảnh lên",
     "composer.upload-file": "Tải Lên Tệp",
     "composer.zen_mode": "Chế Độ Zen",
diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json
index 1b476f4c30..04c1344637 100644
--- a/public/language/vi/topic.json
+++ b/public/language/vi/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Được khôi phục bởi",
     "moved-from-by": "Đã chuyển từ %1 bởi",
     "queued-by": "Bài đăng được xếp hàng chờ duyệt &rarr;",
+    "backlink": "Được giới thiệu bởi",
     "bookmark_instructions": "Nhấn vào đây để trở lại bài viết đã đọc cuối cùng trong chủ đề này.",
     "flag-post": "Gắn cờ bài đăng này",
     "flag-user": "Gắn cờ người dùng này",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "Mới đến cũ",
     "most_votes": "Nhiều Bình Chọn",
     "most_posts": "Nhiều Bài Đăng",
+    "most_views": "Xem Nhiều",
     "stale.title": "Tạo chủ đề mới thay thế?",
     "stale.warning": "Chủ đề bạn đang trả lời đã khá cũ. Bạn có muốn tạo chủ đề mới, và liên kết với chủ đề hiện tại trong bài viết trả lời của bạn?",
     "stale.create": "Tạo chủ đề mới",
diff --git a/public/language/zh-CN/admin/settings/notifications.json b/public/language/zh-CN/admin/settings/notifications.json
index bd4ee2967f..147dc97f23 100644
--- a/public/language/zh-CN/admin/settings/notifications.json
+++ b/public/language/zh-CN/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "通知",
 	"welcome-notification": "欢迎通知",
 	"welcome-notification-link": "欢迎通知链接",
-	"welcome-notification-uid": "用户欢迎通知 (UID)"
+	"welcome-notification-uid": "用户欢迎通知 (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json
index c1b58d8595..7dc59bb94b 100644
--- a/public/language/zh-CN/admin/settings/post.json
+++ b/public/language/zh-CN/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "显示“帮助”选项卡",
 	"composer.enable-plugin-help": "允许插件添加内容到帮助选项卡",
 	"composer.custom-help": "自定义帮助文本",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP 跟踪",
 	"ip-tracking.each-post": "跟踪每个帖子的 IP 地址",
 	"enable-post-history": "启用回复历史"
diff --git a/public/language/zh-CN/modules.json b/public/language/zh-CN/modules.json
index 7f24fbeab9..790e1563e4 100644
--- a/public/language/zh-CN/modules.json
+++ b/public/language/zh-CN/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "删除线",
     "composer.formatting.code": "代码",
     "composer.formatting.link": "链接",
-    "composer.formatting.picture": "图片",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "上传图片",
     "composer.upload-file": "上传文件",
     "composer.zen_mode": "无干扰模式",
diff --git a/public/language/zh-CN/topic.json b/public/language/zh-CN/topic.json
index 6491cd14ba..20188e117a 100644
--- a/public/language/zh-CN/topic.json
+++ b/public/language/zh-CN/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "点击阅读本主题帖中的最新回复",
     "flag-post": "举报此帖",
     "flag-user": "举报此用户",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "从新到旧",
     "most_votes": "最多赞同",
     "most_posts": "回复最多",
+    "most_views": "Most Views",
     "stale.title": "接受建议并创建新主题?",
     "stale.warning": "您回复的主题已经很古老了,是否发布新主题并引用此主题的内容?",
     "stale.create": "创建新主题",
diff --git a/public/language/zh-TW/admin/settings/notifications.json b/public/language/zh-TW/admin/settings/notifications.json
index 34a1ae3d31..991f4e19d6 100644
--- a/public/language/zh-TW/admin/settings/notifications.json
+++ b/public/language/zh-TW/admin/settings/notifications.json
@@ -2,5 +2,6 @@
 	"notifications": "通知",
 	"welcome-notification": "歡迎通知",
 	"welcome-notification-link": "歡迎通知連結",
-	"welcome-notification-uid": "歡迎通知使用者 (UID)"
+	"welcome-notification-uid": "歡迎通知使用者 (UID)",
+	"post-queue-notification-uid": "Post Queue User (UID)"
 }
\ No newline at end of file
diff --git a/public/language/zh-TW/admin/settings/post.json b/public/language/zh-TW/admin/settings/post.json
index eccfaf3af7..607923bae4 100644
--- a/public/language/zh-TW/admin/settings/post.json
+++ b/public/language/zh-TW/admin/settings/post.json
@@ -56,6 +56,9 @@
 	"composer.show-help": "顯示“幫助”選項卡",
 	"composer.enable-plugin-help": "允許外掛添加內容到幫助選項卡",
 	"composer.custom-help": "自訂幫助文字",
+	"backlinks": "Backlinks",
+	"backlinks.enabled": "Enable topic backlinks",
+	"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
 	"ip-tracking": "IP 跟蹤",
 	"ip-tracking.each-post": "跟蹤每個貼文的 IP 地址",
 	"enable-post-history": "啟用回覆歷史"
diff --git a/public/language/zh-TW/modules.json b/public/language/zh-TW/modules.json
index 3049796eab..0c8ac551ba 100644
--- a/public/language/zh-TW/modules.json
+++ b/public/language/zh-TW/modules.json
@@ -54,7 +54,7 @@
     "composer.formatting.strikethrough": "刪除線",
     "composer.formatting.code": "程式碼",
     "composer.formatting.link": "連結",
-    "composer.formatting.picture": "圖片",
+    "composer.formatting.picture": "Image Link",
     "composer.upload-picture": "上傳圖片",
     "composer.upload-file": "上傳檔案",
     "composer.zen_mode": "無干擾模式",
diff --git a/public/language/zh-TW/topic.json b/public/language/zh-TW/topic.json
index 1b7844dca8..4bfd341313 100644
--- a/public/language/zh-TW/topic.json
+++ b/public/language/zh-TW/topic.json
@@ -47,6 +47,7 @@
     "restored-by": "Restored by",
     "moved-from-by": "Moved from %1 by",
     "queued-by": "Post queued for approval &rarr;",
+    "backlink": "Referenced by",
     "bookmark_instructions": "點擊閱讀本主題貼文中的最新回覆",
     "flag-post": "Flag this post",
     "flag-user": "Flag this user",
@@ -158,6 +159,7 @@
     "newest_to_oldest": "從新到舊",
     "most_votes": "最多點贊",
     "most_posts": "回覆最多",
+    "most_views": "Most Views",
     "stale.title": "接受建議並建立新主題?",
     "stale.warning": "您回覆的主題已經很古老了,是否發佈新主題並引用此主題的內容?",
     "stale.create": "建立新主題",
diff --git a/public/less/admin/extend/rewards.less b/public/less/admin/extend/rewards.less
index 3972b16de3..e90eb3e717 100644
--- a/public/less/admin/extend/rewards.less
+++ b/public/less/admin/extend/rewards.less
@@ -31,12 +31,15 @@
 
 		&.if-block {
 			border-color: @brand-primary;
+			max-width: 33%;
 		}
 		&.this-block {
 			border-color: @brand-warning;
+			max-width: 33%;
 		}
 		&.then-block {
 			border-color: @brand-success;
+			max-width: 33%;
 		}
 		&.reward-block {
 			border-color: @brand-success;
diff --git a/public/less/admin/header.less b/public/less/admin/header.less
index 709dbc378f..585ac2fe37 100644
--- a/public/less/admin/header.less
+++ b/public/less/admin/header.less
@@ -102,6 +102,10 @@
 			border-bottom: 4px solid transparent;
 			transition: border-color 150ms linear;
 
+			&:hover {
+				border-color: darken(@brand-primary, 20%);
+			}
+
 			&.active {
 				border-color: @brand-primary;
 			}
diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js
index 7df62a7e15..93f6227ccd 100644
--- a/public/src/admin/extend/rewards.js
+++ b/public/src/admin/extend/rewards.js
@@ -167,11 +167,17 @@ define('admin/extend/rewards', [], function () {
 			activeRewards.push(data);
 		});
 
-		socket.emit('admin.rewards.save', activeRewards, function (err) {
+		socket.emit('admin.rewards.save', activeRewards, function (err, result) {
 			if (err) {
 				app.alertError(err.message);
 			} else {
 				app.alertSuccess('[[admin/extend/rewards:alert.save-success]]');
+				// newly added rewards are missing data-id, update to prevent rewards getting duplicated
+				$('#active li').each(function (index) {
+					if (!$(this).attr('data-id')) {
+						$(this).attr('data-id', result[index].id);
+					}
+				});
 			}
 		});
 	}
diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js
index 96fd8b63fc..8a812e8897 100644
--- a/public/src/admin/manage/groups.js
+++ b/public/src/admin/manage/groups.js
@@ -7,8 +7,6 @@ define('admin/manage/groups', [
 ], function (categorySelector, slugify, api) {
 	var	Groups = {};
 
-	var intervalId = 0;
-
 	Groups.init = function () {
 		var	createModal = $('#create-modal');
 		var createGroupName = $('#create-group-name');
@@ -114,13 +112,7 @@ define('admin/manage/groups', [
 			});
 		}
 
-		queryEl.on('keyup', function () {
-			if (intervalId) {
-				clearTimeout(intervalId);
-				intervalId = 0;
-			}
-			intervalId = setTimeout(doSearch, 200);
-		});
+		queryEl.on('keyup', utils.debounce(doSearch, 200));
 	}
 
 
diff --git a/public/src/admin/manage/tags.js b/public/src/admin/manage/tags.js
index 6cb4ad96f6..0fb7dbf39f 100644
--- a/public/src/admin/manage/tags.js
+++ b/public/src/admin/manage/tags.js
@@ -6,7 +6,6 @@ define('admin/manage/tags', [
 	'admin/modules/selectable',
 ], function (infinitescroll, selectable) {
 	var	Tags = {};
-	var timeoutId = 0;
 
 	Tags.init = function () {
 		selectable.enable('.tag-management', '.tag-row');
@@ -53,32 +52,23 @@ define('admin/manage/tags', [
 	}
 
 	function handleSearch() {
-		$('#tag-search').on('input propertychange', function () {
-			if (timeoutId) {
-				clearTimeout(timeoutId);
-				timeoutId = 0;
-			}
-
-			timeoutId = setTimeout(function () {
-				socket.emit('topics.searchAndLoadTags', {
-					query: $('#tag-search').val(),
-				}, function (err, result) {
-					if (err) {
-						return app.alertError(err.message);
-					}
-
-					app.parseAndTranslate('admin/manage/tags', 'tags', {
-						tags: result.tags,
-					}, function (html) {
-						$('.tag-list').html(html);
-						utils.makeNumbersHumanReadable(html.find('.human-readable-number'));
-						timeoutId = 0;
+		$('#tag-search').on('input propertychange', utils.debounce(function () {
+			socket.emit('topics.searchAndLoadTags', {
+				query: $('#tag-search').val(),
+			}, function (err, result) {
+				if (err) {
+					return app.alertError(err.message);
+				}
 
-						selectable.enable('.tag-management', '.tag-row');
-					});
+				app.parseAndTranslate('admin/manage/tags', 'tags', {
+					tags: result.tags,
+				}, function (html) {
+					$('.tag-list').html(html);
+					utils.makeNumbersHumanReadable(html.find('.human-readable-number'));
+					selectable.enable('.tag-management', '.tag-row');
 				});
-			}, 250);
-		});
+			});
+		}, 250));
 	}
 
 	function handleRename() {
diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js
index 5675f5b0aa..41d9a1a86d 100644
--- a/public/src/admin/manage/users.js
+++ b/public/src/admin/manage/users.js
@@ -412,7 +412,6 @@ define('admin/manage/users', [
 	};
 
 	function handleSearch() {
-		var timeoutId = 0;
 		function doSearch() {
 			$('.fa-spinner').removeClass('hidden');
 			loadSearchPage({
@@ -421,16 +420,8 @@ define('admin/manage/users', [
 				page: 1,
 			});
 		}
-		$('#user-search').on('keyup', function () {
-			if (timeoutId !== 0) {
-				clearTimeout(timeoutId);
-				timeoutId = 0;
-			}
-			timeoutId = setTimeout(doSearch, 250);
-		});
-		$('#user-search-by').on('change', function () {
-			doSearch();
-		});
+		$('#user-search').on('keyup', utils.debounce(doSearch, 250));
+		$('#user-search-by').on('change', doSearch);
 	}
 
 	function loadSearchPage(query) {
diff --git a/public/src/app.js b/public/src/app.js
index 694fed6dd5..a4c97f5f9a 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -485,7 +485,6 @@ app.cacheBuster = null;
 		var searchOptions = Object.assign({ in: config.searchDefaultInQuick || 'titles' }, options.searchOptions);
 		var quickSearchResults = options.searchElements.resultEl;
 		var inputEl = options.searchElements.inputEl;
-		var searchTimeoutId = 0;
 		var oldValue = inputEl.val();
 		var filterCategoryEl = quickSearchResults.find('.filter-category');
 
@@ -556,27 +555,21 @@ app.cacheBuster = null;
 			doSearch();
 		});
 
-		inputEl.off('keyup').on('keyup', function () {
-			if (searchTimeoutId) {
-				clearTimeout(searchTimeoutId);
-				searchTimeoutId = 0;
-			}
-			searchTimeoutId = setTimeout(function () {
-				if (inputEl.val().length < 3) {
-					quickSearchResults.addClass('hidden');
-					oldValue = inputEl.val();
-					return;
-				}
-				if (inputEl.val() === oldValue) {
-					return;
-				}
+		inputEl.off('keyup').on('keyup', utils.debounce(function () {
+			if (inputEl.val().length < 3) {
+				quickSearchResults.addClass('hidden');
 				oldValue = inputEl.val();
-				if (!inputEl.is(':focus')) {
-					return quickSearchResults.addClass('hidden');
-				}
-				doSearch();
-			}, 250);
-		});
+				return;
+			}
+			if (inputEl.val() === oldValue) {
+				return;
+			}
+			oldValue = inputEl.val();
+			if (!inputEl.is(':focus')) {
+				return quickSearchResults.addClass('hidden');
+			}
+			doSearch();
+		}, 250));
 
 		var mousedownOnResults = false;
 		quickSearchResults.on('mousedown', function () {
diff --git a/public/src/client/chats.js b/public/src/client/chats.js
index 0389834e76..de1f74763c 100644
--- a/public/src/client/chats.js
+++ b/public/src/client/chats.js
@@ -30,6 +30,7 @@ define('forum/chats', [
 
 		Chats.addEventListeners();
 		Chats.resizeMainWindow();
+		Chats.setActive();
 
 		if (env === 'md' || env === 'lg') {
 			Chats.addHotkeys();
@@ -452,7 +453,7 @@ define('forum/chats', [
 	};
 
 	Chats.addGlobalEventListeners = function () {
-		$(window).on('resize', Chats.resizeMainWindow);
+		$(window).on('resize', utils.throttle(Chats.resizeMainWindow, 100));
 		$(window).on('mousemove keypress click', function () {
 			if (newMessage && ajaxify.data.roomId) {
 				socket.emit('modules.chats.markRead', ajaxify.data.roomId);
@@ -514,8 +515,6 @@ define('forum/chats', [
 		}
 
 		$('.chats-full').height(viewportHeight - fromTop - 1);
-
-		Chats.setActive();
 	};
 
 	Chats.setActive = function () {
diff --git a/public/src/client/chats/search.js b/public/src/client/chats/search.js
index 3103a22fab..0c83dd101f 100644
--- a/public/src/client/chats/search.js
+++ b/public/src/client/chats/search.js
@@ -5,16 +5,7 @@ define('forum/chats/search', ['components', 'api'], function (components, api) {
 	var search = {};
 
 	search.init = function () {
-		var timeoutId = 0;
-
-		components.get('chat/search').on('keyup', function () {
-			if (timeoutId) {
-				clearTimeout(timeoutId);
-				timeoutId = 0;
-			}
-
-			timeoutId = setTimeout(doSearch, 250);
-		});
+		components.get('chat/search').on('keyup', utils.debounce(doSearch, 250));
 	};
 
 	function doSearch() {
diff --git a/public/src/client/login.js b/public/src/client/login.js
index 204dd799e2..b839c00efb 100644
--- a/public/src/client/login.js
+++ b/public/src/client/login.js
@@ -39,6 +39,7 @@ define('forum/login', ['hooks', 'translator', 'jquery-form'], function (hooks, t
 						var pathname = utils.urlToLocation(data.next).pathname;
 						var params = utils.params({ url: data.next });
 						params.loggedin = true;
+						delete params.register; // clear register message incase it exists
 						var qs = decodeURIComponent($.param(params));
 
 						window.location.href = pathname + '?' + qs;
diff --git a/public/src/client/tag.js b/public/src/client/tag.js
index a23690ba68..ce45a94aae 100644
--- a/public/src/client/tag.js
+++ b/public/src/client/tag.js
@@ -1,22 +1,12 @@
 'use strict';
 
-
-define('forum/tag', ['topicList', 'forum/infinitescroll'], function (topicList, infinitescroll) {
+define('forum/tag', ['topicList', 'forum/infinitescroll'], function (topicList) {
 	var Tag = {};
 
 	Tag.init = function () {
 		app.enterRoom('tags');
 
-		topicList.init('tag', loadMoreTopics);
-
-		function loadMoreTopics(after, direction, callback) {
-			infinitescroll.loadMore('topics.loadMoreFromSet', {
-				set: 'tag:' + ajaxify.data.tag + ':topics',
-				after: after,
-				direction: direction,
-				count: config.topicsPerPage,
-			}, callback);
-		}
+		topicList.init('tag');
 	};
 
 	return Tag;
diff --git a/public/src/client/tags.js b/public/src/client/tags.js
index cb096401de..bde065269e 100644
--- a/public/src/client/tags.js
+++ b/public/src/client/tags.js
@@ -3,32 +3,22 @@
 
 define('forum/tags', ['forum/infinitescroll'], function (infinitescroll) {
 	var Tags = {};
-	var timeoutId = 0;
 
 	Tags.init = function () {
 		app.enterRoom('tags');
 		$('#tag-search').focus();
-		$('#tag-search').on('input propertychange', function () {
-			if (timeoutId) {
-				clearTimeout(timeoutId);
-				timeoutId = 0;
-			}
-
+		$('#tag-search').on('input propertychange', utils.debounce(function () {
 			if (!$('#tag-search').val().length) {
 				return resetSearch();
 			}
 
-			timeoutId = setTimeout(function () {
-				socket.emit('topics.searchAndLoadTags', { query: $('#tag-search').val() }, function (err, results) {
-					if (err) {
-						return app.alertError(err.message);
-					}
-					onTagsLoaded(results.tags, true, function () {
-						timeoutId = 0;
-					});
-				});
-			}, 250);
-		});
+			socket.emit('topics.searchAndLoadTags', { query: $('#tag-search').val() }, function (err, results) {
+				if (err) {
+					return app.alertError(err.message);
+				}
+				onTagsLoaded(results.tags, true);
+			});
+		}, 250));
 
 		infinitescroll.init(Tags.loadMoreTags);
 	};
diff --git a/public/src/client/users.js b/public/src/client/users.js
index 50ecab2ac1..794cf04e90 100644
--- a/public/src/client/users.js
+++ b/public/src/client/users.js
@@ -6,16 +6,8 @@ define('forum/users', [
 ], function (translator, Benchpress, api, AccountInvite) {
 	var	Users = {};
 
-	var searchTimeoutID = 0;
 	var searchResultCount = 0;
 
-	$(window).on('action:ajaxify.start', function () {
-		if (searchTimeoutID) {
-			clearTimeout(searchTimeoutID);
-			searchTimeoutID = 0;
-		}
-	});
-
 	Users.init = function () {
 		app.enterRoom('user_list');
 
@@ -33,23 +25,14 @@ define('forum/users', [
 
 	Users.handleSearch = function (params) {
 		searchResultCount = params && params.resultCount;
-		searchTimeoutID = 0;
-
-		$('#search-user').on('keyup', function () {
-			if (searchTimeoutID) {
-				clearTimeout(searchTimeoutID);
-				searchTimeoutID = 0;
-			}
-
-			searchTimeoutID = setTimeout(doSearch, 250);
-		});
-
-		$('.search select, .search input[type="checkbox"]').on('change', function () {
-			doSearch();
-		});
+		$('#search-user').on('keyup', utils.debounce(doSearch, 250));
+		$('.search select, .search input[type="checkbox"]').on('change', doSearch);
 	};
 
 	function doSearch() {
+		if (!ajaxify.data.template.users) {
+			return;
+		}
 		$('[component="user/search/icon"]').removeClass('fa-search').addClass('fa-spinner fa-spin');
 		var username = $('#search-user').val();
 		var activeSection = getActiveSection();
diff --git a/public/src/modules/categorySelector.js b/public/src/modules/categorySelector.js
index f7de882276..0ac9da7229 100644
--- a/public/src/modules/categorySelector.js
+++ b/public/src/modules/categorySelector.js
@@ -30,7 +30,7 @@ define('categorySelector', [
 			selector.selectCategory(categoryEl.attr('data-cid'));
 			onSelect(selector.selectedCategory);
 		});
-
+		const defaultSelectHtml = selector.el.find('[component="category-selector-selected"]').html();
 		selector.selectCategory = function (cid) {
 			var categoryEl = selector.el.find('[data-cid="' + cid + '"]');
 			selector.selectedCategory = {
@@ -43,8 +43,8 @@ define('categorySelector', [
 					categoryEl.find('[component="category-markup"]').html()
 				);
 			} else {
-				selector.el.find('[component="category-selector-selected"]').translateHtml(
-					options.selectCategoryLabel || '[[topic:thread_tools.select_category]]'
+				selector.el.find('[component="category-selector-selected"]').html(
+					defaultSelectHtml
 				);
 			}
 		};
diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js
index 33cc8a6323..d34266b093 100644
--- a/public/src/modules/helpers.js
+++ b/public/src/modules/helpers.js
@@ -70,10 +70,10 @@
 	}
 
 	function buildLinkTag(tag) {
-		const attributes = ['link', 'rel', 'as', 'type', 'href', 'sizes', 'title'];
-		const [link, rel, as, type, href, sizes, title] = attributes.map(attr => (tag[attr] ? `${attr}="${tag[attr]}" ` : ''));
+		const attributes = ['link', 'rel', 'as', 'type', 'href', 'sizes', 'title', 'crossorigin'];
+		const [link, rel, as, type, href, sizes, title, crossorigin] = attributes.map(attr => (tag[attr] ? `${attr}="${tag[attr]}" ` : ''));
 
-		return '<link ' + link + rel + as + type + sizes + title + href + '/>\n\t';
+		return '<link ' + link + rel + as + type + sizes + title + href + crossorigin + '/>\n\t';
 	}
 
 	function stringify(obj) {
@@ -228,7 +228,7 @@
 					<i class="fa ${event.icon || 'fa-circle'}"></i>
 				</div>
 				<span class="timeline-text">
-					${event.href ? `<a href="${relative_path}${event.href}>${event.text}</a>` : event.text}&nbsp;
+					${event.href ? `<a href="${relative_path}${event.href}">${event.text}</a>` : event.text}&nbsp;
 				</span>
 			`;
 
diff --git a/public/src/modules/topicList.js b/public/src/modules/topicList.js
index b85e04aef9..6ff9e33c24 100644
--- a/public/src/modules/topicList.js
+++ b/public/src/modules/topicList.js
@@ -11,13 +11,6 @@ define('topicList', [
 	var TopicList = {};
 	var templateName = '';
 
-	var tplToSort = {
-		recent: 'recent',
-		unread: 'unread',
-		popular: 'posts',
-		top: 'votes',
-	};
-
 	var newTopicCount = 0;
 	var newPostCount = 0;
 
@@ -197,21 +190,15 @@ define('topicList', [
 		});
 	};
 
+	function calculateNextPage(after, direction) {
+		return Math.floor(after / config.topicsPerPage) + (direction > 0 ? 1 : 0);
+	}
+
 	function loadTopicsAfter(after, direction, callback) {
 		callback = callback || function () {};
 		var query = utils.params();
-		infinitescroll.loadMore('topics.loadMoreSortedTopics', {
-			after: after,
-			direction: direction,
-			sort: tplToSort[templateName],
-			count: config.topicsPerPage,
-			cid: query.cid,
-			tags: query.tags,
-			query: query,
-			term: ajaxify.data.selectedTerm && ajaxify.data.selectedTerm.term,
-			filter: ajaxify.data.selectedFilter.filter,
-			set: topicListEl.attr('data-set') ? topicListEl.attr('data-set') : 'topics:recent',
-		}, callback);
+		query.page = calculateNextPage(after, direction);
+		infinitescroll.loadMoreXhr(query, callback);
 	}
 
 	function filterTopicsOnDom(topics) {
diff --git a/public/src/utils.js b/public/src/utils.js
index fa9864ac5c..92317a0154 100644
--- a/public/src/utils.js
+++ b/public/src/utils.js
@@ -771,6 +771,26 @@
 				}
 			};
 		},
+		throttle: function (func, wait, immediate) {
+			var timeout;
+			return function () {
+				var context = this;
+				var args = arguments;
+				var later = function () {
+					timeout = null;
+					if (!immediate) {
+						func.apply(context, args);
+					}
+				};
+				var callNow = immediate && !timeout;
+				if (!timeout) {
+					timeout = setTimeout(later, wait);
+				}
+				if (callNow) {
+					func.apply(context, args);
+				}
+			};
+		},
 	};
 
 	return utils;
diff --git a/src/categories/delete.js b/src/categories/delete.js
index 539ce20b19..ff491a5dbc 100644
--- a/src/categories/delete.js
+++ b/src/categories/delete.js
@@ -39,6 +39,7 @@ module.exports = function (Categories) {
 			`cid:${cid}:tids:pinned`,
 			`cid:${cid}:tids:posts`,
 			`cid:${cid}:tids:votes`,
+			`cid:${cid}:tids:views`,
 			`cid:${cid}:tids:lastposttime`,
 			`cid:${cid}:recent_tids`,
 			`cid:${cid}:pids`,
diff --git a/src/categories/topics.js b/src/categories/topics.js
index 381776738d..fdba6ab59d 100644
--- a/src/categories/topics.js
+++ b/src/categories/topics.js
@@ -100,6 +100,8 @@ module.exports = function (Categories) {
 			set = `cid:${cid}:tids:posts`;
 		} else if (sort === 'most_votes') {
 			set = `cid:${cid}:tids:votes`;
+		} else if (sort === 'most_views') {
+			set = `cid:${cid}:tids:views`;
 		}
 
 		if (data.tag) {
@@ -123,7 +125,7 @@ module.exports = function (Categories) {
 
 	Categories.getSortedSetRangeDirection = async function (sort) {
 		sort = sort || 'newest_to_oldest';
-		const direction = sort === 'newest_to_oldest' || sort === 'most_posts' || sort === 'most_votes' ? 'highest-to-lowest' : 'lowest-to-highest';
+		const direction = ['newest_to_oldest', 'most_posts', 'most_votes', 'most_views'].includes(sort) ? 'highest-to-lowest' : 'lowest-to-highest';
 		const result = await plugins.hooks.fire('filter:categories.getSortedSetRangeDirection', {
 			sort: sort,
 			direction: direction,
diff --git a/src/cli/colors.js b/src/cli/colors.js
index 35f30376ae..4ba0012a40 100644
--- a/src/cli/colors.js
+++ b/src/cli/colors.js
@@ -8,17 +8,13 @@ const { Command, Help } = require('commander');
 
 const colors = [
 	// depth = 0, top-level command
-	{
-		command: 'yellow',
-		option: 'cyan',
-		arg: 'magenta',
-	},
+	{ command: 'yellow', option: 'cyan', arg: 'magenta' },
 	// depth = 1, second-level commands
-	{
-		command: 'green',
-		option: 'blue',
-		arg: 'red',
-	},
+	{ command: 'green', option: 'blue', arg: 'red' },
+	// depth = 2, third-level commands
+	{ command: 'yellow', option: 'cyan', arg: 'magenta' },
+	// depth = 3 fourth-level commands
+	{ command: 'green', option: 'blue', arg: 'red' },
 ];
 
 function humanReadableArgName(arg) {
diff --git a/src/cli/index.js b/src/cli/index.js
index e69910cf1f..b7ef494f36 100644
--- a/src/cli/index.js
+++ b/src/cli/index.js
@@ -253,6 +253,10 @@ resetCommand
 		});
 	});
 
+// user
+program
+	.addCommand(require('./user')());
+
 // upgrades
 program
 	.command('upgrade [scripts...]')
diff --git a/src/cli/user.js b/src/cli/user.js
new file mode 100644
index 0000000000..e02622e263
--- /dev/null
+++ b/src/cli/user.js
@@ -0,0 +1,310 @@
+'use strict';
+
+const { Command, Option } = require('commander');
+
+module.exports = () => {
+	const userCmd = new Command('user')
+		.description('Manage users')
+		.arguments('[command]');
+
+	userCmd.configureHelp(require('./colors'));
+	const userCommands = UserCommands();
+
+	userCmd
+		.command('info')
+		.description('Display user info by uid/username/userslug.')
+		.option('-i, --uid <uid>', 'Retrieve user by uid')
+		.option('-u, --username <username>', 'Retrieve user by username')
+		.option('-s, --userslug <userslug>', 'Retrieve user by userslug')
+		.action((...args) => execute(userCommands.info, args));
+	userCmd
+		.command('create')
+		.description('Create a new user.')
+		.arguments('<username>')
+		.option('-p, --password <password>', 'Set a new password. (Auto-generates if omitted)')
+		.option('-e, --email <email>', 'Associate with an email.')
+		.action((...args) => execute(userCommands.create, args));
+	userCmd
+		.command('reset')
+		.description('Reset a user\'s password or send a password reset email.')
+		.arguments('<uid>')
+		.option('-p, --password <password>', 'Set a new password. (Auto-generates if passed empty)', false)
+		.option('-s, --send-reset-email', 'Send a password reset email.', false)
+		.action((...args) => execute(userCommands.reset, args));
+	userCmd
+		.command('delete')
+		.description('Delete user(s) and/or their content')
+		.arguments('<uids...>')
+		.addOption(
+			new Option('-c, --content [operation]', 'Delete user content ([purge]), leave content ([account]), or delete content only ([content])')
+				.choices(['purge', 'account', 'content']).default('purge')
+		)
+		.action((...args) => execute(userCommands.deleteUser, args));
+
+	const make = userCmd.command('make')
+		.description('Make user(s) admin, global mod, moderator or a regular user.')
+		.arguments('[command]');
+
+	make.command('admin')
+		.description('Make user(s) an admin')
+		.arguments('<uids...>')
+		.action((...args) => execute(userCommands.makeAdmin, args));
+	make.command('global-mod')
+		.description('Make user(s) a global moderator')
+		.arguments('<uids...>')
+		.action((...args) => execute(userCommands.makeGlobalMod, args));
+	make.command('mod')
+		.description('Make uid(s) of user(s) moderator of given category IDs (cids)')
+		.arguments('<uids...>')
+		.requiredOption('-c, --cid <cids...>', 'ID(s) of categories to make the user a moderator of')
+		.action((...args) => execute(userCommands.makeMod, args));
+	make.command('regular')
+		.description('Make user(s) a non-privileged user')
+		.arguments('<uids...>')
+		.action((...args) => execute(userCommands.makeRegular, args));
+
+	return userCmd;
+};
+
+let db;
+let user;
+let groups;
+let privileges;
+let privHelpers;
+let utils;
+let winston;
+
+async function init() {
+	db = require('../database');
+	await db.init();
+
+	user = require('../user');
+	groups = require('../groups');
+	privileges = require('../privileges');
+	privHelpers = require('../privileges/helpers');
+	utils = require('../utils');
+	winston = require('winston');
+}
+
+async function execute(cmd, args) {
+	await init();
+	try {
+		await cmd(...args);
+	} catch (err) {
+		const userError = err.name === 'UserError';
+		winston.error(`[userCmd/${cmd.name}] ${userError ? `${err.message}` : 'Command failed.'}`, userError ? '' : err);
+		process.exit(1);
+	}
+
+	process.exit();
+}
+
+function UserCmdHelpers() {
+	async function getAdminUidOrFail() {
+		const adminUid = (await db.getSortedSetMembers('group:administrators:members')).reverse()[0];
+		if (!adminUid) {
+			const err = new Error('An admin account does not exists to execute the operation.');
+			err.name = 'UserError';
+			throw err;
+		}
+		return adminUid;
+	}
+
+	async function setupApp() {
+		const nconf = require('nconf');
+		const Benchpress = require('benchpressjs');
+
+		const meta = require('../meta');
+		await meta.configs.init();
+
+		const webserver = require('../webserver');
+		const viewsDir = nconf.get('views_dir');
+
+		webserver.app.engine('tpl', (filepath, data, next) => {
+			filepath = filepath.replace(/\.tpl$/, '.js');
+
+			Benchpress.__express(filepath, data, next);
+		});
+		webserver.app.set('view engine', 'tpl');
+		webserver.app.set('views', viewsDir);
+
+		const emailer = require('../emailer');
+		emailer.registerApp(webserver.app);
+	}
+
+	const argParsers = {
+		intParse: (value, varName) => {
+			const parsedValue = parseInt(value, 10);
+			if (isNaN(parsedValue)) {
+				const err = new Error(`"${varName}" expected to be a number.`);
+				err.name = 'UserError';
+				throw err;
+			}
+			return parsedValue;
+		},
+		intArrayParse: (values, varName) => values.map(value => argParsers.intParse(value, varName)),
+	};
+
+	return {
+		argParsers,
+		getAdminUidOrFail,
+		setupApp,
+	};
+}
+
+function UserCommands() {
+	const { argParsers, getAdminUidOrFail, setupApp } = UserCmdHelpers();
+
+	async function info({ uid, username, userslug }) {
+		if (!uid && !username && !userslug) {
+			return winston.error('[userCmd/info] At least one option has to be passed (--uid, --username or --userslug).');
+		}
+
+		if (uid) {
+			uid = argParsers.intParse(uid, 'uid');
+		} else if (username) {
+			uid = await user.getUidByUsername(username);
+		} else {
+			uid = await user.getUidByUserslug(userslug);
+		}
+
+		const userData = await user.getUserData(uid);
+		winston.info('[userCmd/info] User info retrieved:');
+		console.log(userData);
+	}
+
+	async function create(username, { password, email }) {
+		let pwGenerated = false;
+		if (password === undefined) {
+			password = utils.generateUUID().slice(0, 8);
+			pwGenerated = true;
+		}
+
+		const userExists = await user.getUidByUsername(username);
+		if (userExists) {
+			return winston.error(`[userCmd/create] A user with username '${username}' already exists`);
+		}
+
+		const uid = await user.create({
+			username,
+			password,
+			email,
+		});
+
+		winston.info(`[userCmd/create] User '${username}'${password ? '' : ' without a password'} has been created with uid: ${uid}.\
+${pwGenerated ? ` Generated password: ${password}` : ''}`);
+	}
+
+	async function reset(uid, { password, sendResetEmail }) {
+		uid = argParsers.intParse(uid, 'uid');
+
+		if (password === false && sendResetEmail === false) {
+			return winston.error('[userCmd/reset] At least one option has to be passed (--password or --send-reset-email).');
+		}
+
+		const userExists = await user.exists(uid);
+		if (!userExists) {
+			return winston.error(`[userCmd/reset] A user with given uid does not exists.`);
+		}
+
+		let pwGenerated = false;
+		if (password === '') {
+			password = utils.generateUUID().slice(0, 8);
+			pwGenerated = true;
+		}
+
+		const adminUid = await getAdminUidOrFail();
+
+		if (password) {
+			await user.changePassword(adminUid, {
+				newPassword: password,
+				uid,
+			});
+			winston.info(`[userCmd/reset] ${password ? 'User password changed.' : ''}${pwGenerated ? ` Generated password: ${password}` : ''}`);
+		}
+
+		if (sendResetEmail) {
+			const userEmail = await user.getUserField(uid, 'email');
+			if (!userEmail) {
+				return winston.error('User doesn\'t have an email address to send reset email.');
+			}
+			await setupApp();
+			await user.reset.send(userEmail);
+			winston.info('[userCmd/reset] Password reset email has been sent.');
+		}
+	}
+
+	async function deleteUser(uids, { content }) {
+		uids = argParsers.intArrayParse(uids, 'uids');
+
+		const userExists = await user.exists(uids);
+		if (!userExists || userExists.some(r => r === false)) {
+			return winston.error(`[userCmd/reset] A user with given uid does not exists.`);
+		}
+
+		await db.initSessionStore();
+		const adminUid = await getAdminUidOrFail();
+
+		switch (content) {
+			case 'purge':
+				await Promise.all(uids.map(uid => user.delete(adminUid, uid)));
+				winston.info(`[userCmd/delete] User(s) with their content has been deleted.`);
+				break;
+			case 'account':
+				await Promise.all(uids.map(uid => user.deleteAccount(uid)));
+				winston.info(`[userCmd/delete] User(s) has been deleted, their content left intact.`);
+				break;
+			case 'content':
+				await Promise.all(uids.map(uid => user.deleteContent(adminUid, uid)));
+				winston.info(`[userCmd/delete] User(s)' content has been deleted.`);
+				break;
+		}
+	}
+
+	async function makeAdmin(uids) {
+		uids = argParsers.intArrayParse(uids, 'uids');
+		await Promise.all(uids.map(uid => groups.join('administrators', uid)));
+
+		winston.info('[userCmd/make/admin] User(s) added as administrators.');
+	}
+
+	async function makeGlobalMod(uids) {
+		uids = argParsers.intArrayParse(uids, 'uids');
+		await Promise.all(uids.map(uid => groups.join('Global Moderators', uid)));
+
+		winston.info('[userCmd/make/globalMod] User(s) added as global moderators.');
+	}
+
+	async function makeMod(uids, { cid: cids }) {
+		uids = argParsers.intArrayParse(uids, 'uids');
+		cids = argParsers.intArrayParse(cids, 'cids');
+
+		const categoryPrivList = await privileges.categories.getPrivilegeList();
+		await privHelpers.giveOrRescind(groups.join, categoryPrivList, cids, uids);
+
+		winston.info('[userCmd/make/mod] User(s) added as moderators to given categories.');
+	}
+
+	async function makeRegular(uids) {
+		uids = argParsers.intArrayParse(uids, 'uids');
+
+		await Promise.all(uids.map(uid => groups.leave(['administrators', 'Global Moderators'], uid)));
+
+		const categoryPrivList = await privileges.categories.getPrivilegeList();
+		const cids = await db.getSortedSetRevRange('categories:cid', 0, -1);
+		await privHelpers.giveOrRescind(groups.leave, categoryPrivList, cids, uids);
+
+		winston.info('[userCmd/make/regular] User(s) made regular/non-privileged.');
+	}
+
+	return {
+		info,
+		create,
+		reset,
+		deleteUser,
+		makeAdmin,
+		makeGlobalMod,
+		makeMod,
+		makeRegular,
+	};
+}
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index d1a3173417..d0c066056d 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -128,7 +128,7 @@ async function addToApprovalQueue(req, userData) {
 	if (meta.config.showAverageApprovalTime) {
 		const average_time = await db.getObjectField('registration:queue:approval:times', 'average');
 		if (average_time > 0) {
-			message += ` [[register:registration-queue-average-time, ${Math.floor(average_time / 60)}, ${average_time % 60}]]`;
+			message += ` [[register:registration-queue-average-time, ${Math.floor(average_time / 60)}, ${Math.floor(average_time % 60)}]]`;
 		}
 	}
 	if (meta.config.autoApproveTime > 0) {
@@ -164,15 +164,15 @@ authenticationController.registerComplete = async function (req, res) {
 
 		const done = function (data) {
 			delete req.session.registration;
-
+			const relative_path = nconf.get('relative_path');
 			if (data && data.message) {
-				return res.redirect(`${nconf.get('relative_path')}/?register=${encodeURIComponent(data.message)}`);
+				return res.redirect(`${relative_path}/?register=${encodeURIComponent(data.message)}`);
 			}
 
 			if (req.session.returnTo) {
-				res.redirect(nconf.get('relative_path') + req.session.returnTo);
+				res.redirect(relative_path + req.session.returnTo.replace(new RegExp(`^${relative_path}`), ''));
 			} else {
-				res.redirect(`${nconf.get('relative_path')}/`);
+				res.redirect(`${relative_path}/`);
 			}
 		};
 
@@ -220,7 +220,7 @@ authenticationController.registerAbort = function (req, res) {
 	if (req.uid) {
 		// Clear interstitial data and continue on...
 		delete req.session.registration;
-		res.redirect(nconf.get('relative_path') + req.session.returnTo);
+		res.redirect(nconf.get('relative_path') + (req.session.returnTo || '/'));
 	} else {
 		// End the session and redirect to home
 		req.session.destroy(() => {
diff --git a/src/controllers/unread.js b/src/controllers/unread.js
index 55d0d7c008..26a8c5675c 100644
--- a/src/controllers/unread.js
+++ b/src/controllers/unread.js
@@ -49,6 +49,7 @@ unreadController.get = async function (req, res) {
 	data.selectedCategory = categoryData.selectedCategory;
 	data.selectedCids = categoryData.selectedCids;
 	data.selectCategoryLabel = '[[unread:mark_as_read]]';
+	data.selectCategoryIcon = 'fa-inbox';
 	if (req.originalUrl.startsWith(`${nconf.get('relative_path')}/api/unread`) || req.originalUrl.startsWith(`${nconf.get('relative_path')}/unread`)) {
 		data.title = '[[pages:unread]]';
 		data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[unread:title]]' }]);
diff --git a/src/database/mongo/connection.js b/src/database/mongo/connection.js
index c7c2b484e7..2f5d64e714 100644
--- a/src/database/mongo/connection.js
+++ b/src/database/mongo/connection.js
@@ -44,10 +44,9 @@ connection.getConnectionString = function (mongo) {
 connection.getConnectionOptions = function (mongo) {
 	mongo = mongo || nconf.get('mongo');
 	const connOptions = {
-		poolSize: 10,
+		maxPoolSize: 10,
+		minPoolSize: 3,
 		connectTimeoutMS: 90000,
-		useNewUrlParser: true,
-		useUnifiedTopology: true,
 	};
 
 	return _.merge(connOptions, mongo.options || {});
diff --git a/src/database/postgres/hash.js b/src/database/postgres/hash.js
index 04b38713c2..015154aa42 100644
--- a/src/database/postgres/hash.js
+++ b/src/database/postgres/hash.js
@@ -16,7 +16,20 @@ module.exports = function (module) {
 		}
 		await module.transaction(async (client) => {
 			const dataString = JSON.stringify(data);
-			async function setOne(key) {
+
+			if (Array.isArray(key)) {
+				await helpers.ensureLegacyObjectsType(client, key, 'hash');
+				await client.query({
+					name: 'setObjectKeys',
+					text: `
+	INSERT INTO "legacy_hash" ("_key", "data")
+	SELECT k, $2::TEXT::JSONB
+	FROM UNNEST($1::TEXT[]) vs(k)
+	ON CONFLICT ("_key")
+	DO UPDATE SET "data" = "legacy_hash"."data" || $2::TEXT::JSONB`,
+					values: [key, dataString],
+				});
+			} else {
 				await helpers.ensureLegacyObjectType(client, key, 'hash');
 				await client.query({
 					name: 'setObject',
@@ -28,11 +41,6 @@ module.exports = function (module) {
 					values: [key, dataString],
 				});
 			}
-			if (Array.isArray(key)) {
-				await Promise.all(key.map(k => setOne(k)));
-			} else {
-				await setOne(key);
-			}
 		});
 	};
 
@@ -40,8 +48,36 @@ module.exports = function (module) {
 		if (!keys.length || !data.length) {
 			return;
 		}
-		// TODO: single query?
-		await Promise.all(keys.map((k, i) => module.setObject(k, data[i])));
+		await module.transaction(async (client) => {
+			keys = keys.slice();
+			data = data.filter((d, i) => {
+				if (d.hasOwnProperty('')) {
+					delete d[''];
+				}
+				const keep = !!Object.keys(d).length;
+				if (!keep) {
+					keys.splice(i, 1);
+				}
+				return keep;
+			});
+
+			if (!keys.length) {
+				return;
+			}
+
+			await helpers.ensureLegacyObjectsType(client, keys, 'hash');
+			const dataStrings = data.map(JSON.stringify);
+			await client.query({
+				name: 'setObjectBulk',
+				text: `
+			INSERT INTO "legacy_hash" ("_key", "data")
+			SELECT k, d
+			FROM UNNEST($1::TEXT[], $2::TEXT::JSONB[]) vs(k, d)
+			ON CONFLICT ("_key")
+			DO UPDATE SET "data" = "legacy_hash"."data" || EXCLUDED.data`,
+				values: [keys, dataStrings],
+			});
+		});
 	};
 
 	module.setObjectField = async function (key, field, value) {
@@ -51,7 +87,9 @@ module.exports = function (module) {
 
 		await module.transaction(async (client) => {
 			const valueString = JSON.stringify(value);
-			async function setOne(key) {
+			if (Array.isArray(key)) {
+				await module.setObject(key, { [field]: value });
+			} else {
 				await helpers.ensureLegacyObjectType(client, key, 'hash');
 				await client.query({
 					name: 'setObjectField',
@@ -63,12 +101,6 @@ module.exports = function (module) {
 					values: [key, field, valueString],
 				});
 			}
-
-			if (Array.isArray(key)) {
-				await Promise.all(key.map(k => setOne(k)));
-			} else {
-				await setOne(key);
-			}
 		});
 	};
 
@@ -269,7 +301,19 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
 		if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
 			return;
 		}
-		async function delKey(key, fields) {
+
+		if (Array.isArray(key)) {
+			await module.pool.query({
+				name: 'deleteObjectFieldsKeys',
+				text: `
+	UPDATE "legacy_hash"
+	   SET "data" = COALESCE((SELECT jsonb_object_agg("key", "value")
+								FROM jsonb_each("data")
+							   WHERE "key" <> ALL ($2::TEXT[])), '{}')
+	 WHERE "_key" = ANY($1::TEXT[])`,
+				values: [key, fields],
+			});
+		} else {
 			await module.pool.query({
 				name: 'deleteObjectFields',
 				text: `
@@ -281,11 +325,6 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
 				values: [key, fields],
 			});
 		}
-		if (Array.isArray(key)) {
-			await Promise.all(key.map(k => delKey(k, fields)));
-		} else {
-			await delKey(key, fields);
-		}
 	};
 
 	module.incrObjectField = async function (key, field) {
diff --git a/src/database/postgres/list.js b/src/database/postgres/list.js
index 4fb4fc40f1..885adb3825 100644
--- a/src/database/postgres/list.js
+++ b/src/database/postgres/list.js
@@ -9,28 +9,18 @@ module.exports = function (module) {
 		}
 
 		await module.transaction(async (client) => {
-			async function doPrepend(value) {
-				await client.query({
-					name: 'listPrepend',
-					text: `
-	INSERT INTO "legacy_list" ("_key", "array")
-	VALUES ($1::TEXT, ARRAY[$2::TEXT])
-	ON CONFLICT ("_key")
-	DO UPDATE SET "array" = ARRAY[$2::TEXT] || "legacy_list"."array"`,
-					values: [key, value],
-				});
-			}
-
 			await helpers.ensureLegacyObjectType(client, key, 'list');
-			if (Array.isArray(value)) {
-				// TODO: perf make single query
-				for (const v of value) {
-					// eslint-disable-next-line
-					await doPrepend(v);
-				}
-				return;
-			}
-			await doPrepend(value);
+			value = Array.isArray(value) ? value : [value];
+			value.reverse();
+			await client.query({
+				name: 'listPrependValues',
+				text: `
+INSERT INTO "legacy_list" ("_key", "array")
+VALUES ($1::TEXT, $2::TEXT[])
+ON CONFLICT ("_key")
+DO UPDATE SET "array" = EXCLUDED.array || "legacy_list"."array"`,
+				values: [key, value],
+			});
 		});
 	};
 
@@ -39,27 +29,18 @@ module.exports = function (module) {
 			return;
 		}
 		await module.transaction(async (client) => {
-			async function doAppend(value) {
-				await client.query({
-					name: 'listAppend',
-					text: `
-	INSERT INTO "legacy_list" ("_key", "array")
-	VALUES ($1::TEXT, ARRAY[$2::TEXT])
-	ON CONFLICT ("_key")
-	DO UPDATE SET "array" = "legacy_list"."array" || ARRAY[$2::TEXT]`,
-					values: [key, value],
-				});
-			}
+			value = Array.isArray(value) ? value : [value];
+
 			await helpers.ensureLegacyObjectType(client, key, 'list');
-			if (Array.isArray(value)) {
-				// TODO: perf make single query
-				for (const v of value) {
-					// eslint-disable-next-line
-					await doAppend(v);
-				}
-				return;
-			}
-			await doAppend(value);
+			await client.query({
+				name: 'listAppend',
+				text: `
+INSERT INTO "legacy_list" ("_key", "array")
+VALUES ($1::TEXT, $2::TEXT[])
+ON CONFLICT ("_key")
+DO UPDATE SET "array" = "legacy_list"."array" || EXCLUDED.array`,
+				values: [key, value],
+			});
 		});
 	};
 
diff --git a/src/database/postgres/sorted/remove.js b/src/database/postgres/sorted/remove.js
index 54bed38505..2b90dd8bc2 100644
--- a/src/database/postgres/sorted/remove.js
+++ b/src/database/postgres/sorted/remove.js
@@ -71,25 +71,21 @@ DELETE FROM "legacy_zset"
 	};
 
 	module.sortedSetRemoveBulk = async function (data) {
-		// const keys = [];
-		// const values = [];
-
-		// data.forEach(function (item) {
-		// 	keys.push(item[0]);
-		// 	values.push(item[1]);
-		// });
-
-		const promises = data.map(item => module.sortedSetRemove(item[0], item[1]));
-		await Promise.all(promises);
+		if (!Array.isArray(data) || !data.length) {
+			return;
+		}
+		const keys = data.map(d => d[0]);
+		const values = data.map(d => d[1]);
 
-		// TODO
-		// 		await query({
-		// 			name: 'sortedSetRemoveBulk',
-		// 			text: `
-		// DELETE FROM "legacy_zset"
-		// SELECT k, v
-		// FROM UNNEST($1::TEXT[], $2::TEXT[]) vs(k, v)`,
-		// 			values: [keys, values],
-		// 		});
+		await module.pool.query({
+			name: 'sortedSetRemoveBulk',
+			text: `
+	DELETE FROM "legacy_zset"
+	WHERE (_key, value) IN (
+		SELECT k, v
+		FROM UNNEST($1::TEXT[], $2::TEXT[]) vs(k, v)
+		)`,
+			values: [keys, values],
+		});
 	};
 };
diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js
index 09aa643fbe..6193885adb 100644
--- a/src/messaging/rooms.js
+++ b/src/messaging/rooms.js
@@ -69,22 +69,27 @@ module.exports = function (Messaging) {
 
 	Messaging.getUserCountInRoom = async roomId => db.sortedSetCard(`chat:room:${roomId}:uids`);
 
-	Messaging.isRoomOwner = async (uid, roomId) => {
-		const owner = await db.getObjectField(`chat:room:${roomId}`, 'owner');
-		return parseInt(uid, 10) === parseInt(owner, 10);
+	Messaging.isRoomOwner = async (uid, roomId, knownOwner) => {
+		const owner = (knownOwner == null) ? await db.getObjectField(`chat:room:${roomId}`, 'owner') : knownOwner;
+		const isOwner = parseInt(uid, 10) === parseInt(owner, 10);
+
+		const payload = await plugins.hooks.fire('filter:messaging.isRoomOwner', { uid, roomId, owner, isOwner });
+		return payload.isOwner;
 	};
 
 	Messaging.addUsersToRoom = async function (uid, uids, roomId) {
-		const now = Date.now();
-		const timestamps = uids.map(() => now);
 		const inRoom = await Messaging.isUserInRoom(uid, roomId);
-		if (!inRoom) {
+		const payload = await plugins.hooks.fire('filter:messaging.addUsersToRoom', { uid, uids, roomId, inRoom });
+
+		if (!payload.inRoom) {
 			throw new Error('[[error:cant-add-users-to-chat-room]]');
 		}
 
-		await db.sortedSetAdd(`chat:room:${roomId}:uids`, timestamps, uids);
-		await updateGroupChatField([roomId]);
-		await Promise.all(uids.map(uid => Messaging.addSystemMessage('user-join', uid, roomId)));
+		const now = Date.now();
+		const timestamps = payload.uids.map(() => now);
+		await db.sortedSetAdd(`chat:room:${payload.roomId}:uids`, timestamps, payload.uids);
+		await updateGroupChatField([payload.roomId]);
+		await Promise.all(payload.uids.map(uid => Messaging.addSystemMessage('user-join', uid, payload.roomId)));
 	};
 
 	Messaging.removeUsersFromRoom = async (uid, uids, roomId) => {
@@ -92,15 +97,16 @@ module.exports = function (Messaging) {
 			Messaging.isRoomOwner(uid, roomId),
 			Messaging.getUserCountInRoom(roomId),
 		]);
+		const payload = await plugins.hooks.fire('filter:messaging.removeUsersFromRoom', { uid, uids, roomId, isOwner, userCount });
 
-		if (!isOwner) {
+		if (!payload.isOwner) {
 			throw new Error('[[error:cant-remove-users-from-chat-room]]');
 		}
-		if (userCount === 2) {
+		if (payload.userCount === 2) {
 			throw new Error('[[error:cant-remove-last-user]]');
 		}
 
-		await Messaging.leaveRoom(uids, roomId);
+		await Messaging.leaveRoom(payload.uids, payload.roomId);
 	};
 
 	Messaging.isGroupChat = async function (roomId) {
@@ -170,10 +176,10 @@ module.exports = function (Messaging) {
 			db.getObjectField(`chat:room:${roomId}`, 'owner'),
 		]);
 
-		return users.map((user) => {
-			user.isOwner = parseInt(user.uid, 10) === parseInt(ownerId, 10);
+		return Promise.all(users.map(async (user) => {
+			user.isOwner = await Messaging.isRoomOwner(user.uid, roomId, ownerId);
 			return user;
-		});
+		}));
 	};
 
 	Messaging.renameRoom = async function (uid, roomId, newName) {
@@ -220,7 +226,7 @@ module.exports = function (Messaging) {
 			return null;
 		}
 
-		const [roomData, canReply, users, messages, isAdminOrGlobalMod] = await Promise.all([
+		const [room, canReply, users, messages, isAdminOrGlobalMod] = await Promise.all([
 			Messaging.getRoomData(data.roomId),
 			Messaging.canReply(data.roomId, uid),
 			Messaging.getUsersInRoom(data.roomId, 0, -1),
@@ -233,9 +239,8 @@ module.exports = function (Messaging) {
 			user.isAdminOrGlobalMod(uid),
 		]);
 
-		const room = roomData;
 		room.messages = messages;
-		room.isOwner = parseInt(room.owner, 10) === parseInt(uid, 10);
+		room.isOwner = await Messaging.isRoomOwner(uid, room.roomId, room.owner);
 		room.users = users.filter(user => user && parseInt(user.uid, 10) && parseInt(user.uid, 10) !== parseInt(uid, 10));
 		room.canReply = canReply;
 		room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : users.length > 2;
@@ -245,6 +250,7 @@ module.exports = function (Messaging) {
 		room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2;
 		room.isAdminOrGlobalMod = isAdminOrGlobalMod;
 
-		return room;
+		const payload = await plugins.hooks.fire('filter:messaging.loadRoom', { uid, data, room });
+		return payload.room;
 	};
 };
diff --git a/src/posts/edit.js b/src/posts/edit.js
index d704996e1c..8de8f9da1a 100644
--- a/src/posts/edit.js
+++ b/src/posts/edit.js
@@ -79,6 +79,7 @@ module.exports = function (Posts) {
 			bodyShort: translator.compile('notifications:user_edited_post', editor.username, topic.title),
 			nid: `edit_post:${data.pid}:uid:${data.uid}`,
 		});
+		await topics.syncBacklinks(returnPostData);
 
 		plugins.hooks.fire('action:post.edit', { post: _.clone(returnPostData), data: data, uid: data.uid });
 
diff --git a/src/posts/votes.js b/src/posts/votes.js
index 60bbe6361a..c4d976a1ee 100644
--- a/src/posts/votes.js
+++ b/src/posts/votes.js
@@ -63,7 +63,8 @@ module.exports = function (Posts) {
 
 		putVoteInProgress(pid, uid);
 		try {
-			return await unvote(pid, uid, 'unvote');
+			const voteStatus = await Posts.hasVoted(pid, uid);
+			return await unvote(pid, uid, 'unvote', voteStatus);
 		} finally {
 			clearVoteProgress(pid, uid);
 		}
@@ -114,48 +115,26 @@ module.exports = function (Posts) {
 	}
 
 	async function toggleVote(type, pid, uid) {
-		await unvote(pid, uid, type);
-		return await vote(type, false, pid, uid);
+		const voteStatus = await Posts.hasVoted(pid, uid);
+		await unvote(pid, uid, type, voteStatus);
+		return await vote(type, false, pid, uid, voteStatus);
 	}
 
-	async function unvote(pid, uid, command) {
-		const [owner, voteStatus] = await Promise.all([
-			Posts.getPostField(pid, 'uid'),
-			Posts.hasVoted(pid, uid),
-		]);
-
+	async function unvote(pid, uid, type, voteStatus) {
+		const owner = await Posts.getPostField(pid, 'uid');
 		if (parseInt(uid, 10) === parseInt(owner, 10)) {
 			throw new Error('[[error:self-vote]]');
 		}
 
-		if (command === 'downvote') {
+		if (type === 'downvote') {
 			await checkDownvoteLimitation(pid, uid);
 		}
 
-		let hook;
-		let current = voteStatus.upvoted ? 'upvote' : 'downvote';
-
-		if ((voteStatus.upvoted && command === 'downvote') || (voteStatus.downvoted && command === 'upvote')) {	// e.g. User *has* upvoted, and clicks downvote
-			hook = command;
-		} else if (voteStatus.upvoted || voteStatus.downvoted) {	// e.g. User *has* upvoted, clicks upvote (so we "unvote")
-			hook = 'unvote';
-		} else {	// e.g. User *has not* voted, clicks upvote
-			hook = command;
-			current = 'unvote';
-		}
-
-		plugins.hooks.fire(`action:post.${hook}`, {
-			pid: pid,
-			uid: uid,
-			owner: owner,
-			current: current,
-		});
-
 		if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) {
 			return;
 		}
 
-		return await vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid);
+		return await vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid, voteStatus);
 	}
 
 	async function checkDownvoteLimitation(pid, uid) {
@@ -185,7 +164,7 @@ module.exports = function (Posts) {
 		}
 	}
 
-	async function vote(type, unvote, pid, uid) {
+	async function vote(type, unvote, pid, uid, voteStatus) {
 		uid = parseInt(uid, 10);
 		if (uid <= 0) {
 			throw new Error('[[error:not-logged-in]]');
@@ -209,6 +188,8 @@ module.exports = function (Posts) {
 
 		await adjustPostVotes(postData, uid, type, unvote);
 
+		await fireVoteHook(postData, uid, type, unvote, voteStatus);
+
 		return {
 			user: {
 				reputation: newReputation,
@@ -220,6 +201,25 @@ module.exports = function (Posts) {
 		};
 	}
 
+	async function fireVoteHook(postData, uid, type, unvote, voteStatus) {
+		let hook = type;
+		let current = voteStatus.upvoted ? 'upvote' : 'downvote';
+		if (unvote) { // e.g. unvoting, removing a upvote or downvote
+			hook = 'unvote';
+		} else {	// e.g. User *has not* voted, clicks upvote or downvote
+			current = 'unvote';
+		}
+		// action:post.upvote
+		// action:post.downvote
+		// action:post.unvote
+		plugins.hooks.fire(`action:post.${hook}`, {
+			pid: postData.pid,
+			uid: uid,
+			owner: postData.uid,
+			current: current,
+		});
+	}
+
 	async function adjustPostVotes(postData, uid, type, unvote) {
 		const notType = (type === 'upvote' ? 'downvote' : 'upvote');
 		if (unvote) {
diff --git a/src/rewards/admin.js b/src/rewards/admin.js
index 15a0c9fcc8..edd3202e26 100644
--- a/src/rewards/admin.js
+++ b/src/rewards/admin.js
@@ -24,6 +24,7 @@ rewards.save = async function (data) {
 
 	await Promise.all(data.map(data => save(data)));
 	await saveConditions(data);
+	return data;
 };
 
 rewards.delete = async function (data) {
diff --git a/src/socket.io/admin/cache.js b/src/socket.io/admin/cache.js
index 534cefeac8..9188ac8f5b 100644
--- a/src/socket.io/admin/cache.js
+++ b/src/socket.io/admin/cache.js
@@ -2,11 +2,13 @@
 
 const SocketCache = module.exports;
 
+const db = require('../../database');
+
 SocketCache.clear = async function (socket, data) {
 	if (data.name === 'post') {
 		require('../../posts/cache').reset();
-	} else if (data.name === 'object') {
-		require('../../database').objectCache.reset();
+	} else if (data.name === 'object' && db.objectCache) {
+		db.objectCache.reset();
 	} else if (data.name === 'group') {
 		require('../../groups').cache.reset();
 	} else if (data.name === 'local') {
@@ -17,7 +19,7 @@ SocketCache.clear = async function (socket, data) {
 SocketCache.toggle = async function (socket, data) {
 	const caches = {
 		post: require('../../posts/cache'),
-		object: require('../../database').objectCache,
+		object: db.objectCache,
 		group: require('../../groups').cache,
 		local: require('../../cache'),
 	};
diff --git a/src/socket.io/admin/rewards.js b/src/socket.io/admin/rewards.js
index e68527547c..278d5e6e0f 100644
--- a/src/socket.io/admin/rewards.js
+++ b/src/socket.io/admin/rewards.js
@@ -5,7 +5,7 @@ const rewardsAdmin = require('../../rewards/admin');
 const SocketRewards = module.exports;
 
 SocketRewards.save = async function (socket, data) {
-	await rewardsAdmin.save(data);
+	return await rewardsAdmin.save(data);
 };
 
 SocketRewards.delete = async function (socket, data) {
diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js
index 7ff99d6992..a4ab025cc4 100644
--- a/src/socket.io/posts.js
+++ b/src/socket.io/posts.js
@@ -150,13 +150,17 @@ async function acceptOrReject(method, socket, data) {
 }
 
 async function sendQueueNotification(type, targetUid, path) {
-	const notifObj = await notifications.create({
+	const notifData = {
 		type: type,
 		nid: `${type}-${targetUid}-${path}`,
 		bodyShort: type === 'post-queue-accepted' ?
 			'[[notifications:post-queue-accepted]]' : '[[notifications:post-queue-rejected]]',
 		path: path,
-	});
+	};
+	if (parseInt(meta.config.postQueueNotificationUid, 10) > 0) {
+		notifData.from = meta.config.postQueueNotificationUid;
+	}
+	const notifObj = await notifications.create(notifData);
 	await notifications.push(notifObj, [targetUid]);
 }
 
diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js
index 03266a3059..93d2bbeb49 100644
--- a/src/socket.io/topics/infinitescroll.js
+++ b/src/socket.io/topics/infinitescroll.js
@@ -1,5 +1,7 @@
 'use strict';
 
+const winston = require('winston');
+
 const topics = require('../../topics');
 const privileges = require('../../privileges');
 const meta = require('../../meta');
@@ -62,6 +64,7 @@ module.exports = function (SocketTopics) {
 	};
 
 	SocketTopics.loadMoreSortedTopics = async function (socket, data) {
+		winston.warn('[deprecated] use infinitescroll.loadMoreXhr'); // TODO: remove in 1.19.0
 		if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) {
 			throw new Error('[[error:invalid-data]]');
 		}
@@ -85,6 +88,7 @@ module.exports = function (SocketTopics) {
 	};
 
 	SocketTopics.loadMoreFromSet = async function (socket, data) {
+		winston.warn('[deprecated] use infinitescroll.loadMoreXhr'); // TODO: remove in 1.19.0
 		if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0 || !data.set) {
 			throw new Error('[[error:invalid-data]]');
 		}
diff --git a/src/topics/create.js b/src/topics/create.js
index c9ac208ea9..1308827789 100644
--- a/src/topics/create.js
+++ b/src/topics/create.js
@@ -60,6 +60,7 @@ module.exports = function (Topics) {
 				'topics:views', 'topics:posts', 'topics:votes',
 				`cid:${topicData.cid}:tids:votes`,
 				`cid:${topicData.cid}:tids:posts`,
+				`cid:${topicData.cid}:tids:views`,
 			], 0, topicData.tid),
 			user.addTopicIdToUser(topicData.uid, topicData.tid, timestamp),
 			db.incrObjectField(`category:${topicData.cid}`, 'topic_count'),
@@ -217,6 +218,7 @@ module.exports = function (Topics) {
 			posts.getUserInfoForPosts([postData.uid], uid),
 			Topics.getTopicFields(tid, ['tid', 'uid', 'title', 'slug', 'cid', 'postcount', 'mainPid', 'scheduled']),
 			Topics.addParentPosts([postData]),
+			Topics.syncBacklinks(postData),
 			posts.parsePost(postData),
 		]);
 
diff --git a/src/topics/delete.js b/src/topics/delete.js
index 889f129c76..3e84a29cc8 100644
--- a/src/topics/delete.js
+++ b/src/topics/delete.js
@@ -125,6 +125,7 @@ module.exports = function (Topics) {
 				`cid:${topicData.cid}:tids:posts`,
 				`cid:${topicData.cid}:tids:lastposttime`,
 				`cid:${topicData.cid}:tids:votes`,
+				`cid:${topicData.cid}:tids:views`,
 				`cid:${topicData.cid}:recent_tids`,
 				`cid:${topicData.cid}:uid:${topicData.uid}:tids`,
 				`uid:${topicData.uid}:topics`,
diff --git a/src/topics/events.js b/src/topics/events.js
index 0e9e64182b..1d2688e5fd 100644
--- a/src/topics/events.js
+++ b/src/topics/events.js
@@ -2,6 +2,7 @@
 
 const _ = require('lodash');
 const db = require('../database');
+const meta = require('../meta');
 const user = require('../user');
 const posts = require('../posts');
 const categories = require('../categories');
@@ -53,6 +54,10 @@ Events._types = {
 		text: '[[topic:queued-by]]',
 		href: '/post-queue',
 	},
+	backlink: {
+		icon: 'fa-link',
+		text: '[[topic:backlink]]',
+	},
 };
 
 Events.init = async () => {
@@ -115,6 +120,11 @@ async function modifyEvent({ tid, uid, eventIds, timestamps, events }) {
 		getCategoryInfo(events.map(event => event.fromCid).filter(Boolean)),
 	]);
 
+	// Remove backlink events if backlinks are disabled
+	if (meta.config.topicBacklinks !== 1) {
+		events = events.filter(event => event.type !== 'backlink');
+	}
+
 	// Remove events whose types no longer exist (e.g. plugin uninstalled)
 	events = events.filter(event => Events._types.hasOwnProperty(event.type));
 
diff --git a/src/topics/posts.js b/src/topics/posts.js
index ed6f6089ad..2384dead48 100644
--- a/src/topics/posts.js
+++ b/src/topics/posts.js
@@ -3,6 +3,7 @@
 
 const _ = require('lodash');
 const validator = require('validator');
+const nconf = require('nconf');
 
 const db = require('../database');
 const user = require('../user');
@@ -11,6 +12,8 @@ const meta = require('../meta');
 const plugins = require('../plugins');
 const utils = require('../../public/src/utils');
 
+const backlinkRegex = new RegExp(`(?:${nconf.get('url').replace('/', '\\/')}|\b|\\s)\\/topic\\/(\\d+)(?:\\/\\w+)?`, 'g');
+
 module.exports = function (Topics) {
 	Topics.onNewPostMade = async function (postData) {
 		await Topics.updateLastPostTime(postData.tid, postData.timestamp);
@@ -208,12 +211,13 @@ module.exports = function (Topics) {
 	};
 
 	Topics.increaseViewCount = async function (tid) {
-		incrementFieldAndUpdateSortedSet(tid, 'viewcount', 1, 'topics:views');
+		const cid = await Topics.getTopicField(tid, 'cid');
+		incrementFieldAndUpdateSortedSet(tid, 'viewcount', 1, ['topics:views', `cid:${cid}:tids:views`]);
 	};
 
 	async function incrementFieldAndUpdateSortedSet(tid, field, by, set) {
 		const value = await db.incrObjectFieldBy(`topic:${tid}`, field, by);
-		await db.sortedSetAdd(set, value, tid);
+		await db[Array.isArray(set) ? 'sortedSetsAdd' : 'sortedSetAdd'](set, value, tid);
 	}
 
 	Topics.getTitleByPid = async function (pid) {
@@ -287,4 +291,40 @@ module.exports = function (Topics) {
 
 		return returnData;
 	}
+
+	Topics.syncBacklinks = async (postData) => {
+		if (!postData) {
+			throw new Error('[[error:invalid-data]]');
+		}
+
+		// Scan post content for topic links
+		const matches = [...postData.content.matchAll(backlinkRegex)];
+		if (!matches) {
+			return 0;
+		}
+
+		const { pid, uid, tid } = postData;
+		let add = matches.map(match => match[1]);
+
+		const now = Date.now();
+		const topicsExist = await Topics.exists(add);
+		const current = (await db.getSortedSetMembers(`pid:${pid}:backlinks`)).map(tid => parseInt(tid, 10));
+		const remove = current.filter(tid => !add.includes(tid));
+		add = add.filter((_tid, idx) => topicsExist[idx] && !current.includes(_tid) && tid !== parseInt(_tid, 10));
+
+		// Remove old backlinks
+		await db.sortedSetRemove(`pid:${pid}:backlinks`, remove);
+
+		// Add new backlinks
+		await db.sortedSetAdd(`pid:${pid}:backlinks`, add.map(Number.bind(null, now)), add);
+		await Promise.all(add.map(async (tid) => {
+			await Topics.events.log(tid, {
+				uid,
+				type: 'backlink',
+				href: `/post/${pid}`,
+			});
+		}));
+
+		return add.length + (current - remove);
+	};
 };
diff --git a/src/topics/scheduled.js b/src/topics/scheduled.js
index ab9cb04ef0..5d99b1432b 100644
--- a/src/topics/scheduled.js
+++ b/src/topics/scheduled.js
@@ -54,6 +54,7 @@ Scheduled.pin = async function (tid, topicData) {
 			`cid:${topicData.cid}:tids`,
 			`cid:${topicData.cid}:tids:posts`,
 			`cid:${topicData.cid}:tids:votes`,
+			`cid:${topicData.cid}:tids:views`,
 		], tid),
 	]);
 };
@@ -80,6 +81,7 @@ function unpin(tid, topicData) {
 			[`cid:${topicData.cid}:tids`, topicData.lastposttime, tid],
 			[`cid:${topicData.cid}:tids:posts`, topicData.postcount, tid],
 			[`cid:${topicData.cid}:tids:votes`, parseInt(topicData.votes, 10) || 0, tid],
+			[`cid:${topicData.cid}:tids:views`, topicData.viewcount, tid],
 		]),
 	];
 }
diff --git a/src/topics/sorted.js b/src/topics/sorted.js
index 52f96ed59e..af2cd344cb 100644
--- a/src/topics/sorted.js
+++ b/src/topics/sorted.js
@@ -118,6 +118,7 @@ module.exports = function (Topics) {
 			old: sortOld,
 			posts: sortPopular,
 			votes: sortVotes,
+			views: sortViews,
 		};
 		const sortFn = sortMap[params.sort] || sortRecent;
 
@@ -156,6 +157,10 @@ module.exports = function (Topics) {
 		return b.viewcount - a.viewcount;
 	}
 
+	function sortViews(a, b) {
+		return b.viewcount - a.viewcount;
+	}
+
 	async function filterTids(tids, params) {
 		const { filter } = params;
 		const { uid } = params;
diff --git a/src/topics/tools.js b/src/topics/tools.js
index fa0d32aad9..887f166247 100644
--- a/src/topics/tools.js
+++ b/src/topics/tools.js
@@ -173,6 +173,7 @@ module.exports = function (Topics) {
 				`cid:${topicData.cid}:tids`,
 				`cid:${topicData.cid}:tids:posts`,
 				`cid:${topicData.cid}:tids:votes`,
+				`cid:${topicData.cid}:tids:views`,
 			], tid));
 		} else {
 			promises.push(db.sortedSetRemove(`cid:${topicData.cid}:tids:pinned`, tid));
@@ -181,6 +182,7 @@ module.exports = function (Topics) {
 				[`cid:${topicData.cid}:tids`, topicData.lastposttime, tid],
 				[`cid:${topicData.cid}:tids:posts`, topicData.postcount, tid],
 				[`cid:${topicData.cid}:tids:votes`, parseInt(topicData.votes, 10) || 0, tid],
+				[`cid:${topicData.cid}:tids:views`, topicData.viewcount, tid],
 			]));
 			topicData.pinExpiry = undefined;
 			topicData.pinExpiryISO = undefined;
@@ -243,6 +245,7 @@ module.exports = function (Topics) {
 			`cid:${topicData.cid}:tids:pinned`,
 			`cid:${topicData.cid}:tids:posts`,
 			`cid:${topicData.cid}:tids:votes`,
+			`cid:${topicData.cid}:tids:views`,
 			`cid:${topicData.cid}:tids:lastposttime`,
 			`cid:${topicData.cid}:recent_tids`,
 			`cid:${topicData.cid}:uid:${topicData.uid}:tids`,
@@ -263,6 +266,7 @@ module.exports = function (Topics) {
 			bulk.push([`cid:${cid}:tids`, topicData.lastposttime, tid]);
 			bulk.push([`cid:${cid}:tids:posts`, topicData.postcount, tid]);
 			bulk.push([`cid:${cid}:tids:votes`, votes, tid]);
+			bulk.push([`cid:${cid}:tids:views`, topicData.viewcount, tid]);
 		}
 		await db.sortedSetAddBulk(bulk);
 
diff --git a/src/upgrades/1.18.0/topic_tags_refactor.js b/src/upgrades/1.18.0/topic_tags_refactor.js
index c81e139bb3..5fd2218c49 100644
--- a/src/upgrades/1.18.0/topic_tags_refactor.js
+++ b/src/upgrades/1.18.0/topic_tags_refactor.js
@@ -28,6 +28,7 @@ module.exports = {
 				topicsWithTags.map(t => `topic:${t.tid}`),
 				topicsWithTags.map(t => ({ tags: t.tags.join(',') }))
 			);
+			await db.deleteAll(tids.map(tid => `topic:${tid}:tags`));
 			progress.incr(tids.length);
 		}, {
 			batch: 500,
diff --git a/src/upgrades/1.18.4/category_topics_views.js b/src/upgrades/1.18.4/category_topics_views.js
new file mode 100644
index 0000000000..c2d78fbf9e
--- /dev/null
+++ b/src/upgrades/1.18.4/category_topics_views.js
@@ -0,0 +1,23 @@
+'use strict';
+
+const db = require('../../database');
+const batch = require('../../batch');
+const topics = require('../../topics');
+
+module.exports = {
+	name: 'Category topics sorted sets by views',
+	timestamp: Date.UTC(2021, 8, 28),
+	method: async function () {
+		const { progress } = this;
+
+		await batch.processSortedSet('topics:tid', async (tids) => {
+			let topicData = await topics.getTopicsData(tids);
+			topicData = topicData.filter(t => t && t.cid);
+			await db.sortedSetAddBulk(topicData.map(t => ([`cid:${t.cid}:tids:views`, t.viewcount || 0, t.tid])));
+			progress.incr(tids.length);
+		}, {
+			batch: 500,
+			progress: progress,
+		});
+	},
+};
diff --git a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js
index 0ebfd4b274..a6aa26ba4d 100644
--- a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js
+++ b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js
@@ -1,107 +1,52 @@
-'use strict';
+/* eslint-disable no-await-in-loop */
 
+'use strict';
 
-const async = require('async');
 const winston = require('winston');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Granting edit/delete/delete topic on existing categories',
 	timestamp: Date.UTC(2016, 7, 7),
-	method: function (callback) {
+	method: async function () {
 		const groupsAPI = require('../../groups');
 		const privilegesAPI = require('../../privileges');
 
-		db.getSortedSetRange('categories:cid', 0, -1, (err, cids) => {
-			if (err) {
-				return callback(err);
+		const cids = await db.getSortedSetRange('categories:cid', 0, -1);
+
+		for (const cid of cids) {
+			const data = await privilegesAPI.categories.list(cid);
+			const { groups, users } = data;
+
+			for (const group of groups) {
+				if (group.privileges['groups:topics:reply']) {
+					await Promise.all([
+						groupsAPI.join(`cid:${cid}:privileges:groups:posts:edit`, group.name),
+						groupsAPI.join(`cid:${cid}:privileges:groups:posts:delete`, group.name),
+					]);
+					winston.verbose(`cid:${cid}:privileges:groups:posts:edit, cid:${cid}:privileges:groups:posts:delete granted to gid: ${group.name}`);
+				}
+
+				if (group.privileges['groups:topics:create']) {
+					await groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name);
+					winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`);
+				}
 			}
 
-			async.eachSeries(cids, (cid, next) => {
-				privilegesAPI.categories.list(cid, (err, data) => {
-					if (err) {
-						return next(err);
-					}
-
-					const { groups } = data;
-					const { users } = data;
-
-					async.waterfall([
-						function (next) {
-							async.eachSeries(groups, (group, next) => {
-								if (group.privileges['groups:topics:reply']) {
-									return async.parallel([
-										async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:edit`, group.name),
-										async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:delete`, group.name),
-									], (err) => {
-										if (!err) {
-											winston.verbose(`cid:${cid}:privileges:groups:posts:edit, cid:${cid}:privileges:groups:posts:delete granted to gid: ${group.name}`);
-										}
-
-										return next(err);
-									});
-								}
-
-								next(null);
-							}, next);
-						},
-						function (next) {
-							async.eachSeries(groups, (group, next) => {
-								if (group.privileges['groups:topics:create']) {
-									return groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name, (err) => {
-										if (!err) {
-											winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`);
-										}
-
-										return next(err);
-									});
-								}
-
-								next(null);
-							}, next);
-						},
-						function (next) {
-							async.eachSeries(users, (user, next) => {
-								if (user.privileges['topics:reply']) {
-									return async.parallel([
-										async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:edit`, user.uid),
-										async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:delete`, user.uid),
-									], (err) => {
-										if (!err) {
-											winston.verbose(`cid:${cid}:privileges:posts:edit, cid:${cid}:privileges:posts:delete granted to uid: ${user.uid}`);
-										}
-
-										return next(err);
-									});
-								}
-
-								next(null);
-							}, next);
-						},
-						function (next) {
-							async.eachSeries(users, (user, next) => {
-								if (user.privileges['topics:create']) {
-									return groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid, (err) => {
-										if (!err) {
-											winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`);
-										}
-
-										return next(err);
-									});
-								}
-
-								next(null);
-							}, next);
-						},
-					], (err) => {
-						if (!err) {
-							winston.verbose(`-- cid ${cid} upgraded`);
-						}
-
-						next(err);
-					});
-				});
-			}, callback);
-		});
+			for (const user of users) {
+				if (user.privileges['topics:reply']) {
+					await Promise.all([
+						groupsAPI.join(`cid:${cid}:privileges:posts:edit`, user.uid),
+						groupsAPI.join(`cid:${cid}:privileges:posts:delete`, user.uid),
+					]);
+					winston.verbose(`cid:${cid}:privileges:posts:edit, cid:${cid}:privileges:posts:delete granted to uid: ${user.uid}`);
+				}
+				if (user.privileges['topics:create']) {
+					await groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid);
+					winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`);
+				}
+			}
+			winston.verbose(`-- cid ${cid} upgraded`);
+		}
 	},
 };
diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js
index 782b4774db..a08aa6a5d3 100644
--- a/src/upgrades/1.3.0/favourites_to_bookmarks.js
+++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js
@@ -1,56 +1,39 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
-
 module.exports = {
 	name: 'Favourites to Bookmarks',
 	timestamp: Date.UTC(2016, 9, 8),
-	method: function (callback) {
+	method: async function () {
 		const { progress } = this;
+		const batch = require('../../batch');
 
-		function upgradePosts(next) {
-			const batch = require('../../batch');
-
-			batch.processSortedSet('posts:pid', (ids, next) => {
-				async.each(ids, (id, next) => {
+		async function upgradePosts() {
+			await batch.processSortedSet('posts:pid', async (ids) => {
+				await Promise.all(ids.map(async (id) => {
 					progress.incr();
-
-					async.waterfall([
-						function (next) {
-							db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`, next);
-						},
-						function (next) {
-							db.getObjectField(`post:${id}`, 'reputation', next);
-						},
-						function (reputation, next) {
-							if (parseInt(reputation, 10)) {
-								db.setObjectField(`post:${id}`, 'bookmarks', reputation, next);
-							} else {
-								next();
-							}
-						},
-						function (next) {
-							db.deleteObjectField(`post:${id}`, 'reputation', next);
-						},
-					], next);
-				}, next);
+					await db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`);
+					const reputation = await db.getObjectField(`post:${id}`, 'reputation');
+					if (parseInt(reputation, 10)) {
+						await db.setObjectField(`post:${id}`, 'bookmarks', reputation);
+					}
+					await db.deleteObjectField(`post:${id}`, 'reputation');
+				}));
 			}, {
 				progress: progress,
-			}, next);
+			});
 		}
 
-		function upgradeUsers(next) {
-			const batch = require('../../batch');
-
-			batch.processSortedSet('users:joindate', (ids, next) => {
-				async.each(ids, (id, next) => {
-					db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`, next);
-				}, next);
-			}, {}, next);
+		async function upgradeUsers() {
+			await batch.processSortedSet('users:joindate', async (ids) => {
+				await Promise.all(ids.map(async (id) => {
+					await db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`);
+				}));
+			}, {});
 		}
 
-		async.series([upgradePosts, upgradeUsers], callback);
+		await upgradePosts();
+		await upgradeUsers();
 	},
 };
diff --git a/src/upgrades/1.4.0/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js
index f565d6c423..e18442e8f8 100644
--- a/src/upgrades/1.4.0/global_and_user_language_keys.js
+++ b/src/upgrades/1.4.0/global_and_user_language_keys.js
@@ -1,57 +1,37 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
-
 module.exports = {
 	name: 'Update global and user language keys',
 	timestamp: Date.UTC(2016, 10, 22),
-	method: function (callback) {
+	method: async function () {
+		const { progress } = this;
 		const user = require('../../user');
 		const meta = require('../../meta');
 		const batch = require('../../batch');
-		let newLanguage;
-		async.parallel([
-			function (next) {
-				meta.configs.get('defaultLang', (err, defaultLang) => {
-					if (err) {
-						return next(err);
-					}
 
-					if (!defaultLang) {
-						return setImmediate(next);
-					}
+		const defaultLang = await meta.configs.get('defaultLang');
+		if (defaultLang) {
+			const newLanguage = defaultLang.replace('_', '-').replace('@', '-x-');
+			if (newLanguage !== defaultLang) {
+				await meta.configs.set('defaultLang', newLanguage);
+			}
+		}
 
-					newLanguage = defaultLang.replace('_', '-').replace('@', '-x-');
-					if (newLanguage !== defaultLang) {
-						meta.configs.set('defaultLang', newLanguage, next);
-					} else {
-						setImmediate(next);
+		await batch.processSortedSet('users:joindate', async (ids) => {
+			await Promise.all(ids.map(async (uid) => {
+				progress.incr();
+				const language = await db.getObjectField(`user:${uid}:settings`, 'userLang');
+				if (language) {
+					const newLanguage = language.replace('_', '-').replace('@', '-x-');
+					if (newLanguage !== language) {
+						await user.setSetting(uid, 'userLang', newLanguage);
 					}
-				});
-			},
-			function (next) {
-				batch.processSortedSet('users:joindate', (ids, next) => {
-					async.each(ids, (uid, next) => {
-						async.waterfall([
-							async.apply(db.getObjectField, `user:${uid}:settings`, 'userLang'),
-							function (language, next) {
-								if (!language) {
-									return setImmediate(next);
-								}
-
-								newLanguage = language.replace('_', '-').replace('@', '-x-');
-								if (newLanguage !== language) {
-									user.setSetting(uid, 'userLang', newLanguage, next);
-								} else {
-									setImmediate(next);
-								}
-							},
-						], next);
-					}, next);
-				}, next);
-			},
-		], callback);
+				}
+			}));
+		}, {
+			progress: progress,
+		});
 	},
 };
diff --git a/src/upgrades/1.4.4/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js
index 5bf80c14fa..97b3c95670 100644
--- a/src/upgrades/1.4.4/config_urls_update.js
+++ b/src/upgrades/1.4.4/config_urls_update.js
@@ -1,36 +1,34 @@
 'use strict';
 
-const async = require('async');
-const db = require('../../database');
 
+const db = require('../../database');
 
 module.exports = {
 	name: 'Upgrading config urls to use assets route',
 	timestamp: Date.UTC(2017, 1, 28),
-	method: function (callback) {
-		async.waterfall([
-			function (cb) {
-				db.getObject('config', cb);
-			},
-			function (config, cb) {
-				if (!config) {
-					return cb();
+	method: async function () {
+		const config = await db.getObject('config');
+		if (config) {
+			const keys = [
+				'brand:favicon',
+				'brand:touchicon',
+				'og:image',
+				'brand:logo:url',
+				'defaultAvatar',
+				'profile:defaultCovers',
+			];
+
+			keys.forEach((key) => {
+				const oldValue = config[key];
+
+				if (!oldValue || typeof oldValue !== 'string') {
+					return;
 				}
 
-				const keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers'];
-
-				keys.forEach((key) => {
-					const oldValue = config[key];
-
-					if (!oldValue || typeof oldValue !== 'string') {
-						return;
-					}
-
-					config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/');
-				});
+				config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/');
+			});
 
-				db.setObject('config', config, cb);
-			},
-		], callback);
+			await db.setObject('config', config);
+		}
 	},
 };
diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js
index a257da1725..dc3a1f465c 100644
--- a/src/upgrades/1.4.6/delete_sessions.js
+++ b/src/upgrades/1.4.6/delete_sessions.js
@@ -1,7 +1,5 @@
 'use strict';
 
-const async = require('async');
-
 const nconf = require('nconf');
 const db = require('../../database');
 const batch = require('../../batch');
@@ -9,7 +7,7 @@ const batch = require('../../batch');
 module.exports = {
 	name: 'Delete accidentally long-lived sessions',
 	timestamp: Date.UTC(2017, 3, 16),
-	method: function (callback) {
+	method: async function () {
 		let configJSON;
 		try {
 			configJSON = require('../../../config.json') || { [process.env.database]: true };
@@ -20,44 +18,24 @@ module.exports = {
 		const isRedisSessionStore = configJSON.hasOwnProperty('redis');
 		const { progress } = this;
 
-		async.waterfall([
-			function (next) {
-				if (isRedisSessionStore) {
-					const connection = require('../../database/redis/connection');
-					let client;
-					async.waterfall([
-						function (next) {
-							connection.connect(nconf.get('redis'), next);
-						},
-						function (_client, next) {
-							client = _client;
-							client.keys('sess:*', next);
-						},
-						function (sessionKeys, next) {
-							progress.total = sessionKeys.length;
+		if (isRedisSessionStore) {
+			const connection = require('../../database/redis/connection');
+			const client = await connection.connect(nconf.get('redis'));
+			const sessionKeys = await client.keys('sess:*');
+			progress.total = sessionKeys.length;
 
-							batch.processArray(sessionKeys, (keys, next) => {
-								const multi = client.multi();
-								keys.forEach((key) => {
-									progress.incr();
-									multi.del(key);
-								});
-								multi.exec(next);
-							}, {
-								batch: 1000,
-							}, next);
-						},
-					], (err) => {
-						next(err);
-					});
-				} else if (db.client && db.client.collection) {
-					db.client.collection('sessions').deleteMany({}, {}, (err) => {
-						next(err);
-					});
-				} else {
-					next();
-				}
-			},
-		], callback);
+			await batch.processArray(sessionKeys, async (keys) => {
+				const multi = client.multi();
+				keys.forEach((key) => {
+					progress.incr();
+					multi.del(key);
+				});
+				await multi.exec();
+			}, {
+				batch: 1000,
+			});
+		} else if (db.client && db.client.collection) {
+			await db.client.collection('sessions').deleteMany({}, {});
+		}
 	},
 };
diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js
index f16d9a5224..dfbb28d70d 100644
--- a/src/upgrades/1.5.0/flags_refactor.js
+++ b/src/upgrades/1.5.0/flags_refactor.js
@@ -1,88 +1,56 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
-
 module.exports = {
 	name: 'Migrating flags to new schema',
 	timestamp: Date.UTC(2016, 11, 7),
-	method: function (callback) {
+	method: async function () {
 		const batch = require('../../batch');
 		const posts = require('../../posts');
 		const flags = require('../../flags');
 		const { progress } = this;
 
-		batch.processSortedSet('posts:pid', (ids, next) => {
-			posts.getPostsByPids(ids, 1, (err, posts) => {
-				if (err) {
-					return next(err);
-				}
-
-				posts = posts.filter(post => post.hasOwnProperty('flags'));
-
-				async.each(posts, (post, next) => {
-					progress.incr();
-
-					async.parallel({
-						uids: async.apply(db.getSortedSetRangeWithScores, `pid:${post.pid}:flag:uids`, 0, -1),
-						reasons: async.apply(db.getSortedSetRange, `pid:${post.pid}:flag:uid:reason`, 0, -1),
-					}, (err, data) => {
-						if (err) {
-							return next(err);
+		await batch.processSortedSet('posts:pid', async (ids) => {
+			let postData = await posts.getPostsByPids(ids, 1);
+			postData = postData.filter(post => post.hasOwnProperty('flags'));
+			await Promise.all(postData.map(async (post) => {
+				progress.incr();
+
+				const [uids, reasons] = await Promise.all([
+					db.getSortedSetRangeWithScores(`pid:${post.pid}:flag:uids`, 0, -1),
+					db.getSortedSetRange(`pid:${post.pid}:flag:uid:reason`, 0, -1),
+				]);
+
+				// Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db)
+				if (uids.length && reasons.length) {
+					// Just take the first entry
+					const datetime = uids[0].score;
+					const reason = reasons[0].split(':')[1];
+
+					try {
+						const flagObj = await flags.create('post', post.pid, uids[0].value, reason, datetime);
+						if (post['flag:state'] || post['flag:assignee']) {
+							await flags.update(flagObj.flagId, 1, {
+								state: post['flag:state'],
+								assignee: post['flag:assignee'],
+								datetime: datetime,
+							});
 						}
-
-						// Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db)
-						if (!data.uids.length || !data.reasons.length) {
-							return setImmediate(next);
+						if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) {
+							let history = JSON.parse(post['flag:history']);
+							history = history.filter(event => event.type === 'notes')[0];
+							await flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp);
 						}
-
-						// Just take the first entry
-						const datetime = data.uids[0].score;
-						const reason = data.reasons[0].split(':')[1];
-						let flagObj;
-
-						async.waterfall([
-							async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime),
-							function (_flagObj, next) {
-								flagObj = _flagObj;
-								if (post['flag:state'] || post['flag:assignee']) {
-									flags.update(flagObj.flagId, 1, {
-										state: post['flag:state'],
-										assignee: post['flag:assignee'],
-										datetime: datetime,
-									}, next);
-								} else {
-									setImmediate(next);
-								}
-							},
-							function (next) {
-								if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) {
-									try {
-										let history = JSON.parse(post['flag:history']);
-										history = history.filter(event => event.type === 'notes')[0];
-
-										flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next);
-									} catch (e) {
-										next(e);
-									}
-								} else {
-									setImmediate(next);
-								}
-							},
-						], (err) => {
-							if (err && err.message === '[[error:post-already-flagged]]') {
-								// Already flagged, no need to parse, but not an error
-								next();
-							} else {
-								next(err);
-							}
-						});
-					});
-				}, next);
-			});
+					} catch (err) {
+						if (err.message !== '[[error:post-already-flagged]]') {
+							throw err;
+						}
+					}
+				}
+			}));
 		}, {
 			progress: this.progress,
-		}, callback);
+		});
 	},
 };
diff --git a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js
index 9cb34c5986..769ca20247 100644
--- a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js
+++ b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js
@@ -1,36 +1,26 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 const batch = require('../../batch');
 
-
 module.exports = {
 	name: 'Remove relative_path from uploaded profile cover urls',
 	timestamp: Date.UTC(2017, 3, 26),
-	method: function (callback) {
+	method: async function () {
 		const { progress } = this;
 
-		batch.processSortedSet('users:joindate', (ids, done) => {
-			async.each(ids, (uid, cb) => {
-				async.waterfall([
-					function (next) {
-						db.getObjectField(`user:${uid}`, 'cover:url', next);
-					},
-					function (url, next) {
-						progress.incr();
-
-						if (!url) {
-							return next();
-						}
+		await batch.processSortedSet('users:joindate', async (ids) => {
+			await Promise.all(ids.map(async (uid) => {
+				const url = await db.getObjectField(`user:${uid}`, 'cover:url');
+				progress.incr();
 
-						const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/');
-						db.setObjectField(`user:${uid}`, 'cover:url', newUrl, next);
-					},
-				], cb);
-			}, done);
+				if (url) {
+					const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/');
+					await db.setObjectField(`user:${uid}`, 'cover:url', newUrl);
+				}
+			}));
 		}, {
 			progress: this.progress,
-		}, callback);
+		});
 	},
 };
diff --git a/src/upgrades/1.6.0/clear-stale-digest-template.js b/src/upgrades/1.6.0/clear-stale-digest-template.js
index ff34668691..3bcd11b49c 100644
--- a/src/upgrades/1.6.0/clear-stale-digest-template.js
+++ b/src/upgrades/1.6.0/clear-stale-digest-template.js
@@ -1,31 +1,21 @@
 'use strict';
 
-
-const async = require('async');
 const crypto = require('crypto');
 const meta = require('../../meta');
 
 module.exports = {
 	name: 'Clearing stale digest templates that were accidentally saved as custom',
 	timestamp: Date.UTC(2017, 8, 6),
-	method: function (callback) {
+	method: async function () {
 		const matches = [
 			'112e541b40023d6530dd44df4b0d9c5d',		// digest @ 75917e25b3b5ad7bed8ed0c36433fb35c9ab33eb
 			'110b8805f70395b0282fd10555059e9f',		// digest @ 9b02bb8f51f0e47c6e335578f776ffc17bc03537
 			'9538e7249edb369b2a25b03f2bd3282b',		// digest @ 3314ab4b83138c7ae579ac1f1f463098b8c2d414
 		];
-
-		async.waterfall([
-			async.apply(meta.configs.getFields, ['email:custom:digest']),
-			function (fieldset, next) {
-				const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null;
-
-				if (matches.includes(hash)) {
-					meta.configs.remove('email:custom:digest', next);
-				} else {
-					setImmediate(next);
-				}
-			},
-		], callback);
+		const fieldset = await meta.configs.getFields(['email:custom:digest']);
+		const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null;
+		if (matches.includes(hash)) {
+			await meta.configs.remove('email:custom:digest');
+		}
 	},
 };
diff --git a/src/upgrades/1.6.0/ipblacklist-fix.js b/src/upgrades/1.6.0/ipblacklist-fix.js
index 7e3f13d506..000de231ba 100644
--- a/src/upgrades/1.6.0/ipblacklist-fix.js
+++ b/src/upgrades/1.6.0/ipblacklist-fix.js
@@ -1,25 +1,13 @@
 'use strict';
 
-const async = require('async');
-
 const db = require('../../database');
 
 module.exports = {
 	name: 'Changing ip blacklist storage to object',
 	timestamp: Date.UTC(2017, 8, 7),
-	method: function (callback) {
-		let rules;
-		async.waterfall([
-			function (next) {
-				db.get('ip-blacklist-rules', next);
-			},
-			function (_rules, next) {
-				rules = _rules;
-				db.delete('ip-blacklist-rules', rules ? next : callback);
-			},
-			function (next) {
-				db.setObject('ip-blacklist-rules', { rules: rules }, next);
-			},
-		], callback);
+	method: async function () {
+		const rules = await db.get('ip-blacklist-rules');
+		await db.delete('ip-blacklist-rules');
+		await db.setObject('ip-blacklist-rules', { rules: rules });
 	},
 };
diff --git a/src/upgrades/1.6.0/robots-config-change.js b/src/upgrades/1.6.0/robots-config-change.js
index 7efc789357..7e787389e7 100644
--- a/src/upgrades/1.6.0/robots-config-change.js
+++ b/src/upgrades/1.6.0/robots-config-change.js
@@ -1,35 +1,21 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Fix incorrect robots.txt schema',
 	timestamp: Date.UTC(2017, 6, 10),
-	method: function (callback) {
-		async.waterfall([
-			function (next) {
-				db.getObject('config', next);
-			},
-			function (config, next) {
-				if (!config) {
-					return callback();
-				}
-				// fix mongo nested data
-				if (config.robots && config.robots.txt) {
-					db.setObjectField('config', 'robots:txt', config.robots.txt, next);
-				} else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) {
-					db.setObjectField('config', 'robots:txt', config['robots.txt'], next);
-				} else {
-					next();
-				}
-			},
-			function (next) {
-				db.deleteObjectField('config', 'robots', next);
-			},
-			function (next) {
-				db.deleteObjectField('config', 'robots.txt', next);
-			},
-		], callback);
+	method: async function () {
+		const config = await db.getObject('config');
+		if (config) {
+			// fix mongo nested data
+			if (config.robots && config.robots.txt) {
+				await db.setObjectField('config', 'robots:txt', config.robots.txt);
+			} else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) {
+				await db.setObjectField('config', 'robots:txt', config['robots.txt']);
+			}
+			await db.deleteObjectField('config', 'robots');
+			await db.deleteObjectField('config', 'robots.txt');
+		}
 	},
 };
diff --git a/src/upgrades/1.7.1/notification-settings.js b/src/upgrades/1.7.1/notification-settings.js
index e7a455feb9..fed592effb 100644
--- a/src/upgrades/1.7.1/notification-settings.js
+++ b/src/upgrades/1.7.1/notification-settings.js
@@ -1,48 +1,31 @@
 'use strict';
 
-const async = require('async');
 const batch = require('../../batch');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Convert old notification digest settings',
 	timestamp: Date.UTC(2017, 10, 15),
-	method: function (callback) {
+	method: async function () {
 		const { progress } = this;
 
-		batch.processSortedSet('users:joindate', (uids, next) => {
-			async.eachLimit(uids, 500, (uid, next) => {
+		await batch.processSortedSet('users:joindate', async (uids) => {
+			await Promise.all(uids.map(async (uid) => {
 				progress.incr();
-				async.waterfall([
-					function (next) {
-						db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next);
-					},
-					function (userSettings, _next) {
-						if (!userSettings) {
-							return next();
-						}
-						const tasks = [];
-						if (parseInt(userSettings.sendChatNotifications, 10) === 1) {
-							tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail'));
-						}
-						if (parseInt(userSettings.sendPostNotifications, 10) === 1) {
-							tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail'));
-						}
-						if (!tasks.length) {
-							return next();
-						}
-
-						async.series(tasks, (err) => {
-							_next(err);
-						});
-					},
-					function (next) {
-						db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next);
-					},
-				], next);
-			}, next);
+				const userSettings = await db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']);
+				if (userSettings) {
+					if (parseInt(userSettings.sendChatNotifications, 10) === 1) {
+						await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail');
+					}
+					if (parseInt(userSettings.sendPostNotifications, 10) === 1) {
+						await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail');
+					}
+				}
+				await db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']);
+			}));
 		}, {
 			progress: progress,
-		}, callback);
+			batch: 500,
+		});
 	},
 };
diff --git a/src/upgrades/1.7.3/key_value_schema_change.js b/src/upgrades/1.7.3/key_value_schema_change.js
index 997db02551..0fb38f4c44 100644
--- a/src/upgrades/1.7.3/key_value_schema_change.js
+++ b/src/upgrades/1.7.3/key_value_schema_change.js
@@ -1,13 +1,13 @@
-'use strict';
+/* eslint-disable no-await-in-loop */
 
-const async = require('async');
+'use strict';
 
 const db = require('../../database');
 
 module.exports = {
 	name: 'Change the schema of simple keys so they don\'t use value field (mongodb only)',
 	timestamp: Date.UTC(2017, 11, 18),
-	method: function (callback) {
+	method: async function () {
 		let configJSON;
 		try {
 			configJSON = require('../../../config.json') || { [process.env.database]: true, database: process.env.database };
@@ -17,56 +17,29 @@ module.exports = {
 		const isMongo = configJSON.hasOwnProperty('mongo') && configJSON.database === 'mongo';
 		const { progress } = this;
 		if (!isMongo) {
-			return callback();
+			return;
 		}
 		const { client } = db;
-		let cursor;
-		async.waterfall([
-			function (next) {
-				client.collection('objects').countDocuments({
-					_key: { $exists: true },
-					value: { $exists: true },
-					score: { $exists: false },
-				}, next);
-			},
-			function (count, next) {
-				progress.total = count;
-				cursor = client.collection('objects').find({
-					_key: { $exists: true },
-					value: { $exists: true },
-					score: { $exists: false },
-				}).batchSize(1000);
+		const query = {
+			_key: { $exists: true },
+			value: { $exists: true },
+			score: { $exists: false },
+		};
+		progress.total = await client.collection('objects').countDocuments(query);
+		const cursor = await client.collection('objects').find(query).batchSize(1000);
 
-				let done = false;
-				async.whilst(
-					(next) => {
-						next(null, !done);
-					},
-					(next) => {
-						async.waterfall([
-							function (next) {
-								cursor.next(next);
-							},
-							function (item, next) {
-								progress.incr();
-								if (item === null) {
-									done = true;
-									return next();
-								}
-								delete item.expireAt;
-								if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) {
-									client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } }, next);
-								} else {
-									next();
-								}
-							},
-						], (err) => {
-							next(err);
-						});
-					},
-					next
-				);
-			},
-		], callback);
+		let done = false;
+		while (!done) {
+			const item = await cursor.next();
+			progress.incr();
+			if (item === null) {
+				done = true;
+			} else {
+				delete item.expireAt;
+				if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) {
+					await client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } });
+				}
+			}
+		}
 	},
 };
diff --git a/src/upgrades/1.7.3/topic_votes.js b/src/upgrades/1.7.3/topic_votes.js
index f7c5d0c122..008aaece0a 100644
--- a/src/upgrades/1.7.3/topic_votes.js
+++ b/src/upgrades/1.7.3/topic_votes.js
@@ -1,34 +1,22 @@
 'use strict';
 
-const async = require('async');
+
 const batch = require('../../batch');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Add votes to topics',
 	timestamp: Date.UTC(2017, 11, 8),
-	method: function (callback) {
+	method: async function () {
 		const { progress } = this;
 
-		batch.processSortedSet('topics:tid', (tids, next) => {
-			async.eachLimit(tids, 500, (tid, _next) => {
+		batch.processSortedSet('topics:tid', async (tids) => {
+			await Promise.all(tids.map(async (tid) => {
 				progress.incr();
-				let topicData;
-				async.waterfall([
-					function (next) {
-						db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned'], next);
-					},
-					function (_topicData, next) {
-						topicData = _topicData;
-						if (!topicData.mainPid || !topicData.cid) {
-							return _next();
-						}
-						db.getObject(`post:${topicData.mainPid}`, next);
-					},
-					function (postData, next) {
-						if (!postData) {
-							return _next();
-						}
+				const topicData = await db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned']);
+				if (topicData.mainPid && topicData.cid) {
+					const postData = await db.getObject(`post:${topicData.mainPid}`);
+					if (postData) {
 						const upvotes = parseInt(postData.upvotes, 10) || 0;
 						const downvotes = parseInt(postData.downvotes, 10) || 0;
 						const data = {
@@ -36,29 +24,19 @@ module.exports = {
 							downvotes: downvotes,
 						};
 						const votes = upvotes - downvotes;
-						async.parallel([
-							function (next) {
-								db.setObject(`topic:${tid}`, data, next);
-							},
-							function (next) {
-								db.sortedSetAdd('topics:votes', votes, tid, next);
-							},
-							function (next) {
-								if (parseInt(topicData.pinned, 10) !== 1) {
-									db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next);
-								} else {
-									next();
-								}
-							},
-						], (err) => {
-							next(err);
-						});
-					},
-				], _next);
-			}, next);
+						await Promise.all([
+							db.setObject(`topic:${tid}`, data),
+							db.sortedSetAdd('topics:votes', votes, tid),
+						]);
+						if (parseInt(topicData.pinned, 10) !== 1) {
+							await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid);
+						}
+					}
+				}
+			}));
 		}, {
 			progress: progress,
 			batch: 500,
-		}, callback);
+		});
 	},
 };
diff --git a/src/upgrades/1.7.4/fix_moved_topics_byvotes.js b/src/upgrades/1.7.4/fix_moved_topics_byvotes.js
index bb183557ef..33aafcb70d 100644
--- a/src/upgrades/1.7.4/fix_moved_topics_byvotes.js
+++ b/src/upgrades/1.7.4/fix_moved_topics_byvotes.js
@@ -1,53 +1,31 @@
 'use strict';
 
-const async = require('async');
 const batch = require('../../batch');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Fix sort by votes for moved topics',
 	timestamp: Date.UTC(2018, 0, 8),
-	method: function (callback) {
+	method: async function () {
 		const { progress } = this;
 
-		batch.processSortedSet('topics:tid', (tids, next) => {
-			async.eachLimit(tids, 500, (tid, _next) => {
+		await batch.processSortedSet('topics:tid', async (tids) => {
+			await Promise.all(tids.map(async (tid) => {
 				progress.incr();
-				let topicData;
-				async.waterfall([
-					function (next) {
-						db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned'], next);
-					},
-					function (_topicData, next) {
-						topicData = _topicData;
-						if (!topicData.cid || !topicData.oldCid) {
-							return _next();
-						}
-
-						const upvotes = parseInt(topicData.upvotes, 10) || 0;
-						const downvotes = parseInt(topicData.downvotes, 10) || 0;
-						const votes = upvotes - downvotes;
-
-						async.series([
-							function (next) {
-								db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid, next);
-							},
-							function (next) {
-								if (parseInt(topicData.pinned, 10) !== 1) {
-									db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next);
-								} else {
-									next();
-								}
-							},
-						], (err) => {
-							next(err);
-						});
-					},
-				], _next);
-			}, next);
+				const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned']);
+				if (topicData.cid && topicData.oldCid) {
+					const upvotes = parseInt(topicData.upvotes, 10) || 0;
+					const downvotes = parseInt(topicData.downvotes, 10) || 0;
+					const votes = upvotes - downvotes;
+					await db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid);
+					if (parseInt(topicData.pinned, 10) !== 1) {
+						await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid);
+					}
+				}
+			}));
 		}, {
 			progress: progress,
 			batch: 500,
-		}, callback);
+		});
 	},
 };
diff --git a/src/upgrades/1.7.4/fix_user_topics_per_category.js b/src/upgrades/1.7.4/fix_user_topics_per_category.js
index 73b6671864..5ee19fb41f 100644
--- a/src/upgrades/1.7.4/fix_user_topics_per_category.js
+++ b/src/upgrades/1.7.4/fix_user_topics_per_category.js
@@ -1,52 +1,29 @@
 'use strict';
 
-const async = require('async');
 const batch = require('../../batch');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Fix topics in categories per user if they were moved',
 	timestamp: Date.UTC(2018, 0, 22),
-	method: function (callback) {
+	method: async function () {
 		const { progress } = this;
 
-		batch.processSortedSet('topics:tid', (tids, next) => {
-			async.eachLimit(tids, 500, (tid, _next) => {
+		await batch.processSortedSet('topics:tid', async (tids) => {
+			await Promise.all(tids.map(async (tid) => {
 				progress.incr();
-				let topicData;
-				async.waterfall([
-					function (next) {
-						db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp'], next);
-					},
-					function (_topicData, next) {
-						topicData = _topicData;
-						if (!topicData.cid || !topicData.oldCid) {
-							return _next();
-						}
-
-						db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid, next);
-					},
-					function (isMember, next) {
-						if (isMember) {
-							async.series([
-								function (next) {
-									db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid, next);
-								},
-								function (next) {
-									db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid, next);
-								},
-							], (err) => {
-								next(err);
-							});
-						} else {
-							next();
-						}
-					},
-				], _next);
-			}, next);
+				const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp']);
+				if (topicData.cid && topicData.oldCid) {
+					const isMember = await db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid);
+					if (isMember) {
+						await db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid);
+						await db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid);
+					}
+				}
+			}));
 		}, {
 			progress: progress,
 			batch: 500,
-		}, callback);
+		});
 	},
 };
diff --git a/src/upgrades/1.7.6/flatten_navigation_data.js b/src/upgrades/1.7.6/flatten_navigation_data.js
index e5cb5932b8..96dc6408ae 100644
--- a/src/upgrades/1.7.6/flatten_navigation_data.js
+++ b/src/upgrades/1.7.6/flatten_navigation_data.js
@@ -1,38 +1,24 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Flatten navigation data',
 	timestamp: Date.UTC(2018, 1, 17),
-	method: function (callback) {
-		async.waterfall([
-			function (next) {
-				db.getSortedSetRangeWithScores('navigation:enabled', 0, -1, next);
-			},
-			function (data, next) {
-				const order = [];
-				const items = [];
-				data.forEach((item) => {
-					let navItem = JSON.parse(item.value);
-					const keys = Object.keys(navItem);
-					if (keys.length && parseInt(keys[0], 10) >= 0) {
-						navItem = navItem[keys[0]];
-					}
-					order.push(item.score);
-					items.push(JSON.stringify(navItem));
-				});
-
-				async.series([
-					function (next) {
-						db.delete('navigation:enabled', next);
-					},
-					function (next) {
-						db.sortedSetAdd('navigation:enabled', order, items, next);
-					},
-				], next);
-			},
-		], callback);
+	method: async function () {
+		const data = await db.getSortedSetRangeWithScores('navigation:enabled', 0, -1);
+		const order = [];
+		const items = [];
+		data.forEach((item) => {
+			let navItem = JSON.parse(item.value);
+			const keys = Object.keys(navItem);
+			if (keys.length && parseInt(keys[0], 10) >= 0) {
+				navItem = navItem[keys[0]];
+			}
+			order.push(item.score);
+			items.push(JSON.stringify(navItem));
+		});
+		await db.delete('navigation:enabled');
+		await db.sortedSetAdd('navigation:enabled', order, items);
 	},
 };
diff --git a/src/upgrades/1.7.6/notification_types.js b/src/upgrades/1.7.6/notification_types.js
index 8d022a157b..42d59cdd38 100644
--- a/src/upgrades/1.7.6/notification_types.js
+++ b/src/upgrades/1.7.6/notification_types.js
@@ -1,28 +1,21 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
 module.exports = {
 	name: 'Add default settings for notification delivery types',
 	timestamp: Date.UTC(2018, 1, 14),
-	method: function (callback) {
-		async.waterfall([
-			function (next) {
-				db.getObject('config', next);
-			},
-			function (config, next) {
-				const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none';
-				const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none';
-				db.setObject('config', {
-					notificationType_upvote: config.notificationType_upvote || 'notification',
-					'notificationType_new-topic': config['notificationType_new-topic'] || 'notification',
-					'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications,
-					notificationType_follow: config.notificationType_follow || 'notification',
-					'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications,
-					'notificationType_group-invite': config['notificationType_group-invite'] || 'notification',
-				}, next);
-			},
-		], callback);
+	method: async function () {
+		const config = await db.getObject('config');
+		const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none';
+		const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none';
+		await db.setObject('config', {
+			notificationType_upvote: config.notificationType_upvote || 'notification',
+			'notificationType_new-topic': config['notificationType_new-topic'] || 'notification',
+			'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications,
+			notificationType_follow: config.notificationType_follow || 'notification',
+			'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications,
+			'notificationType_group-invite': config['notificationType_group-invite'] || 'notification',
+		});
 	},
 };
diff --git a/src/upgrades/1.7.6/update_min_pass_strength.js b/src/upgrades/1.7.6/update_min_pass_strength.js
index 5afdb4f3ec..b207e0c166 100644
--- a/src/upgrades/1.7.6/update_min_pass_strength.js
+++ b/src/upgrades/1.7.6/update_min_pass_strength.js
@@ -1,22 +1,14 @@
 'use strict';
 
-const async = require('async');
 const db = require('../../database');
 
-
 module.exports = {
 	name: 'Revising minimum password strength to 1 (from 0)',
 	timestamp: Date.UTC(2018, 1, 21),
-	method: function (callback) {
-		async.waterfall([
-			async.apply(db.getObjectField.bind(db), 'config', 'minimumPasswordStrength'),
-			function (strength, next) {
-				if (!strength) {
-					return db.setObjectField('config', 'minimumPasswordStrength', 1, next);
-				}
-
-				setImmediate(next);
-			},
-		], callback);
+	method: async function () {
+		const strength = await db.getObjectField('config', 'minimumPasswordStrength');
+		if (!strength) {
+			await db.setObjectField('config', 'minimumPasswordStrength', 1);
+		}
 	},
 };
diff --git a/src/user/delete.js b/src/user/delete.js
index 8ee7e99146..1643d4643a 100644
--- a/src/user/delete.js
+++ b/src/user/delete.js
@@ -105,7 +105,7 @@ module.exports = function (User) {
 			throw new Error('[[error:no-user]]');
 		}
 
-		await plugins.hooks.fire('static:user.delete', { uid: uid });
+		await plugins.hooks.fire('static:user.delete', { uid: uid, userData: userData });
 		await deleteVotes(uid);
 		await deleteChats(uid);
 		await User.auth.revokeAllSessions(uid);
diff --git a/src/user/interstitials.js b/src/user/interstitials.js
index 3a98b12373..4eee0df11f 100644
--- a/src/user/interstitials.js
+++ b/src/user/interstitials.js
@@ -68,6 +68,10 @@ Interstitials.email = async (data) => {
 					}
 				}
 			} else {
+				if (meta.config.requireEmailAddress && !(formData.email && formData.email.length)) {
+					throw new Error('[[error:invalid-email]]');
+				}
+
 				// New registrants have the confirm email sent from user.create()
 				userData.email = formData.email;
 			}
diff --git a/src/views/admin/settings/notifications.tpl b/src/views/admin/settings/notifications.tpl
index 3e68295699..7fc1934832 100644
--- a/src/views/admin/settings/notifications.tpl
+++ b/src/views/admin/settings/notifications.tpl
@@ -7,6 +7,7 @@
 			<strong>[[admin/settings/notifications:welcome-notification]]</strong><br /> <textarea class="form-control" data-field="welcomeNotification"></textarea><br />
 			<strong>[[admin/settings/notifications:welcome-notification-link]]</strong><br /> <input type="text" class="form-control" data-field="welcomeLink"><br />
 			<strong>[[admin/settings/notifications:welcome-notification-uid]]</strong><br /> <input type="text" class="form-control" data-field="welcomeUid"><br />
+			<strong>[[admin/settings/notifications:post-queue-notification-uid]]</strong><br /> <input type="text" class="form-control" data-field="postQueueNotificationUid"><br />
 		</form>
 	</div>
 </div>
diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl
index bc9f5026ee..b010065321 100644
--- a/src/views/admin/settings/post.tpl
+++ b/src/views/admin/settings/post.tpl
@@ -294,6 +294,21 @@
 	</div>
 </div>
 
+<div class="row">
+	<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/post:backlinks]]</div>
+	<div class="col-sm-10 col-xs-12">
+		<form>
+			<div class="checkbox">
+				<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
+					<input class="mdl-switch__input" type="checkbox" data-field="topicBacklinks">
+					<span class="mdl-switch__label"><strong>[[admin/settings/post:backlinks.enabled]]</strong></span>
+					<p class="help-block">[[admin/settings/post:backlinks.help]]</p>
+				</label>
+			</div>
+		</form>
+	</div>
+</div>
+
 <div class="row">
 	<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/post:ip-tracking]]</div>
 	<div class="col-sm-10 col-xs-12">
diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl
index 1bfc772737..63308932b6 100644
--- a/src/views/admin/settings/user.tpl
+++ b/src/views/admin/settings/user.tpl
@@ -198,6 +198,15 @@
 					<span class="mdl-switch__label"><strong>[[admin/settings/user:registration-queue-show-average-time]]</strong></span>
 				</label>
 			</div>
+
+			<div class="checkbox">
+				<label for="requireEmailAddress" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
+					<input class="mdl-switch__input" type="checkbox" id="requireEmailAddress" data-field="requireEmailAddress" name="requireEmailAddress" />
+					<span class="mdl-switch__label">[[admin/settings/email:require-email-address]]</span>
+				</label>
+			</div>
+			<p class="help-block">[[admin/settings/email:require-email-address-warning]]</p>
+
 			<div class="form-group">
 				<label>[[admin/settings/user:max-invites]]</label>
 				<input type="number" class="form-control" data-field="maximumInvites" placeholder="0">
diff --git a/test/controllers-admin.js b/test/controllers-admin.js
index 3e720094f2..70538e522d 100644
--- a/test/controllers-admin.js
+++ b/test/controllers-admin.js
@@ -1,7 +1,7 @@
 'use strict';
 
 const async = require('async');
-const	assert = require('assert');
+const assert = require('assert');
 const nconf = require('nconf');
 const request = require('request');
 
@@ -719,4 +719,57 @@ describe('Admin Controllers', () => {
 			});
 		});
 	});
+
+	describe('admin page privileges', () => {
+		let userJar;
+		let uid;
+		const privileges = require('../src/privileges');
+		before((done) => {
+			user.create({ username: 'regularjoe', password: 'barbar' }, (err, _uid) => {
+				assert.ifError(err);
+				uid = _uid;
+				helpers.loginUser('regularjoe', 'barbar', (err, _jar) => {
+					assert.ifError(err);
+					userJar = _jar;
+					done();
+				});
+			});
+		});
+
+		it('should allow normal user access to admin pages', async () => {
+			function makeRequest(url) {
+				return new Promise((resolve, reject) => {
+					request(url, { jar: userJar, json: true }, (err, res, body) => {
+						if (err) reject(err);
+						else resolve(res);
+					});
+				});
+			}
+			for (const route of Object.keys(privileges.admin.routeMap)) {
+				/* eslint-disable no-await-in-loop */
+				await privileges.admin.rescind([privileges.admin.routeMap[route]], uid);
+				let res = await makeRequest(`${nconf.get('url')}/api/admin/${route}`);
+				assert.strictEqual(res.statusCode, 403);
+
+				await privileges.admin.give([privileges.admin.routeMap[route]], uid);
+				res = await makeRequest(`${nconf.get('url')}/api/admin/${route}`);
+				assert.strictEqual(res.statusCode, 200);
+
+				await privileges.admin.rescind([privileges.admin.routeMap[route]], uid);
+			}
+
+			for (const route of Object.keys(privileges.admin.routeMap)) {
+				/* eslint-disable no-await-in-loop */
+				await privileges.admin.rescind([privileges.admin.routeMap[route]], uid);
+				let res = await makeRequest(`${nconf.get('url')}/api/admin`);
+				assert.strictEqual(res.statusCode, 403);
+
+				await privileges.admin.give([privileges.admin.routeMap[route]], uid);
+				res = await makeRequest(`${nconf.get('url')}/api/admin`);
+				assert.strictEqual(res.statusCode, 200);
+
+				await privileges.admin.rescind([privileges.admin.routeMap[route]], uid);
+			}
+		});
+	});
 });
diff --git a/test/controllers.js b/test/controllers.js
index 5696ea7073..e95840bf7b 100644
--- a/test/controllers.js
+++ b/test/controllers.js
@@ -302,16 +302,6 @@ describe('Controllers', () => {
 	});
 
 	it('should load /register/complete', (done) => {
-		function hookMethod(data, next) {
-			data.interstitials.push({ template: 'topic.tpl', data: {} });
-			next(null, data);
-		}
-
-		plugins.hooks.register('myTestPlugin', {
-			hook: 'filter:register.interstitial',
-			method: hookMethod,
-		});
-
 		const data = {
 			username: 'interstitial',
 			password: '123456',
@@ -347,13 +337,86 @@ describe('Controllers', () => {
 					assert(body.sections);
 					assert(body.errors);
 					assert(body.title);
-					plugins.hooks.unregister('myTestPlugin', 'filter:register.interstitial', hookMethod);
 					done();
 				});
 			});
 		});
 	});
 
+	describe('registration interstitials', () => {
+		let jar;
+		let token;
+
+		it('email interstitial should still apply if empty email entered and requireEmailAddress is enabled', async () => {
+			meta.config.requireEmailAddress = 1;
+
+			jar = await helpers.registerUser({
+				username: 'testEmailReg',
+				password: 'asdasd',
+			});
+			token = await helpers.getCsrfToken(jar);
+
+			let res = await requestAsync(`${nconf.get('url')}/register/complete`, {
+				method: 'post',
+				jar,
+				json: true,
+				followRedirect: false,
+				simple: false,
+				resolveWithFullResponse: true,
+				headers: {
+					'x-csrf-token': token,
+				},
+				form: {
+					email: '',
+				},
+			});
+
+			assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/register/complete`);
+
+			res = await requestAsync(`${nconf.get('url')}/api/register/complete`, {
+				jar,
+				json: true,
+				resolveWithFullResponse: true,
+			});
+
+			assert(res.body.errors.includes('[[error:invalid-email]]'));
+		});
+
+		it('gdpr interstitial should still apply if email requirement is disabled', async () => {
+			meta.config.requireEmailAddress = 0;
+
+			const res = await requestAsync(`${nconf.get('url')}/api/register/complete`, {
+				jar,
+				json: true,
+				resolveWithFullResponse: true,
+			});
+
+			assert(!res.body.errors.includes('[[error:invalid-email]]'));
+			assert(!res.body.errors.includes('[[error:gdpr_consent_denied]]'));
+		});
+
+		it('registration should succeed once gdpr prompts are agreed to', async () => {
+			const res = await requestAsync(`${nconf.get('url')}/register/complete`, {
+				method: 'post',
+				jar,
+				json: true,
+				followRedirect: false,
+				simple: false,
+				resolveWithFullResponse: true,
+				headers: {
+					'x-csrf-token': token,
+				},
+				form: {
+					gdpr_agree_data: 'on',
+					gdpr_agree_email: 'on',
+				},
+			});
+
+			assert.strictEqual(res.statusCode, 302);
+			assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/`);
+		});
+	});
+
 	it('should load /robots.txt', (done) => {
 		request(`${nconf.get('url')}/robots.txt`, (err, res, body) => {
 			assert.ifError(err);
diff --git a/test/database/hash.js b/test/database/hash.js
index 650afae8bf..e2e2ee7364 100644
--- a/test/database/hash.js
+++ b/test/database/hash.js
@@ -90,6 +90,13 @@ describe('Hash methods', () => {
 			assert.deepStrictEqual(result, [{ foo: '1' }, null]);
 		});
 
+		it('should update existing object on second call', async () => {
+			await db.setObjectBulk(['bulkKey3.5'], [{ foo: '1' }]);
+			await db.setObjectBulk(['bulkKey3.5'], [{ baz: '2' }]);
+			const result = await db.getObject('bulkKey3.5');
+			assert.deepStrictEqual(result, { foo: '1', baz: '2' });
+		});
+
 		it('should not error if object is empty', async () => {
 			const keys = ['bulkKey5'];
 			const data = [{ }];
diff --git a/test/database/sorted.js b/test/database/sorted.js
index 8b348961ee..ede6cc8c5d 100644
--- a/test/database/sorted.js
+++ b/test/database/sorted.js
@@ -1119,6 +1119,29 @@ describe('Sorted Set methods', () => {
 			const members = await db.getSortedSetsMembers(['bulkRemove1', 'bulkRemove2']);
 			assert.deepStrictEqual(members, [[], []]);
 		});
+
+		it('should not remove wrong elements in bulk remove', async () => {
+			await db.sortedSetAddBulk([
+				['bulkRemove4', 1, 'value1'],
+				['bulkRemove4', 2, 'value2'],
+				['bulkRemove4', 3, 'value4'],
+				['bulkRemove5', 1, 'value1'],
+				['bulkRemove5', 2, 'value2'],
+				['bulkRemove5', 3, 'value3'],
+			]);
+			await db.sortedSetRemoveBulk([
+				['bulkRemove4', 'value1'],
+				['bulkRemove4', 'value3'],
+				['bulkRemove5', 'value1'],
+				['bulkRemove5', 'value4'],
+			]);
+			const members = await Promise.all([
+				db.getSortedSetRange('bulkRemove4', 0, -1),
+				db.getSortedSetRange('bulkRemove5', 0, -1),
+			]);
+			assert.deepStrictEqual(members[0], ['value2', 'value4']);
+			assert.deepStrictEqual(members[1], ['value2', 'value3']);
+		});
 	});
 
 	describe('sortedSetsRemove()', () => {
diff --git a/test/files/toobig.jpg b/test/files/toobig.jpg
deleted file mode 100644
index e25c2d3015..0000000000
Binary files a/test/files/toobig.jpg and /dev/null differ
diff --git a/test/files/toobig.png b/test/files/toobig.png
new file mode 100644
index 0000000000..1d2d94d143
Binary files /dev/null and b/test/files/toobig.png differ
diff --git a/test/posts.js b/test/posts.js
index 200810ad89..20a7661da1 100644
--- a/test/posts.js
+++ b/test/posts.js
@@ -1426,4 +1426,111 @@ describe('Post\'s', () => {
 			});
 		});
 	});
+
+	describe('Topic Backlinks', () => {
+		let tid1;
+		before(async () => {
+			tid1 = await topics.post({
+				uid: 1,
+				cid,
+				title: 'Topic backlink testing - topic 1',
+				content: 'Some text here for the OP',
+			});
+			tid1 = tid1.topicData.tid;
+		});
+
+		describe('.syncBacklinks()', () => {
+			it('should error on invalid data', async () => {
+				try {
+					await topics.syncBacklinks();
+				} catch (e) {
+					assert(e);
+					assert.strictEqual(e.message, '[[error:invalid-data]]');
+				}
+			});
+
+			it('should do nothing if the post does not contain a link to a topic', async () => {
+				const backlinks = await topics.syncBacklinks({
+					content: 'This is a post\'s content',
+				});
+
+				assert.strictEqual(backlinks, 0);
+			});
+
+			it('should create a backlink if it detects a topic link in a post', async () => {
+				const count = await topics.syncBacklinks({
+					pid: 2,
+					content: `This is a link to [topic 1](${nconf.get('url')}/topic/1/abcdef)`,
+				});
+				const events = await topics.events.get(1, 1);
+				const backlinks = await db.getSortedSetMembers('pid:2:backlinks');
+
+				assert.strictEqual(count, 1);
+				assert(events);
+				assert.strictEqual(events.length, 1);
+				assert(backlinks);
+				assert(backlinks.includes('1'));
+			});
+
+			it('should remove the backlink (but keep the event) if the post no longer contains a link to a topic', async () => {
+				const count = await topics.syncBacklinks({
+					pid: 2,
+					content: 'This is a link to [nothing](http://example.org)',
+				});
+				const events = await topics.events.get(1, 1);
+				const backlinks = await db.getSortedSetMembers('pid:2:backlinks');
+
+				assert.strictEqual(count, 0);
+				assert(events);
+				assert.strictEqual(events.length, 1);
+				assert(backlinks);
+				assert.strictEqual(backlinks.length, 0);
+			});
+		});
+
+		describe('integration tests', () => {
+			it('should create a topic event in the referenced topic', async () => {
+				const topic = await topics.post({
+					uid: 1,
+					cid,
+					title: 'Topic backlink testing - topic 2',
+					content: `Some text here for the OP &ndash; ${nconf.get('url')}/topic/${tid1}`,
+				});
+
+				const events = await topics.events.get(tid1, 1);
+				assert(events);
+				assert.strictEqual(events.length, 1);
+				assert.strictEqual(events[0].type, 'backlink');
+				assert.strictEqual(parseInt(events[0].uid, 10), 1);
+				assert.strictEqual(events[0].href, `/post/${topic.postData.pid}`);
+			});
+
+			it('should not create a topic event if referenced topic is the same as current topic', async () => {
+				await topics.reply({
+					uid: 1,
+					tid: tid1,
+					content: `Referencing itself &ndash; ${nconf.get('url')}/topic/${tid1}`,
+				});
+
+				const events = await topics.events.get(tid1, 1);
+				assert(events);
+				assert.strictEqual(events.length, 1);	// should still equal 1
+			});
+
+			it('should not show backlink events if the feature is disabled', async () => {
+				meta.config.topicBacklinks = 0;
+
+				await topics.post({
+					uid: 1,
+					cid,
+					title: 'Topic backlink testing - topic 3',
+					content: `Some text here for the OP &ndash; ${nconf.get('url')}/topic/${tid1}`,
+				});
+
+				const events = await topics.events.get(tid1, 1);
+				assert(events);
+				assert.strictEqual(events.length, 0);
+			});
+		});
+	});
 });
diff --git a/test/socket.io.js b/test/socket.io.js
index 7135d5a3a9..b707de09a0 100644
--- a/test/socket.io.js
+++ b/test/socket.io.js
@@ -547,6 +547,22 @@ describe('socket.io', () => {
 		});
 	});
 
+	it('should not error when resending digests', async () => {
+		await socketAdmin.digest.resend({ uid: adminUid }, { action: 'resend-day', uid: adminUid });
+		await socketAdmin.digest.resend({ uid: adminUid }, { action: 'resend-day' });
+	});
+
+	it('should error with invalid interval', async () => {
+		const oldValue = meta.config.dailyDigestFreq;
+		meta.config.dailyDigestFreq = 'off';
+		try {
+			await socketAdmin.digest.resend({ uid: adminUid }, { action: 'resend-' });
+		} catch (err) {
+			assert.strictEqual(err.message, '[[error:digest-not-enabled]]');
+		}
+		meta.config.dailyDigestFreq = oldValue;
+	});
+
 	it('should get logs', (done) => {
 		const fs = require('fs');
 		const path = require('path');
@@ -706,4 +722,35 @@ describe('socket.io', () => {
 			});
 		});
 	});
+
+	it('should clear caches', async () => {
+		await socketAdmin.cache.clear({ uid: adminUid }, { name: 'post' });
+		await socketAdmin.cache.clear({ uid: adminUid }, { name: 'object' });
+		await socketAdmin.cache.clear({ uid: adminUid }, { name: 'group' });
+		await socketAdmin.cache.clear({ uid: adminUid }, { name: 'local' });
+	});
+
+	it('should toggle caches', async () => {
+		const caches = {
+			post: require('../src/posts/cache'),
+			object: require('../src/database').objectCache,
+			group: require('../src/groups').cache,
+			local: require('../src/cache'),
+		};
+
+		await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'post', enabled: !caches.post.enabled });
+		if (caches.object) {
+			await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'object', enabled: !caches.object.enabled });
+		}
+		await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'group', enabled: !caches.group.enabled });
+		await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'local', enabled: !caches.local.enabled });
+
+		// call again to return back to original state
+		await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'post', enabled: !caches.post.enabled });
+		if (caches.object) {
+			await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'object', enabled: !caches.object.enabled });
+		}
+		await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'group', enabled: !caches.group.enabled });
+		await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'local', enabled: !caches.local.enabled });
+	});
 });
diff --git a/test/uploads.js b/test/uploads.js
index 6fbf1e4f11..4def21ed17 100644
--- a/test/uploads.js
+++ b/test/uploads.js
@@ -196,11 +196,11 @@ describe('Upload Controllers', () => {
 		});
 
 		it('should fail to upload image to post if image dimensions are too big', (done) => {
-			helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.jpg'), {}, jar, csrf_token, (err, res, body) => {
+			helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token, (err, res, body) => {
 				assert.ifError(err);
 				assert.strictEqual(res.statusCode, 500);
 				assert(body && body.status && body.status.message);
-				assert.strictEqual(body.status.message, 'Input image exceeds pixel limit');
+				assert.strictEqual(body.status.message, 'Image dimensions are too big');
 				done();
 			});
 		});