diff --git a/app.js b/app.js
index b40ec938a4..9c202470ad 100644
--- a/app.js
+++ b/app.js
@@ -22,7 +22,6 @@
var nconf = require('nconf');
nconf.argv().env('__');
-require('continuation-local-storage');
var url = require('url'),
async = require('async'),
diff --git a/package.json b/package.json
index bdd717d604..cca388c6c2 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,6 @@
"connect-mongo": "~1.1.0",
"connect-multiparty": "^2.0.0",
"connect-redis": "~3.0.2",
- "continuation-local-storage": "^3.1.6",
"cookie-parser": "^1.3.3",
"cron": "^1.0.5",
"csurf": "^1.6.1",
@@ -48,18 +47,18 @@
"morgan": "^1.3.2",
"mousetrap": "^1.5.3",
"nconf": "~0.8.2",
- "nodebb-plugin-composer-default": "4.0.2",
+ "nodebb-plugin-composer-default": "4.0.5",
"nodebb-plugin-dbsearch": "1.0.2",
"nodebb-plugin-emoji-extended": "1.1.0",
"nodebb-plugin-emoji-one": "1.1.5",
- "nodebb-plugin-markdown": "5.1.6",
+ "nodebb-plugin-markdown": "6.0.0",
"nodebb-plugin-mentions": "1.1.2",
"nodebb-plugin-soundpack-default": "0.1.6",
- "nodebb-plugin-spam-be-gone": "0.4.6",
+ "nodebb-plugin-spam-be-gone": "0.4.8",
"nodebb-rewards-essentials": "0.0.8",
"nodebb-theme-lavender": "3.0.13",
- "nodebb-theme-persona": "4.0.148",
- "nodebb-theme-vanilla": "5.0.78",
+ "nodebb-theme-persona": "4.1.3",
+ "nodebb-theme-vanilla": "5.1.0",
"nodebb-widget-essentials": "2.0.9",
"nodemailer": "2.0.0",
"nodemailer-sendmail-transport": "1.0.0",
@@ -118,4 +117,4 @@
"url": "https://github.com/barisusakli"
}
]
-}
\ No newline at end of file
+}
diff --git a/public/language/cs/global.json b/public/language/cs/global.json
index 6fe0e8d32c..273eaffe68 100644
--- a/public/language/cs/global.json
+++ b/public/language/cs/global.json
@@ -23,7 +23,7 @@
"pagination.enter_index": "Enter index",
"header.admin": "Administrace",
"header.categories": "Kategorie",
- "header.recent": "Aktuality",
+ "header.recent": "Nejnovější",
"header.unread": "Nepřečtené",
"header.tags": "Tagy",
"header.popular": "Populární",
diff --git a/public/language/en_GB/global.json b/public/language/en_GB/global.json
index a7959aa349..226589b8af 100644
--- a/public/language/en_GB/global.json
+++ b/public/language/en_GB/global.json
@@ -63,7 +63,9 @@
"topics": "Topics",
"posts": "Posts",
"best": "Best",
+ "upvoters": "Upvoters",
"upvoted": "Upvoted",
+ "downvoters": "Downvoters",
"downvoted": "Downvoted",
"views": "Views",
"reputation": "Reputation",
diff --git a/public/language/en_GB/register.json b/public/language/en_GB/register.json
index dcbd4bb03a..81b20421d4 100644
--- a/public/language/en_GB/register.json
+++ b/public/language/en_GB/register.json
@@ -1,5 +1,6 @@
{
"register": "Register",
+ "cancel_registration": "Cancel Registration",
"help.email": "By default, your email will be hidden from the public.",
"help.username_restrictions": "A unique username between %1 and %2 characters. Others can mention you with @username.",
"help.minimum_password_length": "Your password's length must be at least %1 characters.",
@@ -15,5 +16,8 @@
"alternative_registration": "Alternative Registration",
"terms_of_use": "Terms of Use",
"agree_to_terms_of_use": "I agree to the Terms of Use",
- "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator."
+ "terms_of_use_error": "You must agree to the Terms of Use",
+ "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator.",
+ "interstitial.intro": "We require some additional information before we can create your account.",
+ "interstitial.errors-found": "We could not complete your registration:"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/email.json b/public/language/fa_IR/email.json
index 05b628ed9f..de1a046499 100644
--- a/public/language/fa_IR/email.json
+++ b/public/language/fa_IR/email.json
@@ -24,7 +24,7 @@
"digest.day": "روز",
"digest.week": "هفته",
"digest.month": "ماه",
- "digest.subject": "Digest for %1",
+ "digest.subject": "خلاصه برای %1",
"notif.chat.subject": "پیام چتی جدیدی از %1 دریافت شد",
"notif.chat.cta": "برای ادامهی چت اینجا کلیک کنید",
"notif.chat.unsub.info": "این اطلاعیه ی چتیی که برای شما فرستاده شده به علت تنظیمات اشترک شماست.",
diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json
index d134ea17d4..a5afb7d656 100644
--- a/public/language/fa_IR/error.json
+++ b/public/language/fa_IR/error.json
@@ -58,9 +58,9 @@
"too-many-tags": "تعداد برچسب ها بیشتر از حد مجاز است. موضوع ها نمی توانند بیشتر از %1 برچسب داشته باشند",
"still-uploading": "خواهشمندیم تا پایان بارگذاریها شکیبا باشید.",
"file-too-big": "حداکثر مجاز حجم فایل %1 کیلوبایت می باشد - لطفا فایلی با حجم کمتر بارگذاری کنید",
- "guest-upload-disabled": "Guest uploading has been disabled",
- "already-favourited": "You have already bookmarked this post",
- "already-unfavourited": "You have already unbookmarked this post",
+ "guest-upload-disabled": "بارگذاری برای مهمانان غیر فعال شده است",
+ "already-favourited": "شما در حال حاضر این پست را به علاقمندی های خود اضافه کرده ایید",
+ "already-unfavourited": "شما در حال حاضر این پست را از لیست علاقمندی خود خارج کردید",
"cant-ban-other-admins": "شما نمیتوانید دیگر مدیران را محروم کنید!",
"cant-remove-last-admin": "شما تنها مدیر می باشید . شما باید قبل از عزل خود از مدیریت یک کاربر دیگر را مدیر کنید",
"cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.",
@@ -87,14 +87,14 @@
"about-me-too-long": "با عرض پوزش محتوای 'درباره ی من' نمی تواند طولانی تر از %1 کاراکتر باشد",
"cant-chat-with-yourself": "شما نمیتوانید با خودتان چت کنید!",
"chat-restricted": "این کاربر پیام های چتی خود را محدود کرده است . آنها بایدشما را دنبال کنند تا اینکه شما بتوانید به آنها پیامی بفرستید",
- "chat-disabled": "Chat system disabled",
+ "chat-disabled": "سیستم گفتمان غیرفعال شده است",
"too-many-messages": "شما پیامهای خیلی زیادی فرستاده اید، لطفا مدتی صبر نمایید",
"invalid-chat-message": "پیام نامعتبر",
"chat-message-too-long": "پیام طولانی تر از حد مجاز است",
"cant-edit-chat-message": "شما اجازه ی ویرایش این پیام را ندارید",
"cant-remove-last-user": "You can't remove the last user",
- "cant-delete-chat-message": "You are not allowed to delete this message",
- "already-voting-for-this-post": "You have already voted for this post.",
+ "cant-delete-chat-message": "شما اجازه حذف این پیام را ندارید.",
+ "already-voting-for-this-post": "شما قبلا به این پست رای داده اید.",
"reputation-system-disabled": "سیستم اعتبار غیر فعال شده است",
"downvoting-disabled": "رأی منفی غیر فعال شده است",
"not-enough-reputation-to-downvote": "شما اعتبار کافی برای دادن رأی منفی به این پست را ندارید.",
@@ -107,7 +107,7 @@
"wrong-login-type-username": "لطفا از نام کاربری خود برای ورود استفاده کنید",
"invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2).",
"no-session-found": "No login session found!",
- "not-in-room": "User not in room",
+ "not-in-room": "هیچ کاربری در این گفتگو نیست",
"no-users-in-room": "هیچ کاربری در این گفتگو نیست",
- "cant-kick-self": "You can't kick yourself from the group"
+ "cant-kick-self": "شما نمی توانید خودتان را از گروه کیک کنید"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/global.json b/public/language/fa_IR/global.json
index 948145c589..f1926e1bd8 100644
--- a/public/language/fa_IR/global.json
+++ b/public/language/fa_IR/global.json
@@ -49,7 +49,7 @@
"users": "کاربران",
"topics": "موضوع ها",
"posts": "دیدگاهها",
- "best": "Best",
+ "best": "بهترین",
"upvoted": "Upvoted",
"downvoted": "Downvoted",
"views": "بازدیدها",
@@ -65,7 +65,7 @@
"posted_in_ago_by": "ارسال شده در %1 %2 توسط %3",
"user_posted_ago": "%1 در %2 ارسال کرده است",
"guest_posted_ago": "مهمان در %1 ارسال کرده است",
- "last_edited_by": "last edited by %1",
+ "last_edited_by": "آخرین ویرایش توسط %1 انجام شده",
"norecentposts": "هیچ دیدگاه تازهای نیست",
"norecenttopics": "هیچ جستار تازهای نیست",
"recentposts": "دیدگاههای تازه",
@@ -85,10 +85,10 @@
"unfollow": "دنبال نکن",
"delete_all": "حذف همه",
"map": "نقشه",
- "sessions": "Login Sessions",
- "ip_address": "IP Address",
- "enter_page_number": "Enter page number",
- "upload_file": "Upload file",
- "upload": "Upload",
- "allowed-file-types": "Allowed file types are %1"
+ "sessions": "Session های ورود",
+ "ip_address": "آدرس آی پی",
+ "enter_page_number": "شماره صفحه را وارد کنید",
+ "upload_file": "بارگذاری فایل",
+ "upload": "بارگذاری",
+ "allowed-file-types": "فایل قابل قبول اینها هستند %1"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json
index 2c1db5b30d..d439e04fbf 100644
--- a/public/language/fa_IR/groups.json
+++ b/public/language/fa_IR/groups.json
@@ -41,7 +41,7 @@
"details.hidden": "پنهان",
"details.hidden_help": "اگر فعال باشد، این گروه در فهرست گروهها پیدا نمیشود و کاربران باید دستی فراخوانده شوند",
"details.delete_group": "حذف گروه",
- "details.private_system_help": "Private groups is disabled at system level, this option does not do anything",
+ "details.private_system_help": "گروه های خصوصی در این سطح سیستم غیر فعال هستند، این گزینه هیچ کاری انجام نمی دهد",
"event.updated": "جزییات گروه با موفقیت به روز شد",
"event.deleted": "گروه \"%1\" حدف شد",
"membership.accept-invitation": "دعوت را قبول میکنم",
diff --git a/public/language/fa_IR/login.json b/public/language/fa_IR/login.json
index ea5e13bce4..a6c3e72fa8 100644
--- a/public/language/fa_IR/login.json
+++ b/public/language/fa_IR/login.json
@@ -5,7 +5,7 @@
"remember_me": "مرا به یاد بسپار؟",
"forgot_password": "گذرواژه را فراموش کردهاید؟",
"alternative_logins": "روشهای درون آمدن جایگزین",
- "failed_login_attempt": "Login Unsuccessful",
+ "failed_login_attempt": "ورود ناموفق",
"login_successful": "شما با موفقیت به درون آمدهاید!",
"dont_have_account": "حساب کاربری ندارید؟"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/modules.json b/public/language/fa_IR/modules.json
index c60817f334..58e75bf025 100644
--- a/public/language/fa_IR/modules.json
+++ b/public/language/fa_IR/modules.json
@@ -6,7 +6,7 @@
"chat.user_typing": "%1 در حال نوشتن است...",
"chat.user_has_messaged_you": "%1 به شما پیام داده است.",
"chat.see_all": "دیدن همه ی چت ها",
- "chat.mark_all_read": "Mark all chats read",
+ "chat.mark_all_read": "علامت گذاری شده به عنوان تمام چت ها را خوانده",
"chat.no-messages": "مشخص کنید تاریخچه چتهایتان با چه کاربری را میخواهید ببینید",
"chat.no-users-in-room": "هیچ کاربری در این گفتگو نیست",
"chat.recent-chats": "چتهای اخیر",
@@ -18,8 +18,8 @@
"chat.thirty_days": "30 روز",
"chat.three_months": "3 ماه",
"chat.delete_message_confirm": "آیا مطمئن هستید که می خواهید این پیام را حذف کنید؟",
- "chat.roomname": "Chat Room %1",
- "chat.add-users-to-room": "Add users to room",
+ "chat.roomname": "اتاق گفتگو %1",
+ "chat.add-users-to-room": "اضافه کردن کاربر به این گفتگو",
"composer.compose": "ارسال",
"composer.show_preview": "نمایش پیشنمایش",
"composer.hide_preview": "مخفی کردن پیشنمایش",
@@ -29,14 +29,14 @@
"composer.submit_and_lock": "ارسال و قفل",
"composer.toggle_dropdown": "باز و بسته کردن کرکره",
"composer.uploading": "در حال بارگذاری %1",
- "composer.formatting.bold": "Bold",
- "composer.formatting.italic": "Italic",
- "composer.formatting.list": "List",
- "composer.formatting.strikethrough": "Strikethrough",
- "composer.formatting.link": "Link",
- "composer.formatting.picture": "Picture",
- "composer.upload-picture": "Upload Image",
- "composer.upload-file": "Upload File",
+ "composer.formatting.bold": "توپر",
+ "composer.formatting.italic": "کج",
+ "composer.formatting.list": "فهرست",
+ "composer.formatting.strikethrough": "خط خورده",
+ "composer.formatting.link": "پیوند",
+ "composer.formatting.picture": "عکس",
+ "composer.upload-picture": "بارگذاری عکس",
+ "composer.upload-file": "بارگذاری فایل",
"bootbox.ok": "باشه",
"bootbox.cancel": "لغو",
"bootbox.confirm": "تایید",
diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json
index ee4641d006..9729f68850 100644
--- a/public/language/fa_IR/notifications.json
+++ b/public/language/fa_IR/notifications.json
@@ -5,7 +5,7 @@
"mark_all_read": "همه اطلاعیه ها را خوانده شده علامت بزن",
"back_to_home": "بازگشت به %1",
"outgoing_link": "پیوند برونرو",
- "outgoing_link_message": "You are now leaving %1",
+ "outgoing_link_message": "شما در حال ترک %1 هستید",
"continue_to": "ادامه به %1",
"return_to": "بازگشت به %1",
"new_notification": "آکاهسازی تازه",
diff --git a/public/language/fa_IR/pages.json b/public/language/fa_IR/pages.json
index 06d6c1ec35..ffc6768d62 100644
--- a/public/language/fa_IR/pages.json
+++ b/public/language/fa_IR/pages.json
@@ -11,7 +11,7 @@
"users/latest": "آخرین کاربران",
"users/sort-posts": "کاربران با بیشترین پست",
"users/sort-reputation": "کاربران دارای بیشترین اعتبار",
- "users/banned": "Banned Users",
+ "users/banned": "کاربران اخراج شده",
"users/search": "جستجوی کاربر",
"notifications": "آگاهسازیها",
"tags": "برچسبها",
@@ -39,7 +39,7 @@
"account/upvoted": "Posts upvoted by %1",
"account/downvoted": "Posts downvoted by %1",
"account/best": "Best posts made by %1",
- "confirm": "Email Confirmed",
+ "confirm": "ایمیل تایید شد",
"maintenance.text": "%1 در حال حاضر تحت تعمیر و نگهدارییست. لطفا زمان دیگری مراجعه کنید.",
"maintenance.messageIntro": "علاوه بر این، مدیر این پیام را گذاشته است:",
"throttled.text": "%1 به دلیل بارگذاری بیش از حد ، قابل دسترس نمی باشد. لطفا در زمان دیگری دوباره امتحان کنید"
diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json
index 3da49da432..9383c3403d 100644
--- a/public/language/fa_IR/topic.json
+++ b/public/language/fa_IR/topic.json
@@ -13,7 +13,7 @@
"notify_me": "از پاسخهای تازه در موضوع آگاه شوید",
"quote": "نقل قول",
"reply": "پاسخ",
- "reply-as-topic": "Reply as topic",
+ "reply-as-topic": "پاسخ به موضوع",
"guest-login-reply": "وارد شوید تا پست بفرستید",
"edit": "ویرایش",
"delete": "حذف",
@@ -58,7 +58,7 @@
"thread_tools.move_all": "جابجایی همه",
"thread_tools.fork": "شاخه ساختن از موضوع",
"thread_tools.delete": "پاک کردن موضوع",
- "thread_tools.delete-posts": "Delete Posts",
+ "thread_tools.delete-posts": "حذف پست ها",
"thread_tools.delete_confirm": "آیا مطمئنید می خواهید این موضوع را حذف کنید؟",
"thread_tools.restore": "برگرداندن موضوع",
"thread_tools.restore_confirm": "آیا مطمئنید که می خواهید این موضوع را بازگردانی کنید؟",
@@ -73,7 +73,7 @@
"confirm_move": "جابهجا کردن",
"confirm_fork": "شاخه ساختن",
"favourite": "Bookmark",
- "favourites": "Bookmarks",
+ "favourites": "علاقمندی ها",
"favourites.has_no_favourites": "You haven't bookmarked any posts yet.",
"loading_more_posts": "بارگذاری پستهای بیشتر",
"move_topic": "جابهجایی موضوع",
@@ -112,7 +112,7 @@
"stale.warning": "موضوعی که شما در حال پاسخگویی به آن هستید قدیمی می باشد. آیا میلید به جای آن یک موضوع جدید ایجاد کنید و در آن به این موضوع ارجاع دهید؟",
"stale.create": "ایجاد یک موضوع جدید",
"stale.reply_anyway": "در هر صورت می خواهم به این موضوع پاسخ دهم",
- "link_back": "Re: [%1](%2)",
+ "link_back": "پاسخ: [%1](%2)",
"spam": "اسپم",
"offensive": "توهین آمیز",
"custom-flag-reason": "وارد کردن دلیل پرچمگذاری"
diff --git a/public/language/fa_IR/unread.json b/public/language/fa_IR/unread.json
index 2109d00211..b99597020c 100644
--- a/public/language/fa_IR/unread.json
+++ b/public/language/fa_IR/unread.json
@@ -7,7 +7,7 @@
"all": "همه",
"all_categories": "تمام دسته ها",
"topics_marked_as_read.success": "همه موضوع ها خوانده شدند",
- "all-topics": "All Topics",
- "new-topics": "New Topics",
- "watched-topics": "Watched Topics"
+ "all-topics": "همه موضوع ها",
+ "new-topics": "موضوع های جدید",
+ "watched-topics": "موضوع های پیگیری شده"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/uploads.json b/public/language/fa_IR/uploads.json
index 1622cb5693..8cf6622f5b 100644
--- a/public/language/fa_IR/uploads.json
+++ b/public/language/fa_IR/uploads.json
@@ -1,6 +1,6 @@
{
- "uploading-file": "Uploading the file...",
- "select-file-to-upload": "Select a file to upload!",
- "upload-success": "File uploaded successfully!",
- "maximum-file-size": "Maximum %1 kb"
+ "uploading-file": "در حال بارگذاری فایل...",
+ "select-file-to-upload": "فایل مورد نظر را برای بارگذاری انتخاب کنید!",
+ "upload-success": "فایل با موفقیت بارگذاری شد!",
+ "maximum-file-size": "حداکثر %1 کیلوبایت"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json
index 4b4fcbaf74..d25b6195b4 100644
--- a/public/language/fa_IR/user.json
+++ b/public/language/fa_IR/user.json
@@ -22,7 +22,7 @@
"profile": "پروفایل",
"profile_views": "بازدیدهای نمایه",
"reputation": "اعتبار",
- "favourites": "Bookmarks",
+ "favourites": "علاقمندی ها",
"watched": "پیگیری شده",
"followers": "دنبالکنندهها",
"following": "دنبالشوندهها",
@@ -39,7 +39,7 @@
"change_username": "تغییر نام کاربری",
"change_email": "تغییر ایمیل",
"edit": "ویرایش",
- "edit-profile": "Edit Profile",
+ "edit-profile": "ویرایش پروفایل",
"default_picture": "آیکون پیش فرض",
"uploaded_picture": "تصویر بارشده",
"upload_new_picture": "بارگذاری تصویر تازه",
@@ -56,11 +56,11 @@
"password": "گذرواژه",
"username_taken_workaround": "نام کاربری درخواستی شما در حال حاضر گرفته شده است، بنابراین ما آن را کمی تغییر دادهایم. شما هماکنون با نام %1 1) {
- newUrl += '/' + index;
+ if (navigator.scrollActive) {
+ return;
+ }
+
+ posts.loadImages(threshold);
+
+ var newUrl = 'topic/' + ajaxify.data.slug + (index > 1 ? ('/' + index) : '');
+
+ if (newUrl !== currentUrl) {
+
+ if (Topic.replaceURLTimeout) {
+ clearTimeout(Topic.replaceURLTimeout);
}
- posts.loadImages(threshold);
+ Topic.replaceURLTimeout = setTimeout(function() {
- if (newUrl !== currentUrl) {
- if (Topic.replaceURLTimeout) {
- clearTimeout(Topic.replaceURLTimeout);
+ if (index >= elementCount && app.user.uid) {
+ socket.emit('topics.markAsRead', [ajaxify.data.tid]);
}
- Topic.replaceURLTimeout = setTimeout(function() {
- updateUserBookmark(index);
- Topic.replaceURLTimeout = 0;
- if (history.replaceState) {
- var search = (window.location.search ? window.location.search : '');
- history.replaceState({
- url: newUrl + search
- }, null, window.location.protocol + '//' + window.location.host + RELATIVE_PATH + '/' + newUrl + search);
- }
- currentUrl = newUrl;
- }, 500);
- }
+ updateUserBookmark(index);
+
+ Topic.replaceURLTimeout = 0;
+ if (history.replaceState) {
+ var search = (window.location.search ? window.location.search : '');
+ history.replaceState({
+ url: newUrl + search
+ }, null, window.location.protocol + '//' + window.location.host + RELATIVE_PATH + '/' + newUrl + search);
+ }
+ currentUrl = newUrl;
+ }, 500);
}
};
diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js
index ff6016c5ab..7136c32c27 100644
--- a/public/src/client/topic/postTools.js
+++ b/public/src/client/topic/postTools.js
@@ -226,41 +226,39 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
}
function onReplyClicked(button, tid) {
- showStaleWarning(function(proceed) {
- if (!proceed) {
- var selectedText = getSelectedText(button);
+ showStaleWarning(function() {
+ var selectedText = getSelectedText(button);
- var username = getUserName(button);
- if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) {
- username = '';
- }
+ var username = getUserName(button);
+ if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) {
+ username = '';
+ }
- var toPid = button.is('[component="post/reply"]') ? getData(button, 'data-pid') : null;
+ var toPid = button.is('[component="post/reply"]') ? getData(button, 'data-pid') : null;
- if (selectedText) {
- $(window).trigger('action:composer.addQuote', {
- tid: tid,
- slug: ajaxify.data.slug,
- index: getData(button, 'data-index'),
- pid: toPid,
- topicName: ajaxify.data.titleRaw,
- username: username,
- text: selectedText
- });
- } else {
- $(window).trigger('action:composer.post.new', {
- tid: tid,
- pid: toPid,
- topicName: ajaxify.data.titleRaw,
- text: username ? username + ' ' : ''
- });
- }
+ if (selectedText) {
+ $(window).trigger('action:composer.addQuote', {
+ tid: tid,
+ slug: ajaxify.data.slug,
+ index: getData(button, 'data-index'),
+ pid: toPid,
+ topicName: ajaxify.data.titleRaw,
+ username: username,
+ text: selectedText
+ });
+ } else {
+ $(window).trigger('action:composer.post.new', {
+ tid: tid,
+ pid: toPid,
+ topicName: ajaxify.data.titleRaw,
+ text: username ? username + ' ' : ''
+ });
}
});
}
function onQuoteClicked(button, tid) {
- showStaleWarning(function(proceed) {
+ showStaleWarning(function() {
function quote(text) {
$(window).trigger('action:composer.addQuote', {
@@ -274,21 +272,19 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
});
}
- if (!proceed) {
- var username = getUserName(button);
- var pid = getData(button, 'data-pid');
- var selectedText = getSelectedText(button);
- if (selectedText) {
- return quote(selectedText);
- }
- socket.emit('posts.getRawPost', pid, function(err, post) {
- if (err) {
- return app.alertError(err.message);
- }
-
- quote(post);
- });
+ var username = getUserName(button);
+ var pid = getData(button, 'data-pid');
+ var selectedText = getSelectedText(button);
+ if (selectedText) {
+ return quote(selectedText);
}
+ socket.emit('posts.getRawPost', pid, function(err, post) {
+ if (err) {
+ return app.alertError(err.message);
+ }
+
+ quote(post);
+ });
});
}
@@ -498,39 +494,39 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
}
function showStaleWarning(callback) {
- if (ajaxify.data.lastposttime < (Date.now() - (1000 * 60 * 60 * 24 * ajaxify.data.topicStaleDays))) {
- translator.translate('[[topic:stale.warning]]', function(translated) {
- var warning = bootbox.dialog({
- title: '[[topic:stale.title]]',
- message: translated,
- buttons: {
- reply: {
- label: '[[topic:stale.reply_anyway]]',
- className: 'btn-link',
- callback: function() {
- callback(false);
- }
- },
- create: {
- label: '[[topic:stale.create]]',
- className: 'btn-primary',
- callback: function() {
- translator.translate('[[topic:link_back, ' + ajaxify.data.title + ', ' + config.relative_path + '/topic/' + ajaxify.data.slug + ']]', function(body) {
- $(window).trigger('action:composer.topic.new', {
- cid: ajaxify.data.cid,
- body: body
- });
+ if (ajaxify.data.lastposttime >= (Date.now() - (1000 * 60 * 60 * 24 * ajaxify.data.topicStaleDays))) {
+ return callback();
+ }
+
+ translator.translate('[[topic:stale.warning]]', function(translated) {
+ var warning = bootbox.dialog({
+ title: '[[topic:stale.title]]',
+ message: translated,
+ buttons: {
+ reply: {
+ label: '[[topic:stale.reply_anyway]]',
+ className: 'btn-link',
+ callback: function() {
+ callback();
+ }
+ },
+ create: {
+ label: '[[topic:stale.create]]',
+ className: 'btn-primary',
+ callback: function() {
+ translator.translate('[[topic:link_back, ' + ajaxify.data.title + ', ' + config.relative_path + '/topic/' + ajaxify.data.slug + ']]', function(body) {
+ $(window).trigger('action:composer.topic.new', {
+ cid: ajaxify.data.cid,
+ body: body
});
- }
+ });
}
}
- });
+ }
+ });
- warning.modal();
- });
- } else {
- callback(false);
- }
+ warning.modal();
+ });
}
return PostTools;
diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js
index 31cf70b30e..aaf188c591 100644
--- a/public/src/client/topic/posts.js
+++ b/public/src/client/topic/posts.js
@@ -225,9 +225,6 @@ define('forum/topic/posts', [
if (data && data.posts && data.posts.length) {
createNewPosts(data, replies, direction, done);
} else {
- if (app.user.uid) {
- socket.emit('topics.markAsRead', [tid]);
- }
navigator.update();
done();
}
diff --git a/public/src/client/unread.js b/public/src/client/unread.js
index 5e0fc8e127..4984d398b0 100644
--- a/public/src/client/unread.js
+++ b/public/src/client/unread.js
@@ -45,6 +45,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', '
app.alertSuccess('[[unread:topics_marked_as_read.success]]');
$('[component="category"]').empty();
+ $('[component="pagination"]').addClass('hidden');
$('#category-no-topics').removeClass('hidden');
$('.markread').addClass('hidden');
});
diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js
index 0d1747598c..0fb1a7f368 100644
--- a/public/src/modules/navigator.js
+++ b/public/src/modules/navigator.js
@@ -17,8 +17,15 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
navigator.callback = callback;
toTop = toTop || function() {};
toBottom = toBottom || function() {};
+ var navigatorUpdateTimeoutId = 0;
- $(window).off('scroll', navigator.update).on('scroll', navigator.update);
+ $(window).off('scroll', navigator.update).on('scroll', function() {
+ if (navigatorUpdateTimeoutId) {
+ clearTimeout(navigatorUpdateTimeoutId);
+ navigatorUpdateTimeoutId = 0;
+ }
+ navigatorUpdateTimeoutId = setTimeout(navigator.update, 100);
+ });
$('.pagination-block .dropdown-menu').off('click').on('click', function(e) {
e.stopPropagation();
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.az-short.js b/public/vendor/jquery/timeago/locales/jquery.timeago.az-short.js
new file mode 100644
index 0000000000..a430a45768
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.az-short.js
@@ -0,0 +1,20 @@
+// Azerbaijani shortened
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "",
+ suffixFromNow: "",
+ seconds: '1 dəq',
+ minute: '1 dəq',
+ minutes: '%d dəq',
+ hour: '1 saat',
+ hours: '%d saat',
+ day: '1 gün',
+ days: '%d gün',
+ month: '1 ay',
+ months: '%d ay',
+ year: '1 il',
+ years: '%d il',
+ wordSeparator: '',
+ numbers: []
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.az.js b/public/vendor/jquery/timeago/locales/jquery.timeago.az.js
new file mode 100644
index 0000000000..1e04a23a83
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.az.js
@@ -0,0 +1,20 @@
+// Azerbaijani
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: 'əvvəl',
+ suffixFromNow: 'sonra',
+ seconds: 'saniyələr',
+ minute: '1 dəqiqə',
+ minutes: '%d dəqiqə',
+ hour: '1 saat',
+ hours: '%d saat',
+ day: '1 gün',
+ days: '%d gün',
+ month: '1 ay',
+ months: '%d ay',
+ year: '1 il',
+ years: '%d il',
+ wordSeparator: '',
+ numbers: []
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.de-short.js b/public/vendor/jquery/timeago/locales/jquery.timeago.de-short.js
index 10f158de08..09427ec976 100644
--- a/public/vendor/jquery/timeago/locales/jquery.timeago.de-short.js
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.de-short.js
@@ -4,17 +4,17 @@ jQuery.timeago.settings.strings = {
prefixFromNow: null,
suffixAgo: "",
suffixFromNow: "",
- seconds: "sec",
- minute: "1min",
- minutes: "%dmin",
+ seconds: "s",
+ minute: "1m",
+ minutes: "%dm",
hour: "1h",
hours: "%dh",
- day: "1d",
- days: "%dd",
- month: "1Mon",
- months: "%dMon",
- year: "1Jhr",
- years: "%dJhr",
+ day: "1T.",
+ days: "%dT.",
+ month: "1Mt.",
+ months: "%dMt.",
+ year: "1J.",
+ years: "%dJ.",
wordSeparator: " ",
numbers: []
};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.ja.js b/public/vendor/jquery/timeago/locales/jquery.timeago.ja.js
index fd81f275d0..95a7cd2a7a 100644
--- a/public/vendor/jquery/timeago/locales/jquery.timeago.ja.js
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.ja.js
@@ -11,8 +11,8 @@ jQuery.timeago.settings.strings = {
hours: "約 %d 時間",
day: "約 1 日",
days: "約 %d 日",
- month: "約 1 月",
- months: "約 %d 月",
+ month: "約 1 ヶ月",
+ months: "約 %d ヶ月",
year: "約 1 年",
years: "約 %d 年",
wordSeparator: ""
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.lv.js b/public/vendor/jquery/timeago/locales/jquery.timeago.lv.js
new file mode 100644
index 0000000000..eb02391563
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.lv.js
@@ -0,0 +1,20 @@
+//Latvian
+jQuery.timeago.settings.strings = {
+ prefixAgo: "pirms",
+ prefixFromNow: null,
+ suffixAgo: null,
+ suffixFromNow: "no šī brīža",
+ seconds: "%d sek.",
+ minute: "min.",
+ minutes: "%d min.",
+ hour: "st.",
+ hours: "%d st.",
+ day: "1 d.",
+ days: "%d d.",
+ month: "mēnesis.",
+ months: "%d mēnesis.",
+ year: "gads",
+ years: "%d gads",
+ wordSeparator: " ",
+ numbers: []
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.sl.js b/public/vendor/jquery/timeago/locales/jquery.timeago.sl.js
index 57d4f6020c..b8ab587d82 100644
--- a/public/vendor/jquery/timeago/locales/jquery.timeago.sl.js
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.sl.js
@@ -28,7 +28,7 @@
},
month: "en mesec",
months: function (value) {
- return numpf(value, ["%d mescov", "%d mesec", "%d mesca", "%d mesce"]);
+ return numpf(value, ["%d mesecev", "%d mesec", "%d meseca", "%d mesece"]);
},
year: "eno leto",
years: function (value) {
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.sr.js b/public/vendor/jquery/timeago/locales/jquery.timeago.sr.js
new file mode 100644
index 0000000000..c75a972e77
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.sr.js
@@ -0,0 +1,49 @@
+// Serbian
+(function () {
+ var numpf;
+
+ numpf = function (n, f, s, t) {
+ var n10;
+ n10 = n % 10;
+ if (n10 === 1 && (n === 1 || n > 20)) {
+ return f;
+ } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) {
+ return s;
+ } else {
+ return t;
+ }
+ };
+
+ jQuery.timeago.settings.strings = {
+ prefixAgo: "пре",
+ prefixFromNow: "за",
+ suffixAgo: null,
+ suffixFromNow: null,
+ second: "секунд",
+ seconds: function (value) {
+ return numpf(value, "%d секунд", "%d секунде", "%d секунди");
+ },
+ minute: "један минут",
+ minutes: function (value) {
+ return numpf(value, "%d минут", "%d минута", "%d минута");
+ },
+ hour: "један сат",
+ hours: function (value) {
+ return numpf(value, "%d сат", "%d сата", "%d сати");
+ },
+ day: "један дан",
+ days: function (value) {
+ return numpf(value, "%d дан", "%d дана", "%d дана");
+ },
+ month: "месец дана",
+ months: function (value) {
+ return numpf(value, "%d месец", "%d месеца", "%d месеци");
+ },
+ year: "годину дана",
+ years: function (value) {
+ return numpf(value, "%d годину", "%d године", "%d година");
+ },
+ wordSeparator: " "
+ };
+
+}).call(this);
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.tr-short.js b/public/vendor/jquery/timeago/locales/jquery.timeago.tr-short.js
new file mode 100644
index 0000000000..ebc2277b49
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.tr-short.js
@@ -0,0 +1,20 @@
+// Turkish shortened
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "",
+ suffixFromNow: "",
+ seconds: "1sn",
+ minute: "1d",
+ minutes: "%dd",
+ hour: "1s",
+ hours: "%ds",
+ day: "1g",
+ days: "%dg",
+ month: "1ay",
+ months: "%day",
+ year: "1y",
+ years: "%dy",
+ wordSeparator: " ",
+ numbers: []
+};
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index bc2369bd62..c34b0acd0a 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -81,6 +81,27 @@ authenticationController.register = function(req, res, next) {
function registerAndLoginUser(req, res, userData, callback) {
var uid;
async.waterfall([
+ function(next) {
+ plugins.fireHook('filter:register.interstitial', {
+ userData: userData,
+ interstitials: []
+ }, function(err, data) {
+ // If interstitials are found, save registration attempt into session and abort
+ var deferRegistration = data.interstitials.length;
+
+ if (!deferRegistration) {
+ return next();
+ } else {
+ userData.register = true;
+ req.session.registration = userData;
+ if (res.locals.isAPI) {
+ return res.json({ referrer: nconf.get('relative_path') + '/register/complete' });
+ } else {
+ return res.redirect(nconf.get('relative_path') + '/register/complete');
+ }
+ }
+ });
+ },
function(next) {
user.create(userData, next);
},
@@ -111,6 +132,54 @@ function addToApprovalQueue(req, userData, callback) {
], callback);
}
+authenticationController.registerComplete = function(req, res, next) {
+ // For the interstitials that respond, execute the callback with the form body
+ plugins.fireHook('filter:register.interstitial', {
+ userData: req.session.registration,
+ interstitials: []
+ }, function(err, data) {
+ var callbacks = data.interstitials.reduce(function(memo, cur) {
+ if (cur.hasOwnProperty('callback') && typeof cur.callback === 'function') {
+ memo.push(async.apply(cur.callback, req.session.registration, req.body));
+ }
+
+ return memo;
+ }, []);
+
+ var done = function() {
+ delete req.session.registration;
+
+ if (req.session.returnTo) {
+ res.redirect(req.session.returnTo);
+ } else {
+ res.redirect(nconf.get('relative_path') + '/');
+ }
+ }
+
+ async.parallel(callbacks, function(err) {
+ if (err) {
+ req.flash('error', err.message);
+ return res.redirect(nconf.get('relative_path') + '/register/complete');
+ }
+
+ if (req.session.registration.register === true) {
+ res.locals.processLogin = true;
+ registerAndLoginUser(req, res, req.session.registration, done);
+ } else {
+ // Clear registration data in session
+ done();
+ }
+ });
+ });
+};
+
+authenticationController.registerAbort = function(req, res, next) {
+ // End the session and redirect to home
+ req.session.destroy(function() {
+ res.redirect(nconf.get('relative_path') + '/');
+ });
+};
+
authenticationController.login = function(req, res, next) {
// Handle returnTo data
if (req.body.hasOwnProperty('returnTo') && !req.session.returnTo) {
@@ -307,6 +376,7 @@ authenticationController.logout = function(req, res, next) {
return next(err);
}
req.logout();
+ req.session.destroy();
user.setUserField(uid, 'lastonline', Date.now() - 300000);
diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js
index 8032ed7a0e..3d0f177d67 100644
--- a/src/controllers/helpers.js
+++ b/src/controllers/helpers.js
@@ -12,29 +12,35 @@ var meta = require('../meta');
var helpers = {};
helpers.notAllowed = function(req, res, error) {
- if (req.uid) {
- if (res.locals.isAPI) {
- res.status(403).json({
- path: req.path.replace(/^\/api/, ''),
- loggedIn: !!req.uid, error: error,
- title: '[[global:403.title]]'
- });
+ plugins.fireHook('filter:helpers.notAllowed', {
+ req: req,
+ res: res,
+ error: error
+ }, function(err, data) {
+ if (req.uid) {
+ if (res.locals.isAPI) {
+ res.status(403).json({
+ path: req.path.replace(/^\/api/, ''),
+ loggedIn: !!req.uid, error: error,
+ title: '[[global:403.title]]'
+ });
+ } else {
+ res.status(403).render('403', {
+ path: req.path,
+ loggedIn: !!req.uid, error: error,
+ title: '[[global:403.title]]'
+ });
+ }
} else {
- res.status(403).render('403', {
- path: req.path,
- loggedIn: !!req.uid, error: error,
- title: '[[global:403.title]]'
- });
+ if (res.locals.isAPI) {
+ req.session.returnTo = nconf.get('relative_path') + req.url.replace(/^\/api/, '');
+ res.status(401).json('not-authorized');
+ } else {
+ req.session.returnTo = nconf.get('relative_path') + req.url;
+ res.redirect(nconf.get('relative_path') + '/login');
+ }
}
- } else {
- if (res.locals.isAPI) {
- req.session.returnTo = nconf.get('relative_path') + req.url.replace(/^\/api/, '');
- res.status(401).json('not-authorized');
- } else {
- req.session.returnTo = nconf.get('relative_path') + req.url;
- res.redirect(nconf.get('relative_path') + '/login');
- }
- }
+ });
};
helpers.redirect = function(res, url) {
diff --git a/src/controllers/index.js b/src/controllers/index.js
index 26b7527ce9..2d834c248f 100644
--- a/src/controllers/index.js
+++ b/src/controllers/index.js
@@ -185,6 +185,33 @@ Controllers.register = function(req, res, next) {
});
};
+Controllers.registerInterstitial = function(req, res, next) {
+ if (!req.session.hasOwnProperty('registration')) {
+ return res.redirect(nconf.get('relative_path') + '/register');
+ }
+
+ plugins.fireHook('filter:register.interstitial', {
+ userData: req.session.registration,
+ interstitials: []
+ }, function(err, data) {
+ if (!data.interstitials.length) {
+ return next();
+ }
+
+ var renders = data.interstitials.map(function(interstitial) {
+ return async.apply(req.app.render.bind(req.app), interstitial.template, interstitial.data || {});
+ });
+ var errors = req.flash('error');
+
+ async.parallel(renders, function(err, sections) {
+ res.render('registerComplete', {
+ errors: errors,
+ sections: sections
+ });
+ });
+ });
+};
+
Controllers.compose = function(req, res, next) {
plugins.fireHook('filter:composer.build', {
req: req,
@@ -383,12 +410,14 @@ Controllers.handle404 = function(req, res) {
meta.errors.log404(req.path.replace(/^\/api/, '') || '');
res.status(404);
+ var path = String(req.path || '');
+
if (res.locals.isAPI) {
- return res.json({path: validator.escape(req.path.replace(/^\/api/, '') || ''), title: '[[global:404.title]]'});
+ return res.json({path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]'});
}
req.app.locals.middleware.buildHeader(req, res, function() {
- res.render('404', {path: validator.escape(req.path || ''), title: '[[global:404.title]]'});
+ res.render('404', {path: validator.escape(path), title: '[[global:404.title]]'});
});
} else {
res.status(404).type('txt').send('Not found');
@@ -412,11 +441,12 @@ Controllers.handleErrors = function(err, req, res, next) {
res.status(err.status || 500);
+ var path = String(req.path || '');
if (res.locals.isAPI) {
- res.json({path: validator.escape(req.path || ''), error: err.message});
+ res.json({path: validator.escape(path), error: err.message});
} else {
req.app.locals.middleware.buildHeader(req, res, function() {
- res.render('500', {path: validator.escape(String(req.path || '')), error: validator.escape(err.message)});
+ res.render('500', {path: validator.escape(path), error: validator.escape(String(err.message))});
});
}
};
diff --git a/src/groups/update.js b/src/groups/update.js
index f48ff96d21..27d2cddc00 100644
--- a/src/groups/update.js
+++ b/src/groups/update.js
@@ -168,7 +168,7 @@ module.exports = function(Groups) {
return callback(err);
}
- if (parseInt(group.system, 10) === 1 || parseInt(group.hidden, 10) === 1) {
+ if (parseInt(group.system, 10) === 1) {
return callback();
}
diff --git a/src/middleware/cls.js b/src/middleware/cls.js
deleted file mode 100644
index 8cf0062d30..0000000000
--- a/src/middleware/cls.js
+++ /dev/null
@@ -1,38 +0,0 @@
-var path = require('path');
-var sockets = require('path');
-var websockets = require('../socket.io/');
-var continuationLocalStorage = require('continuation-local-storage');
-var APP_NAMESPACE = require(path.join(__dirname, '../../package.json')).name;
-var namespace = continuationLocalStorage.createNamespace(APP_NAMESPACE);
-
-(function(cls) {
- cls.http = function (req, res, next) {
- namespace.run(function() {
- namespace.set('request', req);
- next && next();
- });
- };
-
- cls.socket = function (socket, payload, event, next) {
- namespace.run(function() {
- namespace.set('request', websockets.reqFromSocket(socket, payload, event));
- next && next();
- });
- };
-
- cls.get = function (key) {
- return namespace.get(key);
- };
-
- cls.set = function (key, value) {
- return namespace.set(key, value);
- };
-
- cls.setItem = cls.set;
- cls.getItem = cls.get;
- cls.namespace = namespace;
- cls.continuationLocalStorage = continuationLocalStorage;
-
-})(exports);
-
-
diff --git a/src/middleware/index.js b/src/middleware/index.js
index 2e39bb6d70..867af6fed2 100644
--- a/src/middleware/index.js
+++ b/src/middleware/index.js
@@ -14,7 +14,6 @@ var meta = require('../meta'),
compression = require('compression'),
favicon = require('serve-favicon'),
session = require('express-session'),
- cls = require('./cls'),
useragent = require('express-useragent');
@@ -62,7 +61,7 @@ module.exports = function(app) {
if (nconf.get('secure')) {
cookie.secure = true;
}
-
+
if (relativePath !== '') {
cookie.path = relativePath;
}
@@ -78,7 +77,6 @@ module.exports = function(app) {
app.use(middleware.addHeaders);
app.use(middleware.processRender);
- app.use(cls.http);
auth.initialize(app, middleware);
return middleware;
diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js
index 7409bd1577..991f6bbe66 100644
--- a/src/middleware/middleware.js
+++ b/src/middleware/middleware.js
@@ -350,6 +350,19 @@ middleware.processTimeagoLocales = function(req, res, next) {
}
};
+middleware.registrationComplete = function(req, res, next) {
+ // If the user's session contains registration data, redirect the user to complete registration
+ if (!req.session.hasOwnProperty('registration')) {
+ return next();
+ } else {
+ if (!req.path.endsWith('/register/complete')) {
+ controllers.helpers.redirect(res, '/register/complete');
+ } else {
+ return next();
+ }
+ }
+};
+
module.exports = function(webserver) {
app = webserver;
middleware.admin = require('./admin')(webserver);
diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js
index 005255582f..c2dde7b92a 100644
--- a/src/plugins/hooks.js
+++ b/src/plugins/hooks.js
@@ -7,29 +7,6 @@ module.exports = function(Plugins) {
Plugins.deprecatedHooks = {
'filter:user.custom_fields': null // remove in v1.1.0
};
-
- Plugins.deprecatedHooksParams = {
- 'action:homepage.get': '{req, res}',
- 'filter:register.check': '{req, res}',
- 'action:user.loggedOut': '{req, res}',
- 'static:user.loggedOut': '{req, res}',
- 'filter:categories.build': '{req, res}',
- 'filter:category.build': '{req, res}',
- 'filter:group.build': '{req, res}',
- 'filter:register.build': '{req, res}',
- 'filter:composer.build': '{req, res}',
- 'filter:popular.build': '{req, res}',
- 'filter:recent.build': '{req, res}',
- 'filter:topic.build': '{req, res}',
- 'filter:users.build': '{req, res}',
- 'filter:admin.category.get': '{req, res}',
- 'filter:middleware.renderHeader': '{req, res}',
- 'filter:widget.render': '{req, res}',
- 'filter:middleware.buildHeader': '{req, locals}',
- 'action:middleware.pageView': '{req}',
- 'action:meta.override404': '{req}'
- };
-
/*
`data` is an object consisting of (* is required):
`data.hook`*, the name of the NodeBB hook
@@ -62,12 +39,6 @@ module.exports = function(Plugins) {
parts.pop();
}
var hook = parts.join(':');
- if (Plugins.deprecatedHooksParams[hook]) {
- winston.warn('[plugins/' + id + '] Hook `' + hook + '` parameters: `' + Plugins.deprecatedHooksParams[hook] + '`, are being deprecated, '
- + 'all plugins should now use the `middleware/cls` module instead of hook\'s arguments to get a reference to the `http-request` or the `socket-request` object(s) (from which you can get the current `uid` if you need to.) '
- + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-each-request-from-within-any-plugin-hook\n');
- delete Plugins.deprecatedHooksParams[hook];
- }
}
if (data.hook && data.method) {
diff --git a/src/routes/authentication.js b/src/routes/authentication.js
index 62e2c66224..d8561f2528 100644
--- a/src/routes/authentication.js
+++ b/src/routes/authentication.js
@@ -64,6 +64,8 @@
});
router.post('/register', Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist, controllers.authentication.register);
+ router.post('/register/complete', Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist, controllers.authentication.registerComplete);
+ router.get('/register/abort', controllers.authentication.registerAbort);
router.post('/login', Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist, controllers.authentication.login);
router.post('/logout', Auth.middleware.applyCSRF, controllers.authentication.logout);
diff --git a/src/routes/helpers.js b/src/routes/helpers.js
index 3dcca80bc4..378e2bdbc8 100644
--- a/src/routes/helpers.js
+++ b/src/routes/helpers.js
@@ -3,7 +3,7 @@
var helpers = {};
helpers.setupPageRoute = function(router, name, middleware, middlewares, controller) {
- middlewares = middlewares.concat([middleware.pageView, middleware.pluginHooks]);
+ middlewares = middlewares.concat([middleware.registrationComplete, middleware.pageView, middleware.pluginHooks]);
router.get(name, middleware.busyCheck, middleware.buildHeader, middlewares, controller);
router.get('/api' + name, middlewares, controller);
diff --git a/src/routes/index.js b/src/routes/index.js
index 626b480db1..06c9706856 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -6,6 +6,7 @@ var async = require('async');
var winston = require('winston');
var controllers = require('../controllers');
var plugins = require('../plugins');
+var user = require('../user');
var express = require('express');
var validator = require('validator');
@@ -28,6 +29,7 @@ function mainRoutes(app, middleware, controllers) {
setupPageRoute(app, '/login', middleware, loginRegisterMiddleware, controllers.login);
setupPageRoute(app, '/register', middleware, loginRegisterMiddleware, controllers.register);
+ setupPageRoute(app, '/register/complete', middleware, [], controllers.registerInterstitial);
setupPageRoute(app, '/compose', middleware, [], controllers.compose);
setupPageRoute(app, '/confirm/:code', middleware, [], controllers.confirmEmail);
setupPageRoute(app, '/outgoing', middleware, [], controllers.outgoing);
@@ -155,6 +157,7 @@ module.exports = function(app, middleware, hotswapIds) {
// Add plugin routes
async.series([
async.apply(plugins.reloadRoutes),
- async.apply(authRoutes.reloadRoutes)
+ async.apply(authRoutes.reloadRoutes),
+ async.apply(user.addInterstitials)
]);
};
diff --git a/src/socket.io/admin/groups.js b/src/socket.io/admin/groups.js
index 206604ec8f..c912008210 100644
--- a/src/socket.io/admin/groups.js
+++ b/src/socket.io/admin/groups.js
@@ -5,8 +5,10 @@ var groups = require('../../groups'),
Groups = {};
Groups.create = function(socket, data, callback) {
- if(!data) {
+ if (!data) {
return callback(new Error('[[error:invalid-data]]'));
+ } else if (groups.isPrivilegeGroup(data.name)) {
+ return callback(new Error('[[error:invalid-group-name]]'));
}
groups.create({
diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js
index a54db76b05..4e48a9649b 100644
--- a/src/socket.io/groups.js
+++ b/src/socket.io/groups.js
@@ -170,6 +170,8 @@ SocketGroups.create = function(socket, data, callback) {
return callback(new Error('[[error:no-privileges]]'));
} else if (parseInt(meta.config.allowGroupCreation, 10) !== 1) {
return callback(new Error('[[error:group-creation-disabled]]'));
+ } else if (groups.isPrivilegeGroup(data.name)) {
+ return callback(new Error('[[error:invalid-group-name]]'));
}
diff --git a/src/socket.io/index.js b/src/socket.io/index.js
index 89459c50b3..772166687a 100644
--- a/src/socket.io/index.js
+++ b/src/socket.io/index.js
@@ -11,7 +11,6 @@ var url = require('url');
var db = require('../database');
var logger = require('../logger');
var ratelimit = require('../middleware/ratelimit');
-var cls = require('../middleware/cls');
(function(Sockets) {
var Namespaces = {};
@@ -30,7 +29,6 @@ var cls = require('../middleware/cls');
io.use(authorize);
io.on('connection', onConnection);
- io.on('disconnect', onDisconnect);
io.listen(server, {
transports: nconf.get('socket.io:transports')
@@ -44,14 +42,10 @@ var cls = require('../middleware/cls');
logger.io_one(socket, socket.uid);
- cls.socket(socket, null, 'connection', function () {
- onConnect(socket);
- });
+ onConnect(socket);
socket.on('*', function (payload) {
- cls.socket(socket, payload, null, function () {
- onMessage(socket, payload);
- });
+ onMessage(socket, payload);
});
}
@@ -64,12 +58,6 @@ var cls = require('../middleware/cls');
}
}
- function onDisconnect(socket) {
- cls.socket(socket, null, 'disconnect', function () {
- });
- }
-
-
function onMessage(socket, payload) {
if (!payload.data.length) {
return winston.warn('[socket.io] Empty payload');
@@ -235,4 +223,4 @@ var cls = require('../middleware/cls');
};
};
-})(exports);
\ No newline at end of file
+})(exports);
diff --git a/src/topics/unread.js b/src/topics/unread.js
index b4900be3a1..61fef8b65e 100644
--- a/src/topics/unread.js
+++ b/src/topics/unread.js
@@ -255,7 +255,7 @@ module.exports = function(Topics) {
async.parallel({
markRead: async.apply(db.sortedSetAdd, 'uid:' + uid + ':tids_read', scores, tids),
markUnread: async.apply(db.sortedSetRemove, 'uid:' + uid + ':tids_unread', tids),
- topicData: async.apply( Topics.getTopicsFields, tids, ['cid'])
+ topicData: async.apply(Topics.getTopicsFields, tids, ['cid'])
}, next);
},
function (results, next) {
diff --git a/src/user.js b/src/user.js
index c7fb18e4fc..50df737bc7 100644
--- a/src/user.js
+++ b/src/user.js
@@ -6,6 +6,7 @@ var plugins = require('./plugins');
var db = require('./database');
var topics = require('./topics');
var privileges = require('./privileges');
+var meta = require('./meta');
var utils = require('../public/src/utils');
(function(User) {
@@ -255,5 +256,32 @@ var utils = require('../public/src/utils');
});
};
+ User.addInterstitials = function(callback) {
+ plugins.registerHook('core', {
+ hook: 'filter:register.interstitial',
+ method: function(data, callback) {
+ if (meta.config.termsOfUse && !data.userData.acceptTos) {
+ data.interstitials.push({
+ template: 'partials/acceptTos',
+ data: {
+ termsOfUse: meta.config.termsOfUse
+ },
+ callback: function(userData, formData, next) {
+ if (formData['agree-terms'] === 'on') {
+ userData.acceptTos = true;
+ }
+
+ next(userData.acceptTos ? null : new Error('[[register:terms_of_use_error]]'));
+ }
+ });
+ }
+
+ callback(null, data);
+ }
+ });
+
+ callback();
+ };
+
}(exports));
diff --git a/src/user/create.js b/src/user/create.js
index ae69f1ad5d..bd79270a93 100644
--- a/src/user/create.js
+++ b/src/user/create.js
@@ -8,11 +8,9 @@ var plugins = require('../plugins');
var groups = require('../groups');
var meta = require('../meta');
-
module.exports = function(User) {
User.create = function(data, callback) {
-
data.username = data.username.trim();
data.userslug = utils.slugify(data.username);
if (data.email !== undefined) {
diff --git a/src/widgets/index.js b/src/widgets/index.js
index d220f73740..5debdf3b26 100644
--- a/src/widgets/index.js
+++ b/src/widgets/index.js
@@ -136,13 +136,21 @@ widgets.reset = function(callback) {
{ name: 'Draft Zone', template: 'global', location: 'sidebar' }
];
- plugins.fireHook('filter:widgets.getAreas', defaultAreas, function(err, areas) {
+ async.parallel({
+ areas: function(next) {
+ plugins.fireHook('filter:widgets.getAreas', defaultAreas, next);
+ },
+ drafts: function(next) {
+ widgets.getArea('global', 'drafts', next);
+ }
+ }, function(err, results) {
if (err) {
return callback(err);
}
- var drafts = [];
- async.each(areas, function(area, next) {
+ var drafts = results.drafts || [];
+
+ async.each(results.areas, function(area, next) {
widgets.getArea(area.template, area.location, function(err, areaData) {
if (err) {
return next(err);