mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-06-26 02:41:07 +02:00
Merge commit '3bebc3ce09e66bbd18203eeaf72a9ab286eceed6' into weekly
This commit is contained in:
1
app.js
1
app.js
@@ -22,7 +22,6 @@
|
||||
|
||||
var nconf = require('nconf');
|
||||
nconf.argv().env('__');
|
||||
require('continuation-local-storage');
|
||||
|
||||
var url = require('url'),
|
||||
async = require('async'),
|
||||
|
||||
13
package.json
13
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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í",
|
||||
|
||||
@@ -63,7 +63,9 @@
|
||||
"topics": "Topics",
|
||||
"posts": "Posts",
|
||||
"best": "Best",
|
||||
"upvoters": "Upvoters",
|
||||
"upvoted": "Upvoted",
|
||||
"downvoters": "Downvoters",
|
||||
"downvoted": "Downvoted",
|
||||
"views": "Views",
|
||||
"reputation": "Reputation",
|
||||
|
||||
@@ -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 @<span id='yourUsername'>username</span>.",
|
||||
"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:"
|
||||
}
|
||||
@@ -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": "این اطلاعیه ی چتیی که برای شما فرستاده شده به علت تنظیمات اشترک شماست.",
|
||||
|
||||
@@ -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": "شما نمی توانید خودتان را از گروه کیک کنید"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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": "دعوت را قبول میکنم",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"remember_me": "مرا به یاد بسپار؟",
|
||||
"forgot_password": "گذرواژه را فراموش کردهاید؟",
|
||||
"alternative_logins": "روشهای درون آمدن جایگزین",
|
||||
"failed_login_attempt": "Login Unsuccessful",
|
||||
"failed_login_attempt": "ورود ناموفق",
|
||||
"login_successful": "شما با موفقیت به درون آمدهاید!",
|
||||
"dont_have_account": "حساب کاربری ندارید؟"
|
||||
}
|
||||
@@ -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": "تایید",
|
||||
|
||||
@@ -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": "آکاهسازی تازه",
|
||||
|
||||
@@ -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 به دلیل بارگذاری بیش از حد ، قابل دسترس نمی باشد. لطفا در زمان دیگری دوباره امتحان کنید"
|
||||
|
||||
@@ -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": "وارد کردن دلیل پرچمگذاری"
|
||||
|
||||
@@ -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": "موضوع های پیگیری شده"
|
||||
}
|
||||
@@ -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 کیلوبایت"
|
||||
}
|
||||
@@ -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": "نام کاربری درخواستی شما در حال حاضر گرفته شده است، بنابراین ما آن را کمی تغییر دادهایم. شما هماکنون با نام <strong>%1</strong شناخته میشوید.",
|
||||
"password_same_as_username": "کلمه ی عبور شما با نام کاربری شما یکسان می باشند ، لطفا کلمه ی عبور دیگری را انتخاب کنید",
|
||||
"password_same_as_email": "Your password is the same as your email, please select another password.",
|
||||
"password_same_as_email": "کلمه ی عبور شما با ایمیل شما یکسان است، لطفا کلمه عبور دیگری را انتخاب کنید.",
|
||||
"upload_picture": "بارگذاری تصویر",
|
||||
"upload_a_picture": "یک تصویر بارگذاری کنید",
|
||||
"remove_uploaded_picture": "پاک کردن عکس بارگذاری شده",
|
||||
"upload_cover_picture": "Upload cover picture",
|
||||
"upload_cover_picture": "بارگذاری عکس کاور",
|
||||
"settings": "تنظیمات",
|
||||
"show_email": "نمایش ایمیلام",
|
||||
"show_fullname": "نام کامل من را نشان بده",
|
||||
@@ -79,9 +79,9 @@
|
||||
"has_no_posts": "این کاربر تا به حال هیچ چیزی ارسال نکرده است.",
|
||||
"has_no_topics": "این کاربر تا به حال هیچ موضوعی ارسال نکرده است",
|
||||
"has_no_watched_topics": "این کاربر تا به حال هیچ موضوعی را پیگیری نکرده است",
|
||||
"has_no_upvoted_posts": "This user hasn't upvoted any posts yet.",
|
||||
"has_no_upvoted_posts": "این کاربر به هیچ پستی امتیاز نداده است.",
|
||||
"has_no_downvoted_posts": "This user hasn't downvoted any posts yet.",
|
||||
"has_no_voted_posts": "This user has no voted posts",
|
||||
"has_no_voted_posts": "این کاربر به پست رای نداده است",
|
||||
"email_hidden": "ایمیل پنهان شده",
|
||||
"hidden": "پنهان",
|
||||
"paginate_description": "صفحه بندی و نمایش موضوع ها و پستها به جای نمایش بر اساس اسکرول موس",
|
||||
@@ -92,12 +92,12 @@
|
||||
"open_links_in_new_tab": "پیوندهای به بیرون را در برگ جدید باز کن",
|
||||
"enable_topic_searching": "فعال کردن جستجوی داخل-موضوع",
|
||||
"topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen",
|
||||
"delay_image_loading": "Delay Image Loading",
|
||||
"delay_image_loading": "تاخیر در حال بارگذاری عکس",
|
||||
"image_load_delay_help": "If enabled, images in topics will not load until they are scrolled into view",
|
||||
"scroll_to_my_post": "After posting a reply, show the new post",
|
||||
"scroll_to_my_post": "پس از ارسال پست، اولین پست جدید نشان بده",
|
||||
"follow_topics_you_reply_to": "تاپیک هایی که پاسخ داده ای را دنبال کن",
|
||||
"follow_topics_you_create": "موضوع هایی که ایجاد کرده ای را دنبال کن",
|
||||
"grouptitle": "Group Title",
|
||||
"grouptitle": "عنوان گروه",
|
||||
"no-group-title": "عنوان گروهی نیست",
|
||||
"select-skin": "Select a Skin",
|
||||
"select-homepage": "Select a Homepage",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "אנגלית (הממלכה המאוחדת/קנדה)",
|
||||
"name": "עברית (ישראל)",
|
||||
"code": "he",
|
||||
"dir": "rtl"
|
||||
}
|
||||
@@ -173,7 +173,15 @@ define('admin/manage/group', [
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
app.alertSuccess('Changes saved!');
|
||||
|
||||
var newName = $('#change-group-name').val();
|
||||
|
||||
// If the group name changed, change url
|
||||
if (groupName === newName) {
|
||||
app.alertSuccess('Changes saved!');
|
||||
} else {
|
||||
ajaxify.go('admin/manage/groups/' + encodeURIComponent(newName), undefined, true);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -13,8 +13,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) {
|
||||
username = $('#username'),
|
||||
password = $('#password'),
|
||||
password_confirm = $('#password-confirm'),
|
||||
register = $('#register'),
|
||||
agreeTerms = $('#agree-terms');
|
||||
register = $('#register');
|
||||
|
||||
handleLanguageOverride();
|
||||
|
||||
@@ -111,18 +110,6 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (agreeTerms.length) {
|
||||
agreeTerms.on('click', function() {
|
||||
if ($(this).prop('checked')) {
|
||||
register.removeAttr('disabled');
|
||||
} else {
|
||||
register.attr('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
|
||||
register.attr('disabled', 'disabled');
|
||||
}
|
||||
};
|
||||
|
||||
function validateEmail(email, callback) {
|
||||
|
||||
@@ -242,37 +242,40 @@ define('forum/topic', [
|
||||
Topic.navigatorCallback = function(index, elementCount, threshold) {
|
||||
var path = ajaxify.removeRelativePath(window.location.pathname.slice(1));
|
||||
if (!path.startsWith('topic')) {
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!navigator.scrollActive) {
|
||||
var parts = ajaxify.removeRelativePath(window.location.pathname.slice(1)).split('/');
|
||||
var topicId = parts[1],
|
||||
slug = parts[2];
|
||||
var newUrl = 'topic/' + topicId + '/' + (slug ? slug : '');
|
||||
if (index > 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
20
public/vendor/jquery/timeago/locales/jquery.timeago.az-short.js
vendored
Normal file
20
public/vendor/jquery/timeago/locales/jquery.timeago.az-short.js
vendored
Normal file
@@ -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: []
|
||||
};
|
||||
20
public/vendor/jquery/timeago/locales/jquery.timeago.az.js
vendored
Normal file
20
public/vendor/jquery/timeago/locales/jquery.timeago.az.js
vendored
Normal file
@@ -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: []
|
||||
};
|
||||
@@ -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: []
|
||||
};
|
||||
|
||||
@@ -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: ""
|
||||
|
||||
20
public/vendor/jquery/timeago/locales/jquery.timeago.lv.js
vendored
Normal file
20
public/vendor/jquery/timeago/locales/jquery.timeago.lv.js
vendored
Normal file
@@ -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: []
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
49
public/vendor/jquery/timeago/locales/jquery.timeago.sr.js
vendored
Normal file
49
public/vendor/jquery/timeago/locales/jquery.timeago.sr.js
vendored
Normal file
@@ -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);
|
||||
20
public/vendor/jquery/timeago/locales/jquery.timeago.tr-short.js
vendored
Normal file
20
public/vendor/jquery/timeago/locales/jquery.timeago.tr-short.js
vendored
Normal file
@@ -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: []
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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]]'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
})(exports);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
28
src/user.js
28
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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user