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);