mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-02-05 06:09:57 +01:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
13
.gitignore
vendored
13
.gitignore
vendored
@@ -8,7 +8,6 @@ public/css/*.css
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.project
|
||||
.idea
|
||||
*.swp
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
@@ -31,4 +30,14 @@ pidfile
|
||||
/public/stylesheet.css
|
||||
/public/admin.css
|
||||
/public/nodebb.min.js
|
||||
/public/nodebb.min.js.map
|
||||
/public/nodebb.min.js.map
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
36
app.js
36
app.js
@@ -43,7 +43,7 @@ winston.add(winston.transports.Console, {
|
||||
var date = new Date();
|
||||
return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']';
|
||||
},
|
||||
level: (global.env === 'production' || nconf.get('log-level') === 'info') ? 'info' : 'verbose'
|
||||
level: nconf.get('log-level') || (global.env === 'production' ? 'info' : 'verbose')
|
||||
});
|
||||
|
||||
if(os.platform() === 'linux') {
|
||||
@@ -182,16 +182,13 @@ function start() {
|
||||
function(next) {
|
||||
require('./src/meta').configs.init(next);
|
||||
},
|
||||
function(next) {
|
||||
require('./src/meta').dependencies.check(next);
|
||||
},
|
||||
function(next) {
|
||||
require('./src/upgrade').check(next);
|
||||
},
|
||||
function(schema_ok, next) {
|
||||
if (!schema_ok && nconf.get('check-schema') !== false) {
|
||||
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
|
||||
winston.warn(' ./nodebb upgrade');
|
||||
process.exit();
|
||||
return;
|
||||
}
|
||||
function(next) {
|
||||
var webserver = require('./src/webserver');
|
||||
require('./src/socket.io').init(webserver.server);
|
||||
|
||||
@@ -204,12 +201,25 @@ function start() {
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
if (err.stacktrace !== false) {
|
||||
winston.error(err.stack);
|
||||
} else {
|
||||
winston.error(err.message);
|
||||
switch(err.message) {
|
||||
case 'schema-out-of-date':
|
||||
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
|
||||
winston.warn(' ./nodebb upgrade');
|
||||
break;
|
||||
case 'dependencies-out-of-date':
|
||||
winston.warn('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:');
|
||||
winston.warn(' ./nodebb upgrade');
|
||||
break;
|
||||
default:
|
||||
if (err.stacktrace !== false) {
|
||||
winston.error(err.stack);
|
||||
} else {
|
||||
winston.error(err.message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Either way, bad stuff happened. Abort start.
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
@@ -403,4 +413,4 @@ function restart() {
|
||||
winston.error('[app] Could not restart server. Shutting down.');
|
||||
shutdown(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,10 @@ Minifier.js.minify = function (scripts, minify, callback) {
|
||||
process.on('message', function(payload) {
|
||||
switch(payload.action) {
|
||||
case 'js':
|
||||
Minifier.js.minify(payload.scripts, payload.minify, function(minified) {
|
||||
Minifier.js.minify(payload.scripts, payload.minify, function(minified/*, sourceMap*/) {
|
||||
process.send({
|
||||
type: 'end',
|
||||
// sourceMap: sourceMap,
|
||||
minified: minified
|
||||
});
|
||||
});
|
||||
@@ -41,8 +42,11 @@ process.on('message', function(payload) {
|
||||
});
|
||||
|
||||
function minifyScripts(scripts, callback) {
|
||||
// The portions of code involving the source map are commented out as they're broken in UglifyJS2
|
||||
// Follow along here: https://github.com/mishoo/UglifyJS2/issues/700
|
||||
try {
|
||||
var minified = uglifyjs.minify(scripts, {
|
||||
// outSourceMap: "nodebb.min.js.map",
|
||||
compress: false
|
||||
}),
|
||||
hasher = crypto.createHash('md5'),
|
||||
@@ -56,7 +60,7 @@ function minifyScripts(scripts, callback) {
|
||||
payload: hash.slice(0, 8)
|
||||
});
|
||||
|
||||
callback(minified.code);
|
||||
callback(minified.code/*, minified.map*/);
|
||||
} catch(err) {
|
||||
process.send({
|
||||
type: 'error',
|
||||
|
||||
11
nodebb
11
nodebb
@@ -35,7 +35,7 @@ switch(process.argv[2]) {
|
||||
process.stdout.write('\t"' + './nodebb restart'.yellow + '" to restart NodeBB\n\n');
|
||||
} else {
|
||||
process.stdout.write('\nNodeBB is not running\n'.bold);
|
||||
process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n');
|
||||
process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n'.reset);
|
||||
}
|
||||
})
|
||||
break;
|
||||
@@ -44,7 +44,7 @@ switch(process.argv[2]) {
|
||||
process.stdout.write('\nStarting NodeBB\n'.bold);
|
||||
process.stdout.write(' "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n');
|
||||
process.stdout.write(' "' + './nodebb log'.yellow + '" to view server output\n');
|
||||
process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n');
|
||||
process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset);
|
||||
|
||||
// Spawn a new NodeBB process
|
||||
cproc.fork(__dirname + '/loader.js', {
|
||||
@@ -91,7 +91,8 @@ switch(process.argv[2]) {
|
||||
break;
|
||||
|
||||
case 'log':
|
||||
process.stdout.write('\nType '.red + 'Ctrl-C '.bold + 'to exit\n\n'.red);
|
||||
process.stdout.write('\nType '.red + 'Ctrl-C '.bold + 'to exit'.red);
|
||||
process.stdout.write('\n\n'.reset);
|
||||
cproc.spawn('tail', ['-F', './logs/output.log'], {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit'
|
||||
@@ -144,7 +145,7 @@ switch(process.argv[2]) {
|
||||
} else {
|
||||
var message = 'NodeBB Upgrade Complete!',
|
||||
spaces = new Array(Math.floor(process.stdout.columns / 2) - (message.length / 2) + 1).join(' ');
|
||||
process.stdout.write('\n' + spaces + message.green.bold + '\n\n');
|
||||
process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset);
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -162,6 +163,6 @@ switch(process.argv[2]) {
|
||||
process.stdout.write('\t' + 'upgrade'.yellow + '\tRun NodeBB upgrade scripts, ensure packages are up-to-date\n');
|
||||
process.stdout.write('\t' + 'dev'.yellow + '\tStart NodeBB in interactive development mode\n');
|
||||
process.stdout.write('\t' + 'watch'.yellow + '\tStart NodeBB in development mode and watch for changes\n');
|
||||
process.stdout.write('\n');
|
||||
process.stdout.write('\n'.reset);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -28,18 +28,19 @@
|
||||
"daemon": "~1.1.0",
|
||||
"express": "^4.9.5",
|
||||
"express-session": "^1.8.2",
|
||||
"lwip": "0.0.7",
|
||||
"gravatar": "^1.1.0",
|
||||
"heapdump": "^0.3.0",
|
||||
"less": "^2.0.0",
|
||||
"logrotate-stream": "^0.2.3",
|
||||
"lru-cache": "^2.6.1",
|
||||
"lwip": "0.0.7",
|
||||
"mime": "^1.3.4",
|
||||
"minimist": "^1.1.1",
|
||||
"mkdirp": "~0.5.0",
|
||||
"mmmagic": "^0.3.13",
|
||||
"morgan": "^1.3.2",
|
||||
"nconf": "~0.7.1",
|
||||
"nodebb-plugin-composer-default": "^1.0.2",
|
||||
"nodebb-plugin-dbsearch": "^0.2.12",
|
||||
"nodebb-plugin-emoji-extended": "^0.4.8",
|
||||
"nodebb-plugin-markdown": "^3.0.0",
|
||||
@@ -48,8 +49,8 @@
|
||||
"nodebb-plugin-spam-be-gone": "^0.4.0",
|
||||
"nodebb-rewards-essentials": "^0.0.1",
|
||||
"nodebb-theme-lavender": "^1.0.42",
|
||||
"nodebb-theme-persona": "^0.2.0",
|
||||
"nodebb-theme-vanilla": "^1.1.0",
|
||||
"nodebb-theme-persona": "^1.0.0",
|
||||
"nodebb-theme-vanilla": "^2.0.0",
|
||||
"nodebb-widget-essentials": "^1.0.2",
|
||||
"npm": "^2.1.4",
|
||||
"passport": "^0.2.1",
|
||||
@@ -68,7 +69,7 @@
|
||||
"string": "^3.0.0",
|
||||
"templates.js": "^0.2.6",
|
||||
"touch": "0.0.3",
|
||||
"uglify-js": "git+https://github.com/julianlam/UglifyJS2.git",
|
||||
"uglify-js": "^2.4.23",
|
||||
"underscore": "~1.8.3",
|
||||
"underscore.deep": "^0.5.1",
|
||||
"validator": "^3.30.0",
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
"guest-login-post": "يجب عليك تسجيل الدخول للرد",
|
||||
"no_topics": "<strong>لا توجد مواضيع في هذه الفئة</strong>لم لا تحاول إنشاء موضوع؟<br />",
|
||||
"browsing": "تصفح",
|
||||
"no_replies": "لم يرد أحد",
|
||||
"no_replies": "لا توجد ردود.",
|
||||
"share_this_category": "انشر هذه الفئة",
|
||||
"watch": "متابعة",
|
||||
"ignore": "تجاهل",
|
||||
"watch.message": "You are now watching updates from this category",
|
||||
"ignore.message": "You are now ignoring updates from this category"
|
||||
"watch.message": "أنت اﻷن متابع لتحديثات هذه الفئة",
|
||||
"ignore.message": "أنت اﻷن تتجاهل تحديثات هذه الفئة"
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
"welcome.text1": "شكرًا على تسجيلك في %1!",
|
||||
"welcome.text2": "لتفعيل حسابك، نحتاج إلى التأكد من صحة عنوان البريد الإلكتروني الذي تسجلت به.",
|
||||
"welcome.cta": "انقر هنا لتفعيل عنوان بريدك الإلكتروني",
|
||||
"reset.text1": "لقد توصلنا بطلب إعادة تعيين كلمة السرالخاصة بك، ربما لكونك قد نسيتها, إن لم يكن الأمر كذلك، المرجو تجاهل هذه الرسالة.",
|
||||
"reset.text2": "لمواصلة طلب إعاة تعيين كلمة السر، المرجو تتبع هذا الرابط.",
|
||||
"reset.text1": "لقد توصلنا بطلب إعادة تعيين كلمة المرور الخاصة بك، ربما لكونك قد نسيتها, إن لم يكن الأمر كذلك، المرجو تجاهل هذه الرسالة.",
|
||||
"reset.text2": "لمواصلة طلب إعاة تعيين كلمة المرور، الرجاء تتبع هذا الرابط.",
|
||||
"reset.cta": "انقر هنا لإعادة تعيين كلمة السر الخاصة بك.",
|
||||
"reset.notify.subject": "تم تغيير كلمة المرور بنجاح",
|
||||
"reset.notify.text1": "نحيطك علما أن كلمة مرورك قد تم تغييرها في %1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"invalid-data": "بيانات غير صالحة",
|
||||
"not-logged-in": "لم تقم بتسجيل الدخول",
|
||||
"account-locked": "تم حظر حسابك مؤقتًا.",
|
||||
"search-requires-login": "Searching requires an account - please login or register.",
|
||||
"search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل",
|
||||
"invalid-cid": "قائمة غير موجودة",
|
||||
"invalid-tid": "موضوع غير متواجد",
|
||||
"invalid-pid": "رد غير موجود",
|
||||
@@ -18,10 +18,10 @@
|
||||
"username-taken": "اسم المستخدم مأخوذ",
|
||||
"email-taken": "البريد الالكتروني مأخوذ",
|
||||
"email-not-confirmed": "عنوان بريدك الإلكتروني غير مفعل بعد. انقر هنا لتفعيله من فضلك.",
|
||||
"email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.",
|
||||
"email-not-confirmed-chat": "لا يمكنك الدردشة حتى تقوم بتأكيد بريدك الإلكتروني، الرجاء إضغط هنا لتأكيد بريدك اﻹلكتروني.",
|
||||
"no-email-to-confirm": "هذا المنتدى يستلزم تفعيل بريدك الإلكتروني، انقر هنا من فضلك لإدخاله.",
|
||||
"email-confirm-failed": "لم نستطع تفعيل بريدك الإلكتروني، المرجو المحاولة لاحقًا.",
|
||||
"confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.",
|
||||
"confirm-email-already-sent": "لقد تم ارسال بريد التأكيد، الرجاء اﻹنتظار 1% دقائق لإعادة اﻹرسال",
|
||||
"username-too-short": "اسم المستخدم قصير.",
|
||||
"username-too-long": "اسم المستخدم طويل",
|
||||
"user-banned": "المستخدم محظور",
|
||||
@@ -57,8 +57,8 @@
|
||||
"group-name-too-short": "اسم المجموعة قصير",
|
||||
"group-already-exists": "المجموعة موجودة مسبقا",
|
||||
"group-name-change-not-allowed": "لايسمح بتغيير أسماء المجموعات",
|
||||
"group-already-member": "You are already part of this group",
|
||||
"group-needs-owner": "This group requires at least one owner",
|
||||
"group-already-member": "أنت بالفعل عضو في هذه المجموعة ",
|
||||
"group-needs-owner": "هذه المجموعة تتطلب مالك واحد على اﻷقل",
|
||||
"post-already-deleted": "سبق وتم حذف هذا الرد",
|
||||
"post-already-restored": "سبق وتم إلغاء حذف هذا الرد",
|
||||
"topic-already-deleted": "سبق وتم حذف هذا الموضوع",
|
||||
@@ -67,7 +67,7 @@
|
||||
"topic-thumbnails-are-disabled": "الصور المصغرة غير مفعلة.",
|
||||
"invalid-file": "ملف غير مقبول",
|
||||
"uploads-are-disabled": "رفع الملفات غير مفعل",
|
||||
"signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).",
|
||||
"signature-too-long": "عذرا، توقيعك يجب ألا يتجاوز %1 حرفًا.",
|
||||
"about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).",
|
||||
"cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك",
|
||||
"chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.",
|
||||
@@ -78,7 +78,7 @@
|
||||
"not-enough-reputation-to-flag": "ليس لديك سمعة تكفي للإشعار بموضوع مخل",
|
||||
"reload-failed": "المنتدى واجه مشكلة أثناء إعادة التحميل: \"%1\". سيواصل المنتدى خدمة العملاء السابقين لكن يجب عليك إلغاء أي تغيير قمت به قبل إعادة التحميل.",
|
||||
"registration-error": "حدث خطأ أثناء التسجيل",
|
||||
"parse-error": "Something went wrong while parsing server response",
|
||||
"parse-error": "حدث خطأ ما أثناء تحليل استجابة الخادم",
|
||||
"wrong-login-type-email": "الرجاء استعمال بريدك اﻹلكتروني للدخول",
|
||||
"wrong-login-type-username": "الرجاء استعمال اسم المستخدم الخاص بك للدخول"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"search": "بحث",
|
||||
"buttons.close": "أغلق",
|
||||
"403.title": "غير مسموح بالدخول",
|
||||
"403.message": "You seem to have stumbled upon a page that you do not have access to.",
|
||||
"403.message": "يبدو أنك قد تعثرت على صفحة لا تمتلك الصلاحية للدخول إليها",
|
||||
"403.login": "Perhaps you should <a href='%1/login'>try logging in</a>?",
|
||||
"404.title": "لم يتم العثور",
|
||||
"404.message": "You seem to have stumbled upon a page that does not exist. Return to the <a href='%1/'>home page</a>.",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"chat.seven_days": "7 أيام",
|
||||
"chat.thirty_days": "30 يومًا",
|
||||
"chat.three_months": "3 أشهر",
|
||||
"composer.compose": "Compose",
|
||||
"composer.compose": "اكتب",
|
||||
"composer.show_preview": "عرض المعاينة",
|
||||
"composer.hide_preview": "إخفاء المعاينة",
|
||||
"composer.user_said_in": "%1 كتب في %2",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"user.followers": "المستخدمون الذين يتبعون %1",
|
||||
"user.posts": "ردود %1",
|
||||
"user.topics": "مواضيع %1",
|
||||
"user.groups": "%1's Groups",
|
||||
"user.groups": "مجموعات %1",
|
||||
"user.favourites": "مفضلات %1",
|
||||
"user.settings": "خيارات المستخدم",
|
||||
"user.watched": "المواضيع المتابعة من قبل %1 ",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"enter_email_address": "ادخل عنوان البريد الإلكتروني",
|
||||
"password_reset_sent": "إعادة تعيين كلمة السر أرسلت",
|
||||
"invalid_email": "بريد إلكتروني غير صالح أو غير موجود",
|
||||
"password_too_short": "The password entered is too short, please pick a different password.",
|
||||
"passwords_do_not_match": "The two passwords you've entered do not match.",
|
||||
"password_expired": "Your password has expired, please choose a new password"
|
||||
"password_too_short": "كلمة المرور التي أدخلتها قصيرة، الرجاء اختر كلمة مرور مختلفة",
|
||||
"passwords_do_not_match": "كلمتا السر التي أدخلتهما غير متطابقتان",
|
||||
"password_expired": "لقد انتهت صلاحية كلمة المرور الخاصة بك، الرجاء اختيار كلمة مرور جديدة"
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"results_matching": "%1 نتيجة (نتائج) موافقة ل \"%2\", (%3 ثواني)",
|
||||
"no-matches": "No matches found",
|
||||
"results_matching": "%1 نتيجة (نتائج) موافقة لـ \"%2\", (%3 ثواني)",
|
||||
"no-matches": "لم يتم العثور على نتائج.",
|
||||
"advanced-search": "بحث متقدم",
|
||||
"in": "في",
|
||||
"titles": "العناوين",
|
||||
"titles-posts": "العناوين والمشاركات",
|
||||
"posted-by": "Posted by",
|
||||
"posted-by": "مشاركة من طرف",
|
||||
"in-categories": "في الفئات",
|
||||
"search-child-categories": "بحث في الفئات الفرعية",
|
||||
"reply-count": "عدد المشاركات",
|
||||
@@ -22,7 +22,7 @@
|
||||
"three-months": "ثلاثة أشهر",
|
||||
"six-months": "ستة أشهر",
|
||||
"one-year": "عام",
|
||||
"sort-by": "Sort by",
|
||||
"sort-by": "عرض حسب",
|
||||
"last-reply-time": "تاريخ آخر رد",
|
||||
"topic-title": "عنوان الموضوع",
|
||||
"number-of-replies": "عدد الردود",
|
||||
@@ -30,8 +30,8 @@
|
||||
"topic-start-date": "تاريخ بدأ الموضوع",
|
||||
"username": "اسم المستخدم",
|
||||
"category": "فئة",
|
||||
"descending": "In descending order",
|
||||
"ascending": "In ascending order",
|
||||
"descending": "في ترتيب تنازلي",
|
||||
"ascending": "في ترتيب تصاعدي",
|
||||
"save-preferences": "حفظ التفضيلات",
|
||||
"clear-preferences": "ازالة التفضيلات",
|
||||
"search-preferences-saved": "تم حفظ تفضيلات البحث",
|
||||
|
||||
@@ -79,6 +79,6 @@
|
||||
"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",
|
||||
"follow_topics_you_reply_to": "متابعة المواضيع التي تقوم بالرد فيها",
|
||||
"follow_topics_you_create": "متابعة المواضيع التي تنشئها",
|
||||
"grouptitle": "Select the group title you would like to display",
|
||||
"grouptitle": "حدد عنوان المجموعة الذي تريد عرضه",
|
||||
"no-group-title": "لا يوجد عنوان للمجموعة"
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"search": "بحث",
|
||||
"enter_username": "أدخل اسم مستخدم للبحث",
|
||||
"load_more": "حمل المزيد",
|
||||
"users-found-search-took": "%1 user(s) found! Search took %2 seconds.",
|
||||
"users-found-search-took": "تم إيجاد %1 مستخدمـ(ين)! استغرق البحث %2 ثانية.",
|
||||
"filter-by": "Filter By",
|
||||
"online-only": "المتصلون فقط",
|
||||
"picture-only": "صورة فقط"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"invalid-data": "Daten ungültig",
|
||||
"not-logged-in": "Du bist nicht angemeldet.",
|
||||
"account-locked": "Dein Account wurde vorübergehend gesperrt.",
|
||||
"search-requires-login": "Searching requires an account - please login or register.",
|
||||
"search-requires-login": "Die Suche erfordert ein Konto, bitte einloggen oder registrieren.",
|
||||
"invalid-cid": "Ungültige Kategorie-ID",
|
||||
"invalid-tid": "Ungültige Themen-ID",
|
||||
"invalid-pid": "Ungültige Beitrags-ID",
|
||||
@@ -21,11 +21,11 @@
|
||||
"email-not-confirmed-chat": "Deine E-Mail wurde noch nicht bestätigt. Bitte klicke hier, um deine E-Mail zu bestätigen.",
|
||||
"no-email-to-confirm": "Dieses Forum setzt E-Mail-Bestätigung voraus, bitte klick hier um eine E-Mail-Adresse einzugeben",
|
||||
"email-confirm-failed": "Wir konnten deine E-Mail-Adresse nicht bestätigen, bitte versuch es später noch einmal",
|
||||
"confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.",
|
||||
"confirm-email-already-sent": "Bestätigungsmail wurde verschickt, bitte warten %1 Minute(n) warten um eine weitere zu verschicken.",
|
||||
"username-too-short": "Benutzername ist zu kurz",
|
||||
"username-too-long": "Der Benutzername ist zu lang",
|
||||
"user-banned": "Der Benutzer ist gesperrt",
|
||||
"user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post",
|
||||
"user-too-new": "Entschuldigung, Sie müssen %1 Sekunde(n) warten, bevor Sie ihren ersten Beitrag schreiben können.",
|
||||
"no-category": "Die Kategorie existiert nicht",
|
||||
"no-topic": "Das Thema existiert nicht",
|
||||
"no-post": "Der Beitrag existiert nicht",
|
||||
@@ -36,17 +36,17 @@
|
||||
"no-emailers-configured": "Es wurde keine E-Mail-Plugins geladen, weshalb eine Test-E-Mail nicht gesendet werden konnte.",
|
||||
"category-disabled": "Kategorie ist deaktiviert",
|
||||
"topic-locked": "Thema ist gesperrt",
|
||||
"post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting",
|
||||
"post-edit-duration-expired": "Entschuldigung, Sie dürfen Beiträge nur %1 Sekunde(n) nach dem veröffentlichen editieren.",
|
||||
"still-uploading": "Bitte warte bis der Vorgang abgeschlossen ist.",
|
||||
"content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).",
|
||||
"content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).",
|
||||
"content-too-short": "Bitte schreiben Sie einen längeren Beitrag. Beiträge sollten mindestens %1 Zeichen enthalten.",
|
||||
"content-too-long": "Bitte schreiben Sie einen kürzeren Beitrag. Beiträge können nicht länger als %1 Zeichen sein.",
|
||||
"title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).",
|
||||
"title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).",
|
||||
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
|
||||
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
|
||||
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
|
||||
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
|
||||
"file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file",
|
||||
"tag-too-short": "Bitte geben Sie ein längeres Schlagwort ein. Tags sollten mindestens %1 Zeichen enthalten.",
|
||||
"tag-too-long": "Bitte geben Sie ein kürzeres Schlagwort ein. Tags können nicht länger als %1 Zeichen sein.",
|
||||
"file-too-big": "Die maximale Dateigröße ist %1 kB, bitte laden Sie eine kleinere Datei hoch.",
|
||||
"cant-vote-self-post": "Du kannst deinen eigenen Beitrag nicht bewerten",
|
||||
"already-favourited": "Dieser Beitrag ist bereits in deinen Favoriten enthalten",
|
||||
"already-unfavourited": "Du hast diesen Beitrag bereits aus deinen Favoriten entfernt",
|
||||
@@ -67,8 +67,8 @@
|
||||
"topic-thumbnails-are-disabled": "Vorschaubilder für Themen sind deaktiviert",
|
||||
"invalid-file": "Datei ungültig",
|
||||
"uploads-are-disabled": "Uploads sind deaktiviert",
|
||||
"signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).",
|
||||
"about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).",
|
||||
"signature-too-long": "Entschuldigung, Ihre Signatur kann nicht länger als %1 Zeichen sein.",
|
||||
"about-me-too-long": "Entschuldigung, Ihr \"über mich\" kann nicht länger als %1 Zeichen sein.",
|
||||
"cant-chat-with-yourself": "Du kannst nicht mit dir selber chatten!",
|
||||
"chat-restricted": "Dieser Benutzer hat seine Chatfunktion eingeschränkt. Du kannst nur mit diesem Benutzer chatten, wenn er dir folgt.",
|
||||
"too-many-messages": "Du hast zu viele Nachrichten versandt, bitte warte eine Weile.",
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"topic": "Thema",
|
||||
"topic_id": "Topic ID",
|
||||
"topic_id_placeholder": "Topic ID eingeben",
|
||||
"no_topics_found": "Keine passenden Themen gefunden.",
|
||||
"topic_id": "Themen-ID",
|
||||
"topic_id_placeholder": "Themen-ID eingeben",
|
||||
"no_topics_found": "Keine passenden Themen gefunden!",
|
||||
"no_posts_found": "Keine Beiträge gefunden!",
|
||||
"post_is_deleted": "Dieser Beitrag wurde gelöscht!",
|
||||
"topic_is_deleted": "This topic is deleted!",
|
||||
"topic_is_deleted": "Dieses Thema wurde gelöscht!",
|
||||
"profile": "Profil",
|
||||
"posted_by": "Geschrieben von %1",
|
||||
"posted_by": "Verfasst von %1",
|
||||
"posted_by_guest": "Verfasst von einem Gast",
|
||||
"chat": "Chat",
|
||||
"notify_me": "Erhalte eine Benachrichtigung bei neuen Antworten zu diesem Thema.",
|
||||
"quote": "zitieren",
|
||||
"reply": "antworten",
|
||||
"quote": "Zitieren",
|
||||
"reply": "Antworten",
|
||||
"guest-login-reply": "Anmelden zum Antworten",
|
||||
"edit": "bearbeiten",
|
||||
"delete": "löschen",
|
||||
"purge": "säubern",
|
||||
"edit": "Bearbeiten",
|
||||
"delete": "Löschen",
|
||||
"purge": "Bereinigen",
|
||||
"restore": "Wiederherstellen",
|
||||
"move": "verschieben",
|
||||
"move": "Verschieben",
|
||||
"fork": "Aufspalten",
|
||||
"link": "Link",
|
||||
"share": "Teilen",
|
||||
"tools": "Tools",
|
||||
"tools": "Werkzeuge",
|
||||
"flag": "Markieren",
|
||||
"locked": "Gesperrt",
|
||||
"bookmark_instructions": "Klicke hier um zur letzten Position zurückzukehren oder schließe zum Abbrechen.",
|
||||
"bookmark_instructions": "Klicke hier, um zur letzten Position zurückzukehren oder schließe zum Abbrechen.",
|
||||
"flag_title": "Diesen Beitrag zur Moderation markieren",
|
||||
"flag_confirm": "Sind Sie sicher, dass Sie diesen Post markieren möchten?",
|
||||
"flag_success": "Dieser Beitrag wurde erfolgreich für die Moderation markiert.",
|
||||
"deleted_message": "Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.",
|
||||
"following_topic.message": "Du erhälst nun eine Benachrichtigung, wenn jemand einen Beitrag zu diesem Thema verfasst.",
|
||||
"not_following_topic.message": "Du erhälst keine weiteren Benachrichtigungen zu diesem Thema.",
|
||||
"not_following_topic.message": "Du erhälst keine weiteren Benachrichtigungen zu diesem Thema mehr.",
|
||||
"login_to_subscribe": "Bitte registrieren oder einloggen um dieses Thema zu abonnieren",
|
||||
"markAsUnreadForAll.success": "Thema für Alle als ungelesen markiert.",
|
||||
"watch": "Beobachten",
|
||||
@@ -39,10 +39,10 @@
|
||||
"watch.title": "Bei neuen Antworten benachrichtigen",
|
||||
"unwatch.title": "Dieses Thema nicht mehr beobachten",
|
||||
"share_this_post": "Diesen Beitrag teilen",
|
||||
"thread_tools.title": "Themen-Tools",
|
||||
"thread_tools.title": "Themen-Werkzeuge",
|
||||
"thread_tools.markAsUnreadForAll": "Als ungelesen markieren",
|
||||
"thread_tools.pin": "Thema anpinnen",
|
||||
"thread_tools.unpin": "Thema nicht mehr anpinnen",
|
||||
"thread_tools.pin": "Thema anheften",
|
||||
"thread_tools.unpin": "Thema nicht mehr anheften",
|
||||
"thread_tools.lock": "Thema schließen",
|
||||
"thread_tools.unlock": "Thema öffnen",
|
||||
"thread_tools.move": "Thema verschieben",
|
||||
@@ -53,11 +53,11 @@
|
||||
"thread_tools.restore": "Thema wiederherstellen",
|
||||
"thread_tools.restore_confirm": "Bist du sicher, dass du dieses Thema wiederherstellen möchtest?",
|
||||
"thread_tools.purge": "Thema säubern",
|
||||
"thread_tools.purge_confirm": "Bist du sicher, dass du dieses Thema säubern möchtest?",
|
||||
"topic_move_success": "Thema wurde erfolgreich zu %1 verschoben.",
|
||||
"thread_tools.purge_confirm": "Bist du sicher, dass du dieses Thema bereinigen möchtest?",
|
||||
"topic_move_success": "Thema wurde erfolgreich nach %1 verschoben.",
|
||||
"post_delete_confirm": "Sind Sie sicher, dass Sie diesen Beitrag löschen möchten?",
|
||||
"post_restore_confirm": "Sind Sie sicher, dass Sie diesen Beitrag wiederherstellen möchten?",
|
||||
"post_purge_confirm": "Sind Sie sicher, das Sie diesen Beitrag säubern möchten?",
|
||||
"post_purge_confirm": "Sind Sie sicher, das Sie diesen Beitrag bereinigen möchten?",
|
||||
"load_categories": "Kategorien laden",
|
||||
"disabled_categories_note": "Deaktivierte Kategorien sind ausgegraut.",
|
||||
"confirm_move": "Verschieben",
|
||||
@@ -72,29 +72,29 @@
|
||||
"post_moved": "Beitrag wurde verschoben!",
|
||||
"fork_topic": "Thema aufspalten",
|
||||
"topic_will_be_moved_to": "Dieses Thema wird verschoben nach",
|
||||
"fork_topic_instruction": "Klicke auf die Beiträge, die du aufspalten willst",
|
||||
"fork_topic_instruction": "Klicke auf die Beiträge, die aufgespaltet werden sollen",
|
||||
"fork_no_pids": "Keine Beiträge ausgewählt!",
|
||||
"fork_success": "Thema erfolgreich abgespalten! Klicke hier, um zum abgespalteten Thema zu gelangen.",
|
||||
"fork_success": "Thema erfolgreich aufgespalten! Klicke hier, um zum aufgespalteten Thema zu gelangen.",
|
||||
"composer.title_placeholder": "Hier den Titel des Themas eingeben...",
|
||||
"composer.handle_placeholder": "Name",
|
||||
"composer.discard": "Verwerfen",
|
||||
"composer.submit": "Absenden",
|
||||
"composer.replying_to": "Antworte auf %1",
|
||||
"composer.new_topic": "Neues Thema",
|
||||
"composer.uploading": "Upload läuft...",
|
||||
"composer.uploading": "Lade hoch...",
|
||||
"composer.thumb_url_label": "Vorschaubild-URL hier einfügen",
|
||||
"composer.thumb_title": "Vorschaubild zu diesem Thema hinzufügen",
|
||||
"composer.thumb_url_placeholder": "http://example.com/thumb.png",
|
||||
"composer.thumb_file_label": "Oder eine Datei hochladen",
|
||||
"composer.thumb_remove": "Felder leeren",
|
||||
"composer.drag_and_drop_images": "Bilder hier reinziehen",
|
||||
"composer.drag_and_drop_images": "Bilder hierher ziehen",
|
||||
"more_users_and_guests": "%1 weitere(r) Nutzer und %2 Gäste",
|
||||
"more_users": "%1 weitere(r) Nutzer",
|
||||
"more_guests": "%1 weitere Gäste",
|
||||
"users_and_others": "%1 und %2 andere",
|
||||
"sort_by": "Sortieren nach",
|
||||
"oldest_to_newest": "Älteste zuerst",
|
||||
"newest_to_oldest": "Neuster zuerst",
|
||||
"newest_to_oldest": "Neuste zuerst",
|
||||
"most_votes": "Die meisten Stimmen",
|
||||
"most_posts": "Die meisten Beiträge"
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"watched": "Beobachtet",
|
||||
"followers": "Folger",
|
||||
"following": "Folgt",
|
||||
"aboutme": "About me",
|
||||
"aboutme": "Über mich",
|
||||
"signature": "Signatur",
|
||||
"gravatar": "Gravatar",
|
||||
"birthday": "Geburtstag",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"new_topic_button": "Nuevo tema",
|
||||
"guest-login-post": "Acceder para poder escribir un mensaje",
|
||||
"no_topics": "<strong>No hay temas en esta categoría.</strong><br />¿Por que no te animas y publicas uno?",
|
||||
"no_topics": "<strong>No hay temas en esta categoría.</strong><br />¿Por qué no te animas y publicas uno?",
|
||||
"browsing": "viendo ahora",
|
||||
"no_replies": "Nadie ha respondido aún",
|
||||
"share_this_category": "Compartir esta categoría",
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
"greeting_with_name": "Hola %1",
|
||||
"welcome.text1": "Gracias por registrarte con %1!",
|
||||
"welcome.text2": "Para activar completamente tu cuenta, necesitamos verificar que la dirección email con la que te registraste te pertenece.",
|
||||
"welcome.cta": "Cliquea aquí para confirmar tu dirección email.",
|
||||
"reset.text1": "Recibimos una solicitud para reiniciar tu contraseña, posiblemente porque la olvidaste. Si no es así, por favor ignora este email.",
|
||||
"welcome.cta": "Cliquea aquí para confirmar tu dirección de email.",
|
||||
"reset.text1": "Hemos recibido una solicitud para reiniciar tu contraseña, posiblemente porque la olvidaste. Si no es así, por favor, ignora este email.",
|
||||
"reset.text2": "Para continuar con el reinicio de contraseña, por favor cliquea en el siguiente vínculo:",
|
||||
"reset.cta": "Cliquea aquí para reiniciar tu contraseña",
|
||||
"reset.notify.subject": "Se ha modificado correctamente la contraseña.",
|
||||
"reset.notify.text1": "Te estamos notificando que a a %1, tu contraseña ha sido cambiado correctamente.",
|
||||
"reset.notify.text2": "Si no has sido tu, por favor notifica al administrador inmediatamente.",
|
||||
"reset.notify.text1": "Te estamos notificando que en %1, tu contraseña ha sido cambiada correctamente.",
|
||||
"reset.notify.text2": "Si no has sido tú, por favor notifica al administrador inmediatamente.",
|
||||
"digest.notifications": "Tiene notificaciones sin leer de %1:",
|
||||
"digest.latest_topics": "Últimos temas de %1",
|
||||
"digest.cta": "Cliquea aquí para visitar %1",
|
||||
"digest.unsub.info": "Este compendio te fue enviado debido a tus ajustes de subscripción.",
|
||||
"digest.no_topics": "No han habido temas activos en el pasado %1",
|
||||
"digest.no_topics": "No han habido temas activos anteriores %1",
|
||||
"notif.chat.subject": "Nuevo mensaje de chat recibido de %1",
|
||||
"notif.chat.cta": "Haz click aquí para continuar la conversación",
|
||||
"notif.chat.unsub.info": "Esta notificación de chat se te envió debido a tus ajustes de suscripción.",
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"open_links_in_new_tab": "Abrir los enlaces externos en una nueva pestaña",
|
||||
"enable_topic_searching": "Activar la búsqueda \"in-topic\"",
|
||||
"topic_search_help": "Si está activada, la búsqueda 'in-topic' sustituirá el comportamiento por defecto del navegador y le permitirá buscar en el tema al completo, en vez de hacer una búsqueda únicamente sobre el contenido mostrado en pantalla",
|
||||
"follow_topics_you_reply_to": "Seguir los temas en las que respondes",
|
||||
"follow_topics_you_reply_to": "Seguir los temas en los que respondes",
|
||||
"follow_topics_you_create": "Seguir publicaciones que creas",
|
||||
"grouptitle": "Selecciona el título del grupo que deseas visualizar",
|
||||
"no-group-title": "Sin título de grupo"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"password-reset-requested": "Parooli muutmise taotlus - %1!",
|
||||
"welcome-to": "Tere tulemast foorumisse %1",
|
||||
"password-reset-requested": "Parooli muutmise taotlus - %1",
|
||||
"welcome-to": "Tere tulemast %1 foorumisse",
|
||||
"greeting_no_name": "Tere",
|
||||
"greeting_with_name": "Tere %1",
|
||||
"welcome.text1": "Täname et oled registreerinud foorumisse %1!",
|
||||
"welcome.text1": "Täname et oled registreerinud %1 foorumisse!",
|
||||
"welcome.text2": "Konto täielikuks aktiveerimiseks peame me kinnitama, et registreerimisel kasutatud e-mail kuulub teile.",
|
||||
"welcome.cta": "Vajuta siia, et kinnitada oma e-maili aadress",
|
||||
"reset.text1": "Meile laekus päring parooli muutmiseks. Kui päring ei ole teie poolt esitatud või te ei soovi parooli muuta, siis võite antud kirja ignoreerida.",
|
||||
"reset.text2": "Selleks, et jätkata parooli muutmisega vajuta järgnevale lingile:",
|
||||
"reset.cta": "Vajuta siia, et taotleda uut parooli",
|
||||
"reset.notify.subject": "Parool edukalt muudetud",
|
||||
"reset.notify.text1": "Teavitame sind, et sinu parool %1 foorumis on edukalt muudetud.",
|
||||
"reset.notify.text1": "Teatame, et sinu parooli muutmine kuupäeval %1 oli edukas.",
|
||||
"reset.notify.text2": "Kui te ei ole lubanud seda, siis teavitage koheselt administraatorit.",
|
||||
"digest.notifications": "Sul on lugemata teateid %1 poolt:",
|
||||
"digest.latest_topics": "Viimased teemad %1 poolt",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"alert.follow": "Sa jälgid nüüd %1!",
|
||||
"online": "Sees",
|
||||
"users": "Kasutajad",
|
||||
"topics": "Teemad",
|
||||
"topics": "Teemat",
|
||||
"posts": "Postitust",
|
||||
"views": "Vaatamist",
|
||||
"reputation": "Reputatsioon",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"user_mentioned_you_in": "<strong>%1</strong> mainis sind postituses <strong>%2</strong>",
|
||||
"user_started_following_you": "<strong>%1</strong> hakkas sind jälgima.",
|
||||
"email-confirmed": "Emaili aadress kinnitatud",
|
||||
"email-confirmed-message": "Täname, et kinnitasite oma emaili aadressi. Teie kasutaja omn nüüd täielikult aktiveeritud.",
|
||||
"email-confirmed-message": "Täname, et kinnitasite oma emaili aadressi. Teie kasutaja on nüüd täielikult aktiveeritud.",
|
||||
"email-confirm-error-message": "Emaili aadressi kinnitamisel tekkis viga. Võibolla kinnituskood oli vale või aegunud.",
|
||||
"email-confirm-sent": "Kinnituskiri on saadetud."
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"new_topic_button": "새 주제 생성",
|
||||
"guest-login-post": "Log in to post",
|
||||
"guest-login-post": "로그인",
|
||||
"no_topics": "<strong>이 카테고리에는 생성된 주제가 없습니다.</strong><br />먼저 주제를 생성해 보세요.",
|
||||
"browsing": "이 주제를 읽고 있는 사용자",
|
||||
"no_replies": "답글이 없습니다.",
|
||||
"share_this_category": "이 카테고리를 공유",
|
||||
"watch": "Watch",
|
||||
"watch": "관심 주제",
|
||||
"ignore": "관심 해제",
|
||||
"watch.message": "You are now watching updates from this category",
|
||||
"ignore.message": "You are now ignoring updates from this category"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"new_topic_button": "Nytt emne",
|
||||
"guest-login-post": "Logg inn til innlegg",
|
||||
"no_topics": "<strong>Det er ingen emner i denne kategorien</strong><br />Hvorfor ikke lage ett?",
|
||||
"no_topics": "<strong>Det er ingen emner i denne kategorien</strong><br />Hvorfor ikke lage et?",
|
||||
"browsing": "leser",
|
||||
"no_replies": "Ingen har svart",
|
||||
"share_this_category": "Del denne kategorien",
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"greeting_no_name": "Hallo",
|
||||
"greeting_with_name": "Hallo, %1",
|
||||
"welcome.text1": "Takk for at du registrerte deg hos %1!",
|
||||
"welcome.text2": "For å aktivere kontoen din må vi verifisere at du eier epost-adressen du registrerte med.",
|
||||
"welcome.cta": "Klikk her for å verifisere epost-adressen din",
|
||||
"welcome.text2": "For å aktivere kontoen din må vi verifisere at du eier e-postadressen du registrerte med.",
|
||||
"welcome.cta": "Klikk her for å verifisere e-postadressen din",
|
||||
"reset.text1": "Vi har blir bedt om å tilbakestille passordet ditt, muligens fordi du har glemt det. Hvis dette ikke stemmer kan du ignorere denne eposten.",
|
||||
"reset.text2": "For å fortsette med tilbakestillingen, vennligst klikk på følgende lenke:",
|
||||
"reset.cta": "Klikk her for å tilbakestille passordet ditt",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"invalid-data": "Ugyldig data",
|
||||
"invalid-data": "Ugyldige data",
|
||||
"not-logged-in": "Du ser ikke ut til å være logget inn.",
|
||||
"account-locked": "Kontoen din har blitt midlertidig låst",
|
||||
"search-requires-login": "Searching requires an account - please login or register.",
|
||||
"search-requires-login": "Søking krever en konto - vennligst logg inn eller registrer deg.",
|
||||
"invalid-cid": "Ugyldig kategori-ID",
|
||||
"invalid-tid": "Ugyldig emne-ID",
|
||||
"invalid-pid": "Ugyldig innlegg-ID",
|
||||
@@ -21,11 +21,11 @@
|
||||
"email-not-confirmed-chat": "Du kan ikke chatte før e-posten din er bekreftet, vennligst klikk her for å bekrefte e-postadressen.",
|
||||
"no-email-to-confirm": "Dette forumet krever at e-postbekreftelse, vennligst klikk her for å skrive inn en e-post",
|
||||
"email-confirm-failed": "Vi kunne ikke godkjenne e-posten din, vennligst prøv igjen senere.",
|
||||
"confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.",
|
||||
"confirm-email-already-sent": "Bekreftelsesepost allerede sendt, vennligst vent %1 minutt(er) for å sende enn til.",
|
||||
"username-too-short": "Brukernavnet er for kort",
|
||||
"username-too-long": "Brukernavnet er for langt",
|
||||
"user-banned": "Bruker utestengt",
|
||||
"user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post",
|
||||
"user-too-new": "Beklager, du må vente %1 sekund(er) før du lager ditt første innlegg",
|
||||
"no-category": "Kategorien eksisterer ikke.",
|
||||
"no-topic": "Emne eksisterer ikke",
|
||||
"no-post": "Innlegg eksisterer ikke",
|
||||
@@ -36,24 +36,24 @@
|
||||
"no-emailers-configured": "Ingen e-post-tillegg er lastet, så ingen test e-post kunne bli sendt",
|
||||
"category-disabled": "Kategori deaktivert",
|
||||
"topic-locked": "Emne låst",
|
||||
"post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting",
|
||||
"post-edit-duration-expired": "Du har bare lov til å redigere innlegg i %1 sekund(er) etter posting",
|
||||
"still-uploading": "Vennligst vent til opplastingene blir fullført.",
|
||||
"content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).",
|
||||
"content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).",
|
||||
"title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).",
|
||||
"title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).",
|
||||
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
|
||||
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
|
||||
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
|
||||
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
|
||||
"file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file",
|
||||
"content-too-short": "Vennligst skriv et lengre innlegg. Innlegg må inneholde minst %1 tegn.",
|
||||
"content-too-long": "Vennligst skriv et kortere innlegg. Innlegg kan ikke være lengre enn %1 tegn.",
|
||||
"title-too-short": "Vennligst skriv en lengre tittel. Titler må inneholde minst %1 tegn.",
|
||||
"title-too-long": "Vennligst skriv en kortere tittel. Tittel kan ikke være lengre enn %1 tegn.",
|
||||
"too-many-posts": "Du kan bare poste hvert %1 sekund – vennligst vent før du poster igjen",
|
||||
"too-many-posts-newbie": "Som en ny bruker kan du bare poste en gang hvert %1. sekund, før du har opparbeidet %2 rykte – vennligst vent før du poster igjen",
|
||||
"tag-too-short": "Vennligst skriv en lengre tag. Tagger må være på minst %1 tegn",
|
||||
"tag-too-long": "Vennligst skriv en kortere tag. Tagger kan ikke være lengre enn %1 tegn",
|
||||
"file-too-big": "Største tillatte filstørrelse er %1 kB – vennligst last opp en mindre fil",
|
||||
"cant-vote-self-post": "Du kan ikke stemme på ditt eget innlegg",
|
||||
"already-favourited": "Du har allerede favorittmerket dette innlegget",
|
||||
"already-unfavourited": "Du har allerede avfavorisert dette innlegget",
|
||||
"cant-ban-other-admins": "Du kan ikke utestenge andre administratorer!",
|
||||
"invalid-image-type": "Ugyldig bildetype. Tilatte typer er: %1",
|
||||
"invalid-image-extension": "Ugyldig bildefiltype",
|
||||
"invalid-file-type": "Invalid file type. Allowed types are: %1",
|
||||
"invalid-file-type": "Ugyldig filtype. Tillatte typer er: %1",
|
||||
"group-name-too-short": "Gruppenavnet er for kort",
|
||||
"group-already-exists": "Gruppe eksisterer allerede",
|
||||
"group-name-change-not-allowed": "Gruppenavn ikke tillatt",
|
||||
@@ -63,12 +63,12 @@
|
||||
"post-already-restored": "Dette innlegget har allerede blitt gjenopprettet",
|
||||
"topic-already-deleted": "Dette emnet har allerede blitt slettet",
|
||||
"topic-already-restored": "Dette emnet har allerede blitt gjenopprettet",
|
||||
"cant-purge-main-post": "You can't purge the main post, please delete the topic instead",
|
||||
"cant-purge-main-post": "Du kan ikke slette hovedinnlegget. Vennligst slett emnet i stedet.",
|
||||
"topic-thumbnails-are-disabled": "Emne-minatyrbilder har blitt deaktivert",
|
||||
"invalid-file": "Ugyldig fil",
|
||||
"uploads-are-disabled": "Opplastninger er deaktivert",
|
||||
"signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).",
|
||||
"about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).",
|
||||
"signature-too-long": "Beklager, din signatur kan ikke være lengre enn %1 tegn",
|
||||
"about-me-too-long": "Beklager, din om meg kan ikke være lengre enn %1 tegn.",
|
||||
"cant-chat-with-yourself": "Du kan ikke chatte med deg selv!",
|
||||
"chat-restricted": "Denne brukeren har begrenset sine chat-meldinger. De må følge deg før du kan chatte med dem",
|
||||
"too-many-messages": "Du har sendt for mange meldinger, vennligst vent en stund.",
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"details.kick": "Kast ut",
|
||||
"details.owner_options": "Gruppeadministrasjon",
|
||||
"details.group_name": "Gruppenavn",
|
||||
"details.member_count": "Member Count",
|
||||
"details.creation_date": "Creation Date",
|
||||
"details.member_count": "Antall medlemmer",
|
||||
"details.creation_date": "Opprettelsesdato",
|
||||
"details.description": "Beskrivelse",
|
||||
"details.badge_preview": "Forhåndsvisning av skilt",
|
||||
"details.change_icon": "Endre ikon",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"title": "Varsler",
|
||||
"no_notifs": "Du har ingen nye varsler",
|
||||
"see_all": "Se alle varsler",
|
||||
"mark_all_read": "Mark all notifications read",
|
||||
"mark_all_read": "Merk alle varsler som lest",
|
||||
"back_to_home": "Tilbake til %1",
|
||||
"outgoing_link": "Utgående link",
|
||||
"outgoing_link_message": "Du forlater nå %1.",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"year": "År",
|
||||
"alltime": "All tid",
|
||||
"no_recent_topics": "Det er ingen nye tråder.",
|
||||
"no_popular_topics": "There are no popular topics.",
|
||||
"no_popular_topics": "Det er ingen populære emner.",
|
||||
"there-is-a-new-topic": "Det finnes et nytt emne.",
|
||||
"there-is-a-new-topic-and-a-new-post": "Det finnes et nytt emne og et nytt innlegg.",
|
||||
"there-is-a-new-topic-and-new-posts": "Det finnes et nytt emne og %1 nye innlegg.",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"enter_email_address": "Skriv e-post",
|
||||
"password_reset_sent": "Passord-tilbakestilling sendt",
|
||||
"invalid_email": "Ugyldig e-post / e-post eksisterer ikke",
|
||||
"password_too_short": "The password entered is too short, please pick a different password.",
|
||||
"passwords_do_not_match": "The two passwords you've entered do not match.",
|
||||
"password_expired": "Your password has expired, please choose a new password"
|
||||
"password_too_short": "Passordet du skrev inn er for kort, velg et lengre passord.",
|
||||
"passwords_do_not_match": "Passordene du har skrevet inn stemmer ikke overens.",
|
||||
"password_expired": "Passordet ditt er utgått, velg et nytt passord"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"results_matching": "%1 resultat(er) samsvarer med \"%2\", (%3 sekunder)",
|
||||
"no-matches": "Ingen matcher funnet",
|
||||
"advanced-search": "Advanced Search",
|
||||
"advanced-search": "Avansert søk",
|
||||
"in": "I",
|
||||
"titles": "Titler",
|
||||
"titles-posts": "Titler og innlegg",
|
||||
@@ -30,11 +30,11 @@
|
||||
"topic-start-date": "Starttid for emne",
|
||||
"username": "Brukernavn",
|
||||
"category": "Kategori",
|
||||
"descending": "In descending order",
|
||||
"ascending": "In ascending order",
|
||||
"descending": "I synkende rekkefølge",
|
||||
"ascending": "I stigende rekkefølge",
|
||||
"save-preferences": "Lagre innstillinger",
|
||||
"clear-preferences": "Tøm innstillinnger",
|
||||
"search-preferences-saved": "Søkeinnstillinger lagret",
|
||||
"search-preferences-cleared": "Søkeinnstillinger tømt",
|
||||
"show-results-as": "Vis resultateter som"
|
||||
"show-results-as": "Vis resultater som"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"no_tag_topics": "Det er ingen emnet med denne taggen.",
|
||||
"tags": "Tagger",
|
||||
"enter_tags_here": "Enter tags here, between %1 and %2 characters each.",
|
||||
"enter_tags_here": "Skriv tagger her, mellom %1 og %2 tegn hver.",
|
||||
"enter_tags_here_short": "Skriv tagger...",
|
||||
"no_tags": "Det finnes ingen tagger enda."
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"no_topics_found": "Ingen emner funnet!",
|
||||
"no_posts_found": "Ingen innlegg funnet!",
|
||||
"post_is_deleted": "Dette innlegget er slettet!",
|
||||
"topic_is_deleted": "This topic is deleted!",
|
||||
"topic_is_deleted": "Dette emnet er slettet!",
|
||||
"profile": "Profil",
|
||||
"posted_by": "Skapt av %1",
|
||||
"posted_by_guest": "Skapt av Gjest",
|
||||
@@ -13,7 +13,7 @@
|
||||
"notify_me": "Bli varslet om nye svar i dette emnet",
|
||||
"quote": "Siter",
|
||||
"reply": "Svar",
|
||||
"guest-login-reply": "Log in to reply",
|
||||
"guest-login-reply": "Logg inn for å besvare",
|
||||
"edit": "Endre",
|
||||
"delete": "Slett",
|
||||
"purge": "Tøm",
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
"watched": "Overvåkede",
|
||||
"followers": "Følgere",
|
||||
"following": "Følger",
|
||||
"aboutme": "About me",
|
||||
"aboutme": "Om meg",
|
||||
"signature": "Signatur",
|
||||
"gravatar": "Gravatar",
|
||||
"birthday": "Bursdag",
|
||||
"chat": "Chatt",
|
||||
"chat": "Chat",
|
||||
"follow": "Følg",
|
||||
"unfollow": "Avfølg",
|
||||
"more": "More",
|
||||
"more": "Mer",
|
||||
"profile_update_success": "Profilen ble oppdatert!",
|
||||
"change_picture": "Bytt bilde",
|
||||
"edit": "Endre",
|
||||
@@ -60,8 +60,8 @@
|
||||
"digest_weekly": "Ukentlig",
|
||||
"digest_monthly": "Månedlig",
|
||||
"send_chat_notifications": "Send en epost hvis jeg mottar en chat-melding når jeg ikke er pålogget",
|
||||
"send_post_notifications": "Send an email when replies are made to topics I am subscribed to",
|
||||
"settings-require-reload": "Some setting changes require a reload. Click here to reload the page.",
|
||||
"send_post_notifications": "Send en e-post når svar postes til emner jeg abonnerer på",
|
||||
"settings-require-reload": "Noen innstillingsendringer krever at du laster siden på nytt. Klikk her for å laste på nytt.",
|
||||
"has_no_follower": "Denne brukeren har ingen følgere :(",
|
||||
"follows_no_one": "Denne brukeren følger ingen :(",
|
||||
"has_no_posts": "Denne brukeren har ikke skrevet noe enda.",
|
||||
@@ -69,16 +69,16 @@
|
||||
"has_no_watched_topics": "Denne brukeren overvåker ingen innlegg foreløpig.",
|
||||
"email_hidden": "E-post skjult",
|
||||
"hidden": "skjult",
|
||||
"paginate_description": "Paginate topics and posts instead of using infinite scroll",
|
||||
"paginate_description": "Bruk sidevelger for emner og innlegg istedet for uendelig scrolling",
|
||||
"topics_per_page": "Tråd per side",
|
||||
"posts_per_page": "Innlegg per side",
|
||||
"notification_sounds": "Play a sound when you receive a notification",
|
||||
"notification_sounds": "Spill en lyd når du mottar et varsel",
|
||||
"browsing": "Surfeinnstillinger",
|
||||
"open_links_in_new_tab": "Open outgoing links in new tab",
|
||||
"open_links_in_new_tab": "Åpne utgående lenker i en ny fane",
|
||||
"enable_topic_searching": "Aktiver søk-i-emne",
|
||||
"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",
|
||||
"follow_topics_you_reply_to": "Follow topics that you reply to",
|
||||
"follow_topics_you_create": "Follow topics you create",
|
||||
"grouptitle": "Select the group title you would like to display",
|
||||
"no-group-title": "No group title"
|
||||
"topic_search_help": "Hvis søk-i-emne er aktivert, overstyres nettleserens standard sidesøk og gir mulighet til å søke gjennom hele emnet, ikke bare det som vises på skjermen",
|
||||
"follow_topics_you_reply_to": "Følg emner du besvarer",
|
||||
"follow_topics_you_create": "Følg emner du oppretter",
|
||||
"grouptitle": "Velg gruppetittelen du vil vise",
|
||||
"no-group-title": "Ingen gruppetittel"
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"search": "Søk",
|
||||
"enter_username": "Skriv ett brukernavn for å søke",
|
||||
"load_more": "Last flere",
|
||||
"users-found-search-took": "%1 user(s) found! Search took %2 seconds.",
|
||||
"users-found-search-took": "%1 bruker(e) funnet. Søket tok %2 sekunder.",
|
||||
"filter-by": "Filtrer etter",
|
||||
"online-only": "Bare påloggede",
|
||||
"picture-only": "Bare bilde"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"invalid-data": "Ongeldige Data",
|
||||
"not-logged-in": "De account lijkt op dit moment niet aangemeld te zijn.",
|
||||
"account-locked": "De account is tijdelijk vergrendeld",
|
||||
"search-requires-login": "Searching requires an account - please login or register.",
|
||||
"search-requires-login": "Zoeken vereist een account - gelieve aan te melden of te registreren.",
|
||||
"invalid-cid": "Ongeldig categoriesleutel",
|
||||
"invalid-tid": "Ongeldig id voor onderwerp",
|
||||
"invalid-pid": "Ongeldig berichtkenmerk",
|
||||
@@ -67,8 +67,8 @@
|
||||
"topic-thumbnails-are-disabled": "Miniatuurweergaven bij onderwerpen uitgeschakeld. ",
|
||||
"invalid-file": "Ongeldig bestand",
|
||||
"uploads-are-disabled": "Uploads zijn uitgeschakeld",
|
||||
"signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).",
|
||||
"about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).",
|
||||
"signature-too-long": "Sorry, je onderschrift kan niet langer zijn da %1 karakter(s).",
|
||||
"about-me-too-long": "Sorry, je over mij kan niet langer zijn da %1 karakter(s).",
|
||||
"cant-chat-with-yourself": "Het is niet mogelijk met jezelf een chatgesprek te houden.",
|
||||
"chat-restricted": "Deze gebruiker heeft beperkingen aan de chatfunctie opgelegd waardoor deze eerst iemand moet volgen voordat deze persoon een nieuwe chat mag initiëren.",
|
||||
"too-many-messages": "Er zijn in korte tijd teveel berichten verzonden, een moment geduld.",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"no_topics_found": "Geen onderwerpen gevonden!",
|
||||
"no_posts_found": "Geen berichten gevonden!",
|
||||
"post_is_deleted": "Dit bericht is verwijderd!",
|
||||
"topic_is_deleted": "This topic is deleted!",
|
||||
"topic_is_deleted": "Dit onderwerp is verwijderd!",
|
||||
"profile": "Profiel",
|
||||
"posted_by": "Geplaatst door %1",
|
||||
"posted_by_guest": "Geplaatst door gast",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"watched": "Bekeken",
|
||||
"followers": "Volgers",
|
||||
"following": "Volgend",
|
||||
"aboutme": "About me",
|
||||
"aboutme": "Over mij",
|
||||
"signature": "Handtekening",
|
||||
"gravatar": "Gravatar",
|
||||
"birthday": "Verjaardag",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"title": "Olästa",
|
||||
"no_unread_topics": "Det finns inga ilästa ämnen.",
|
||||
"no_unread_topics": "Det finns inga olästa ämnen.",
|
||||
"load_more": "Ladda fler",
|
||||
"mark_as_read": "Markerad som läst",
|
||||
"selected": "Vald",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
@import "./modules/alerts";
|
||||
@import "./modules/selectable";
|
||||
@import "./modules/checkboxes";
|
||||
|
||||
.admin {
|
||||
padding-top: 70px;
|
||||
@@ -358,10 +359,4 @@
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.groups-list {
|
||||
.description {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,78 @@
|
||||
div.categories {
|
||||
ul {
|
||||
.no-select;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
ul {
|
||||
.no-select;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
> li > ul > li {
|
||||
margin-left: 4.5rem;
|
||||
}
|
||||
|
||||
> li > ul > li {
|
||||
margin-left: 4.5rem;
|
||||
}
|
||||
}
|
||||
.row {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
border-bottom: 1px dashed #ddd;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: inline-block;
|
||||
.stats {
|
||||
display: inline-block;
|
||||
|
||||
li {
|
||||
min-height: 0;
|
||||
display: inline;
|
||||
margin: 0 @acp-margin 0 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
li {
|
||||
min-height: 0;
|
||||
display: inline;
|
||||
margin: 0 @acp-margin 0 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
min-height: @acp-line-height;
|
||||
margin: @acp-base-line 0;
|
||||
li {
|
||||
min-height: @acp-line-height;
|
||||
margin: @acp-base-line 0;
|
||||
|
||||
&.placeholder {
|
||||
border: 1px dashed #2196F3;
|
||||
background-color: #E1F5FE;
|
||||
}
|
||||
}
|
||||
&.placeholder {
|
||||
border: 1px dashed #2196F3;
|
||||
background-color: #E1F5FE;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
.disabled {
|
||||
|
||||
.icon, .header, .description {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.icon, .header, .description {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.stats {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
.stats {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: @acp-line-height;
|
||||
height: @acp-line-height;
|
||||
border-radius: 50%;
|
||||
line-height: @acp-line-height;
|
||||
text-align: center;
|
||||
vertical-align: bottom;
|
||||
background-size: cover;
|
||||
float: left;
|
||||
margin-right: @acp-margin;
|
||||
cursor: move;
|
||||
}
|
||||
.icon {
|
||||
width: @acp-line-height;
|
||||
height: @acp-line-height;
|
||||
border-radius: 50%;
|
||||
line-height: @acp-line-height;
|
||||
text-align: center;
|
||||
vertical-align: bottom;
|
||||
background-size: cover;
|
||||
float: left;
|
||||
margin-right: @acp-margin;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.information {
|
||||
float: left;
|
||||
}
|
||||
.information {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-top: 0;
|
||||
margin-bottom: @acp-base-line;
|
||||
}
|
||||
.header {
|
||||
margin-top: 0;
|
||||
margin-bottom: @acp-base-line;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
22
public/less/admin/modules/checkboxes.less
Normal file
22
public/less/admin/modules/checkboxes.less
Normal file
@@ -0,0 +1,22 @@
|
||||
[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox label {
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.admin {
|
||||
.fa-toggle-on, .fa-toggle-off {
|
||||
font-size: 21px;
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
||||
.fa-toggle-on {
|
||||
color: @brand-success;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
/*global define, socket, app, ajaxify, utils, Mousetrap, Hammer, RELATIVE_PATH*/
|
||||
/*global define, socket, app, ajaxify, utils, bootbox, Mousetrap, Hammer, RELATIVE_PATH*/
|
||||
|
||||
(function() {
|
||||
$(document).ready(function() {
|
||||
@@ -20,6 +20,7 @@
|
||||
selectMenuItem(data.url);
|
||||
setupHeaderMenu();
|
||||
setupRestartLinks();
|
||||
setupCheckboxes();
|
||||
});
|
||||
|
||||
$('[component="logout"]').on('click', app.logout);
|
||||
@@ -75,15 +76,6 @@
|
||||
socket.emit('admin.restart');
|
||||
});
|
||||
|
||||
Mousetrap.bind('ctrl+shift+a d', function() {
|
||||
var tid = ajaxify.variables.get('topic_id'),
|
||||
cid = ajaxify.variables.get('category_id');
|
||||
|
||||
if (tid && cid) {
|
||||
socket.emit('topics.delete', { tids: [tid], cid: cid });
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('/', function(e) {
|
||||
$('#acp-search input').focus();
|
||||
|
||||
@@ -177,4 +169,26 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupCheckboxes() {
|
||||
$('[type=checkbox]').each(function() {
|
||||
var checkbox = $(this),
|
||||
checked = checkbox.is(':checked');
|
||||
|
||||
if (checked) {
|
||||
checkbox.after('<i class="fa fa-toggle-on"></i>');
|
||||
}
|
||||
else {
|
||||
checkbox.after('<i class="fa fa-toggle-off"></i>');
|
||||
}
|
||||
});
|
||||
|
||||
$('[type=checkbox]').change(function() {
|
||||
var checked = $(this).is(':checked');
|
||||
|
||||
$(this).siblings('[class*=fa-]').toggleClass('fa-toggle-off', !checked)
|
||||
.toggleClass('fa-toggle-on', checked);
|
||||
});
|
||||
}
|
||||
|
||||
}());
|
||||
@@ -181,28 +181,32 @@ define('admin/extend/plugins', function() {
|
||||
socket.emit('admin.plugins.toggleInstall', {
|
||||
id: pluginID,
|
||||
version: version
|
||||
}, function(err, status) {
|
||||
}, function(err, pluginData) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (status.installed) {
|
||||
btn.html('<i class="fa fa-trash-o"></i> Uninstall');
|
||||
} else {
|
||||
btn.html('<i class="fa fa-download"></i> Install');
|
||||
}
|
||||
var targetList = (pluginData.installed ? 'installed' : 'download'),
|
||||
otherList = (pluginData.installed ? 'download' : 'installed'),
|
||||
payload = {};
|
||||
|
||||
activateBtn.toggleClass('hidden', !status.installed);
|
||||
payload[targetList] = pluginData;
|
||||
templates.parse('admin/partials/' + targetList + '_plugin_item', payload, function(html) {
|
||||
var pluginList = $('ul.' + targetList);
|
||||
|
||||
btn.toggleClass('btn-danger', status.installed)
|
||||
.toggleClass('btn-success', !status.installed)
|
||||
.attr('disabled', false)
|
||||
.attr('data-installed', status.installed ? 1 : 0);
|
||||
pluginList.append(html);
|
||||
$('ul.' + otherList).find('li[data-plugin-id="' + pluginID + '"]').slideUp('slow', function() {
|
||||
$(this).remove();
|
||||
$('html,body').animate({
|
||||
scrollTop: pluginList.find('li').last().offset().top - 48
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
app.alert({
|
||||
alert_id: 'plugin_toggled',
|
||||
title: 'Plugin ' + (status.installed ? 'Installed' : 'Uninstalled'),
|
||||
message: status.installed ? 'Plugin successfully installed, please activate the plugin.' : 'The plugin has been successfully deactivated and uninstalled.',
|
||||
title: 'Plugin ' + (pluginData.installed ? 'Installed' : 'Uninstalled'),
|
||||
message: pluginData.installed ? 'Plugin successfully installed, please activate the plugin.' : 'The plugin has been successfully deactivated and uninstalled.',
|
||||
type: 'info',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
@@ -11,10 +11,10 @@ define('admin/extend/rewards', function() {
|
||||
conditionals;
|
||||
|
||||
rewards.init = function() {
|
||||
available = JSON.parse(ajaxify.variables.get('rewards'));
|
||||
active = JSON.parse(ajaxify.variables.get('active'));
|
||||
conditions = JSON.parse(ajaxify.variables.get('conditions'));
|
||||
conditionals = JSON.parse(ajaxify.variables.get('conditionals'));
|
||||
available = ajaxify.data.rewards;
|
||||
active = ajaxify.data.active;
|
||||
conditions = ajaxify.data.conditions;
|
||||
conditionals = ajaxify.data.conditionals;
|
||||
|
||||
$('[data-selected]').each(function() {
|
||||
select($(this));
|
||||
@@ -96,7 +96,7 @@ define('admin/extend/rewards', function() {
|
||||
inputs.forEach(function(input) {
|
||||
html += '<label for="' + input.name + '">' + input.label + '<br />';
|
||||
switch (input.type) {
|
||||
case 'select':
|
||||
case 'select':
|
||||
html += '<select name="' + input.name + '">';
|
||||
input.values.forEach(function(value) {
|
||||
html += '<option value="' + value.value + '">' + value.name + '</option>';
|
||||
@@ -119,7 +119,7 @@ define('admin/extend/rewards', function() {
|
||||
|
||||
for (var reward in rewards) {
|
||||
if (rewards.hasOwnProperty(reward)) {
|
||||
div.find('[name="' + reward + '"]').val(rewards[reward]);
|
||||
div.find('[name="' + reward + '"]').val(rewards[reward]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -155,7 +155,7 @@ define('admin/extend/rewards', function() {
|
||||
var data = {rewards: {}},
|
||||
main = $(this).find('form.main').serializeArray(),
|
||||
rewards = $(this).find('form.rewards').serializeArray();
|
||||
|
||||
|
||||
main.forEach(function(obj) {
|
||||
data[obj.name] = obj.value;
|
||||
});
|
||||
|
||||
@@ -7,7 +7,17 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
rooms: false,
|
||||
graphs: false
|
||||
},
|
||||
isMobile = false;
|
||||
isMobile = false,
|
||||
graphData = {
|
||||
rooms: {},
|
||||
traffic: {}
|
||||
};
|
||||
|
||||
var DEFAULTS = {
|
||||
roomInterval: 10000,
|
||||
graphInterval: 15000,
|
||||
realtimeInterval: 1500
|
||||
};
|
||||
|
||||
|
||||
Admin.init = function() {
|
||||
@@ -16,12 +26,6 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
|
||||
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
|
||||
intervals.rooms = setInterval(function() {
|
||||
if (app.isFocused && app.isConnected) {
|
||||
socket.emit('meta.rooms.getAll', Admin.updateRoomUsage);
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
$(window).on('action:ajaxify.start', function(ev, data) {
|
||||
clearInterval(intervals.rooms);
|
||||
clearInterval(intervals.graphs);
|
||||
@@ -57,8 +61,11 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
}
|
||||
});
|
||||
|
||||
setupGraphs();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
setupRealtimeButton();
|
||||
setupGraphs();
|
||||
initiateDashboard();
|
||||
};
|
||||
|
||||
Admin.updateRoomUsage = function(err, data) {
|
||||
@@ -66,6 +73,12 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (JSON.stringify(graphData.rooms) === JSON.stringify(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
graphData.rooms = data;
|
||||
|
||||
var html = '<div class="text-center pull-left">' +
|
||||
'<div>'+ data.onlineRegisteredCount +'</div>' +
|
||||
'<div>Users</div>' +
|
||||
@@ -225,6 +238,12 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
color: "#949FB1",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Recent/Unread"
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
color: "#4D5360",
|
||||
highlight: "#A8B3C5",
|
||||
label: "Other"
|
||||
}
|
||||
], {
|
||||
responsive: true
|
||||
@@ -238,7 +257,6 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
}
|
||||
};
|
||||
|
||||
intervals.graphs = setInterval(updateTrafficGraph, 15000);
|
||||
updateTrafficGraph();
|
||||
|
||||
$(window).on('resize', adjustPieCharts);
|
||||
@@ -263,6 +281,12 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
}
|
||||
|
||||
socket.emit('admin.analytics.get', {graph: "traffic"}, function (err, data) {
|
||||
if (JSON.stringify(graphData.traffic) === JSON.stringify(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
graphData.traffic = data;
|
||||
|
||||
for (var i = 0, ii = data.pageviews.length; i < ii; i++) {
|
||||
graphs.traffic.datasets[0].points[i].value = data.pageviews[i];
|
||||
graphs.traffic.datasets[1].points[i].value = data.uniqueVisitors[i];
|
||||
@@ -293,6 +317,7 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
graphs.presence.segments[1].value = users.topics;
|
||||
graphs.presence.segments[2].value = users.category;
|
||||
graphs.presence.segments[3].value = users.recent;
|
||||
graphs.presence.segments[4].value = users.other;
|
||||
|
||||
graphs.presence.update();
|
||||
}
|
||||
@@ -390,5 +415,33 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
||||
graphs.topics.update();
|
||||
}
|
||||
|
||||
function setupRealtimeButton() {
|
||||
$('#toggle-realtime .fa').on('click', function() {
|
||||
var $this = $(this);
|
||||
if ($this.hasClass('fa-toggle-on')) {
|
||||
$this.removeClass('fa-toggle-on').addClass('fa-toggle-off');
|
||||
$this.parent().find('strong').html('OFF');
|
||||
initiateDashboard(false);
|
||||
} else {
|
||||
$this.removeClass('fa-toggle-off').addClass('fa-toggle-on');
|
||||
$this.parent().find('strong').html('ON');
|
||||
initiateDashboard(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initiateDashboard(realtime) {
|
||||
clearInterval(intervals.rooms);
|
||||
clearInterval(intervals.graphs);
|
||||
|
||||
intervals.rooms = setInterval(function() {
|
||||
if (app.isFocused && app.isConnected) {
|
||||
socket.emit('meta.rooms.getAll', Admin.updateRoomUsage);
|
||||
}
|
||||
}, realtime ? DEFAULTS.realtimeInterval : DEFAULTS.roomInterval);
|
||||
|
||||
intervals.graphs = setInterval(updateTrafficGraph, realtime ? DEFAULTS.realtimeInterval : DEFAULTS.graphInterval);
|
||||
}
|
||||
|
||||
return Admin;
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ define('admin/general/navigation', ['translator'], function(translator) {
|
||||
available;
|
||||
|
||||
navigation.init = function() {
|
||||
available = JSON.parse(ajaxify.variables.get('available'));
|
||||
available = ajaxify.data.available;
|
||||
|
||||
$('#enabled').html(translator.unescape($('#enabled').html()));
|
||||
translator.translate(translator.unescape($('#available').html()), function(html) {
|
||||
|
||||
@@ -70,9 +70,9 @@ define('admin/manage/category', [
|
||||
|
||||
$this.addClass('hide');
|
||||
target.removeClass('hide').on('blur', function() {
|
||||
$this.removeClass('hide').children('span').html(this.value);
|
||||
$this.removeClass('hide').children('span').text(this.value).html();
|
||||
$(this).addClass('hide');
|
||||
}).val($this.children('span').html());
|
||||
}).val($this.children('span').html().text());
|
||||
|
||||
target.focus();
|
||||
});
|
||||
@@ -95,7 +95,7 @@ define('admin/manage/category', [
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
socket.emit('admin.categories.purge', ajaxify.variables.get('cid'), function(err) {
|
||||
socket.emit('admin.categories.purge', ajaxify.data.category.cid, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -138,10 +138,10 @@ define('admin/manage/category', [
|
||||
});
|
||||
|
||||
// Parent Category Selector
|
||||
$('button[data-action="setParent"]').on('click', Category.launchParentSelector);
|
||||
$('button[data-action="setParent"], button[data-action="changeParent"]').on('click', Category.launchParentSelector);
|
||||
$('button[data-action="removeParent"]').on('click', function() {
|
||||
var payload= {};
|
||||
payload[ajaxify.variables.get('cid')] = {
|
||||
payload[ajaxify.data.category.cid] = {
|
||||
parentCid: 0
|
||||
};
|
||||
|
||||
@@ -149,7 +149,8 @@ define('admin/manage/category', [
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
ajaxify.refresh();
|
||||
$('button[data-action="removeParent"]').parent().addClass('hide');
|
||||
$('button[data-action="setParent"]').removeClass('hide');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -192,7 +193,7 @@ define('admin/manage/category', [
|
||||
};
|
||||
|
||||
Category.refreshPrivilegeTable = function() {
|
||||
socket.emit('admin.categories.getPrivilegeSettings', ajaxify.variables.get('cid'), function(err, privileges) {
|
||||
socket.emit('admin.categories.getPrivilegeSettings', ajaxify.data.category.cid, function(err, privileges) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -230,7 +231,7 @@ define('admin/manage/category', [
|
||||
|
||||
Category.setPrivilege = function(member, privilege, state, checkboxEl) {
|
||||
socket.emit('admin.categories.setPrivilege', {
|
||||
cid: ajaxify.variables.get('cid'),
|
||||
cid: ajaxify.data.category.cid,
|
||||
privilege: privilege,
|
||||
set: state,
|
||||
member: member
|
||||
@@ -258,7 +259,7 @@ define('admin/manage/category', [
|
||||
var parentCid = $(this).attr('data-cid'),
|
||||
payload = {};
|
||||
|
||||
payload[ajaxify.variables.get('cid')] = {
|
||||
payload[ajaxify.data.category.cid] = {
|
||||
parentCid: parentCid
|
||||
};
|
||||
|
||||
@@ -268,7 +269,8 @@ define('admin/manage/category', [
|
||||
}
|
||||
|
||||
modal.modal('hide');
|
||||
ajaxify.refresh();
|
||||
$('button[data-action="removeParent"]').parent().removeClass('hide');
|
||||
$('button[data-action="setParent"]').addClass('hide');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -287,7 +289,7 @@ define('admin/manage/category', [
|
||||
|
||||
autocomplete.user(inputEl, function(ev, ui) {
|
||||
socket.emit('admin.categories.setPrivilege', {
|
||||
cid: ajaxify.variables.get('cid'),
|
||||
cid: ajaxify.data.category.cid,
|
||||
privilege: ['find', 'read'],
|
||||
set: true,
|
||||
member: ui.item.user.uid
|
||||
@@ -315,7 +317,7 @@ define('admin/manage/category', [
|
||||
|
||||
autocomplete.group(inputEl, function(ev, ui) {
|
||||
socket.emit('admin.categories.setPrivilege', {
|
||||
cid: ajaxify.variables.get('cid'),
|
||||
cid: ajaxify.data.category.cid,
|
||||
privilege: ['groups:find', 'groups:read'],
|
||||
set: true,
|
||||
member: ui.item.group.name
|
||||
|
||||
@@ -18,7 +18,7 @@ define('admin/manage/group', [
|
||||
searchDelay;
|
||||
|
||||
|
||||
var groupName = ajaxify.variables.get('groupName');
|
||||
var groupName = ajaxify.data.group.name;
|
||||
|
||||
changeGroupUserTitle.keyup(function() {
|
||||
groupLabelPreview.text(changeGroupUserTitle.val());
|
||||
|
||||
@@ -4,7 +4,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable)
|
||||
var Users = {};
|
||||
|
||||
Users.init = function() {
|
||||
var yourid = ajaxify.variables.get('yourid');
|
||||
var yourid = ajaxify.data.yourid;
|
||||
|
||||
selectable.enable('#users-container', '.user-selectable');
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ $(document).ready(function() {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
ajaxify.data = data;
|
||||
data.relative_path = RELATIVE_PATH;
|
||||
$(window).trigger('action:ajaxify.dataLoaded', {url: url, data: data});
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ app.cacheBuster = null;
|
||||
|
||||
switch(url_parts[0]) {
|
||||
case 'user':
|
||||
room = 'user/' + ajaxify.variables.get('theirid');
|
||||
room = 'user/' + ajaxify.data.theirid;
|
||||
break;
|
||||
case 'topic':
|
||||
room = 'topic_' + url_parts[1];
|
||||
@@ -466,21 +466,23 @@ app.cacheBuster = null;
|
||||
|
||||
function handleNewTopic() {
|
||||
$('#content').on('click', '#new_topic', function() {
|
||||
require(['composer'], function(composer) {
|
||||
var cid = ajaxify.variables.get('category_id');
|
||||
if (cid) {
|
||||
composer.newTopic(cid);
|
||||
} else {
|
||||
socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
if (categories.length) {
|
||||
composer.newTopic(categories[0].cid);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
var cid = ajaxify.data.cid;
|
||||
if (cid) {
|
||||
$(window).trigger('action:composer.topic.new', {
|
||||
cid: cid
|
||||
});
|
||||
} else {
|
||||
socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
if (categories.length) {
|
||||
$(window).trigger('action:composer.topic.new', {
|
||||
cid: categories[0].cid
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
currentEmail;
|
||||
|
||||
AccountEdit.init = function() {
|
||||
gravatarPicture = ajaxify.variables.get('gravatarpicture');
|
||||
uploadedPicture = ajaxify.variables.get('uploadedpicture');
|
||||
gravatarPicture = ajaxify.data.gravatarpicture;
|
||||
uploadedPicture = ajaxify.data.uploadedpicture;
|
||||
|
||||
header.init();
|
||||
|
||||
@@ -87,7 +87,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
}
|
||||
|
||||
function updateHeader(picture, username, userslug) {
|
||||
if (parseInt(ajaxify.variables.get('theirid'), 10) !== parseInt(ajaxify.variables.get('yourid'), 10)) {
|
||||
if (parseInt(ajaxify.data.theirid, 10) !== parseInt(ajaxify.data.yourid, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
$('#uploadPictureBtn').on('click', function() {
|
||||
|
||||
$('#change-picture-modal').modal('hide');
|
||||
uploader.open(config.relative_path + '/api/user/' + ajaxify.variables.get('userslug') + '/uploadpicture', {}, config.maximumProfileImageSize, function(imageUrlOnServer) {
|
||||
uploader.open(config.relative_path + '/api/user/' + ajaxify.data.userslug + '/uploadpicture', {}, config.maximumProfileImageSize, function(imageUrlOnServer) {
|
||||
onUploadComplete(imageUrlOnServer);
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
socket.emit('user.uploadProfileImageFromUrl', {url: url, uid: ajaxify.variables.get('theirid')}, function(err, imageUrlOnServer) {
|
||||
socket.emit('user.uploadProfileImageFromUrl', {url: url, uid: ajaxify.data.theirid}, function(err, imageUrlOnServer) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -286,7 +286,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
socket.emit('user.changePassword', {
|
||||
'currentPassword': currentPassword.val(),
|
||||
'newPassword': password.val(),
|
||||
'uid': ajaxify.variables.get('theirid')
|
||||
'uid': ajaxify.data.theirid
|
||||
}, function(err) {
|
||||
btn.removeClass('disabled').find('i').addClass('hide');
|
||||
currentPassword.val('');
|
||||
@@ -319,7 +319,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
function changeUserPicture(type, callback) {
|
||||
socket.emit('user.changePicture', {
|
||||
type: type,
|
||||
uid: ajaxify.variables.get('theirid')
|
||||
uid: ajaxify.data.theirid
|
||||
}, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ define('forum/account/header', function() {
|
||||
};
|
||||
|
||||
function hidePrivateLinks() {
|
||||
if (!app.user.uid || app.user.uid !== parseInt(ajaxify.variables.get('theirid'), 10)) {
|
||||
if (!app.user.uid || app.user.uid !== parseInt(ajaxify.data.theirid, 10)) {
|
||||
$('.account-sub-links .plugin-link.private').addClass('hide');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'],
|
||||
}
|
||||
|
||||
infinitescroll.loadMore(method, {
|
||||
uid: ajaxify.variables.get('theirid'),
|
||||
uid: ajaxify.data.theirid,
|
||||
after: $('[component="posts"]').attr('data-nextstart')
|
||||
}, function(data, done) {
|
||||
if (data.posts && data.posts.length) {
|
||||
|
||||
@@ -11,9 +11,9 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll',
|
||||
Account.init = function() {
|
||||
header.init();
|
||||
|
||||
yourid = ajaxify.variables.get('yourid');
|
||||
theirid = ajaxify.variables.get('theirid');
|
||||
isFollowing = ajaxify.variables.get('isFollowing');
|
||||
yourid = ajaxify.data.yourid;
|
||||
theirid = ajaxify.data.theirid;
|
||||
isFollowing = ajaxify.data.isFollowing;
|
||||
|
||||
app.enterRoom('user/' + theirid);
|
||||
|
||||
@@ -70,7 +70,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll',
|
||||
}
|
||||
|
||||
function onUserStatusChange(data) {
|
||||
if (parseInt(ajaxify.variables.get('theirid'), 10) !== parseInt(data.uid, 10)) {
|
||||
if (parseInt(ajaxify.data.theirid, 10) !== parseInt(data.uid, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll',
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
socket.emit('admin.user.banUsers', [ajaxify.variables.get('theirid')], function(err) {
|
||||
socket.emit('admin.user.banUsers', [ajaxify.data.theirid], function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -134,7 +134,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll',
|
||||
}
|
||||
|
||||
function unbanAccount() {
|
||||
socket.emit('admin.user.unbanUsers', [ajaxify.variables.get('theirid')], function(err) {
|
||||
socket.emit('admin.user.unbanUsers', [ajaxify.data.theirid], function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -150,7 +150,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll',
|
||||
return;
|
||||
}
|
||||
|
||||
socket.emit('admin.user.deleteUsers', [ajaxify.variables.get('theirid')], function(err) {
|
||||
socket.emit('admin.user.deleteUsers', [ajaxify.data.theirid], function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) {
|
||||
}
|
||||
});
|
||||
|
||||
socket.emit('user.saveSettings', {uid: ajaxify.variables.get('theirid'), settings: settings}, function(err, newSettings) {
|
||||
socket.emit('user.saveSettings', {uid: ajaxify.data.theirid, settings: settings}, function(err, newSettings) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) {
|
||||
}
|
||||
}
|
||||
app.exposeConfigToTemplates();
|
||||
if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.variables.get('theirid'), 10)) {
|
||||
if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) {
|
||||
app.alert({
|
||||
id: 'setting-change',
|
||||
message: '[[user:settings-require-reload]]',
|
||||
|
||||
@@ -9,7 +9,7 @@ define('forum/account/topics', ['forum/account/header', 'forum/infinitescroll'],
|
||||
AccountTopics.init = function() {
|
||||
header.init();
|
||||
|
||||
AccountTopics.handleInfiniteScroll('account/topics', 'uid:' + ajaxify.variables.get('theirid') + ':topics');
|
||||
AccountTopics.handleInfiniteScroll('account/topics', 'uid:' + ajaxify.data.theirid + ':topics');
|
||||
};
|
||||
|
||||
AccountTopics.handleInfiniteScroll = function(_template, _set) {
|
||||
|
||||
@@ -7,7 +7,7 @@ define('forum/account/watched', ['forum/account/header', 'forum/account/topics']
|
||||
AccountWatched.init = function() {
|
||||
header.init();
|
||||
|
||||
topics.handleInfiniteScroll('account/watched', 'uid:' + ajaxify.variables.get('theirid') + ':followed_tids');
|
||||
topics.handleInfiniteScroll('account/watched', 'uid:' + ajaxify.data.theirid + ':followed_tids');
|
||||
};
|
||||
|
||||
return AccountWatched;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
/* global define, config, templates, app, utils, ajaxify, socket */
|
||||
|
||||
define('forum/category', [
|
||||
'composer',
|
||||
'forum/pagination',
|
||||
'forum/infinitescroll',
|
||||
'share',
|
||||
@@ -11,7 +10,7 @@ define('forum/category', [
|
||||
'sort',
|
||||
'components',
|
||||
'translator'
|
||||
], function(composer, pagination, infinitescroll, share, navigator, categoryTools, sort, components, translator) {
|
||||
], function(pagination, infinitescroll, share, navigator, categoryTools, sort, components, translator) {
|
||||
var Category = {};
|
||||
|
||||
$(window).on('action:ajaxify.start', function(ev, data) {
|
||||
@@ -28,23 +27,23 @@ define('forum/category', [
|
||||
}
|
||||
|
||||
Category.init = function() {
|
||||
var cid = ajaxify.variables.get('category_id');
|
||||
var cid = ajaxify.data.cid;
|
||||
|
||||
app.enterRoom('category_' + cid);
|
||||
|
||||
share.addShareHandlers(ajaxify.variables.get('category_name'));
|
||||
share.addShareHandlers(ajaxify.data.name);
|
||||
|
||||
socket.removeListener('event:new_topic', Category.onNewTopic);
|
||||
socket.on('event:new_topic', Category.onNewTopic);
|
||||
|
||||
categoryTools.init(cid);
|
||||
|
||||
sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.variables.get('category_slug'));
|
||||
sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.data.slug);
|
||||
|
||||
enableInfiniteLoadingOrPagination();
|
||||
|
||||
if (!config.usePagination) {
|
||||
navigator.init('[component="category/topic"]', ajaxify.variables.get('topic_count'), Category.toTop, Category.toBottom, Category.navigatorCallback);
|
||||
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback);
|
||||
}
|
||||
|
||||
$('[component="category"]').on('click', '[component="topic/header"]', function() {
|
||||
@@ -84,7 +83,7 @@ define('forum/category', [
|
||||
};
|
||||
|
||||
Category.toBottom = function() {
|
||||
socket.emit('categories.getTopicCount', ajaxify.variables.get('category_id'), function(err, count) {
|
||||
socket.emit('categories.getTopicCount', ajaxify.data.cid, function(err, count) {
|
||||
navigator.scrollBottom(count - 1);
|
||||
});
|
||||
};
|
||||
@@ -158,7 +157,7 @@ define('forum/category', [
|
||||
}
|
||||
|
||||
var scrollTo = components.get('category/topic', 'index', bookmarkIndex);
|
||||
var cid = ajaxify.variables.get('category_id');
|
||||
var cid = ajaxify.data.cid;
|
||||
if (scrollTo.length && cid) {
|
||||
$('html, body').animate({
|
||||
scrollTop: (scrollTo.offset().top - $('#header-menu').height() - offset) + 'px'
|
||||
@@ -174,12 +173,12 @@ define('forum/category', [
|
||||
infinitescroll.init(Category.loadMoreTopics);
|
||||
} else {
|
||||
navigator.hide();
|
||||
pagination.init(ajaxify.variables.get('currentPage'), ajaxify.variables.get('pageCount'));
|
||||
pagination.init(ajaxify.data.currentPage, ajaxify.data.pageCount);
|
||||
}
|
||||
}
|
||||
|
||||
Category.onNewTopic = function(topic) {
|
||||
var cid = ajaxify.variables.get('category_id');
|
||||
var cid = ajaxify.data.cid;
|
||||
if (!topic || parseInt(topic.cid, 10) !== parseInt(cid, 10)) {
|
||||
return;
|
||||
}
|
||||
@@ -236,7 +235,7 @@ define('forum/category', [
|
||||
};
|
||||
|
||||
function updateTopicCount() {
|
||||
socket.emit('categories.getTopicCount', ajaxify.variables.get('category_id'), function(err, topicCount) {
|
||||
socket.emit('categories.getTopicCount', ajaxify.data.cid, function(err, topicCount) {
|
||||
if(err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -338,7 +337,7 @@ define('forum/category', [
|
||||
|
||||
$(window).trigger('action:categories.loading');
|
||||
infinitescroll.loadMore('categories.loadMore', {
|
||||
cid: ajaxify.variables.get('category_id'),
|
||||
cid: ajaxify.data.cid,
|
||||
after: after,
|
||||
author: utils.params().author
|
||||
}, function (data, done) {
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
"use strict";
|
||||
/* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH, utils */
|
||||
|
||||
define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect, components) {
|
||||
define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescroll', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect, components, infinitescroll) {
|
||||
var Details = {
|
||||
cover: {}
|
||||
};
|
||||
|
||||
var searchInterval;
|
||||
|
||||
Details.init = function() {
|
||||
var detailsPage = components.get('groups/container'),
|
||||
settingsFormEl = detailsPage.find('form');
|
||||
|
||||
if (ajaxify.variables.get('is_owner') === 'true') {
|
||||
if (ajaxify.data.group.isOwner) {
|
||||
Details.prepareSettings();
|
||||
Details.initialiseCover();
|
||||
}
|
||||
|
||||
handleMemberSearch();
|
||||
handleMemberInfiniteScroll();
|
||||
|
||||
components.get('groups/activity').find('.content img').addClass('img-responsive');
|
||||
|
||||
detailsPage.on('click', '[data-action]', function() {
|
||||
@@ -29,7 +34,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
case 'toggleOwnership':
|
||||
socket.emit('groups.' + (isOwner ? 'rescind' : 'grant'), {
|
||||
toUid: uid,
|
||||
groupName: ajaxify.variables.get('group_name')
|
||||
groupName: ajaxify.data.group.name
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
ownerFlagEl.toggleClass('invisible');
|
||||
@@ -42,7 +47,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
case 'kick':
|
||||
socket.emit('groups.kick', {
|
||||
uid: uid,
|
||||
groupName: ajaxify.variables.get('group_name')
|
||||
groupName: ajaxify.data.group.name
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
userRow.slideUp().remove();
|
||||
@@ -70,7 +75,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
case 'rejectAll':
|
||||
socket.emit('groups.' + action, {
|
||||
toUid: uid,
|
||||
groupName: ajaxify.variables.get('group_name')
|
||||
groupName: ajaxify.data.group.name
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
ajaxify.refresh();
|
||||
@@ -151,7 +156,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
});
|
||||
|
||||
socket.emit('groups.update', {
|
||||
groupName: ajaxify.variables.get('group_name'),
|
||||
groupName: ajaxify.data.group.name,
|
||||
values: settings
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
@@ -173,15 +178,15 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
};
|
||||
|
||||
Details.deleteGroup = function() {
|
||||
bootbox.confirm('Are you sure you want to delete the group: ' + utils.escapeHTML(ajaxify.variables.get('group_name')), function(confirm) {
|
||||
bootbox.confirm('Are you sure you want to delete the group: ' + utils.escapeHTML(ajaxify.data.group.name), function(confirm) {
|
||||
if (confirm) {
|
||||
bootbox.prompt('Please enter the name of this group in order to delete it:', function(response) {
|
||||
if (response === ajaxify.variables.get('group_name')) {
|
||||
if (response === ajaxify.data.group.name) {
|
||||
socket.emit('groups.delete', {
|
||||
groupName: ajaxify.variables.get('group_name')
|
||||
groupName: ajaxify.data.group.name
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(ajaxify.variables.get('group_name')) + ']]');
|
||||
app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(ajaxify.data.group.name) + ']]');
|
||||
ajaxify.go('groups');
|
||||
} else {
|
||||
app.alertError(err.message);
|
||||
@@ -213,7 +218,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
|
||||
Details.cover.load = function() {
|
||||
socket.emit('groups.cover.get', {
|
||||
groupName: ajaxify.variables.get('group_name')
|
||||
groupName: ajaxify.data.group.name
|
||||
}, function(err, data) {
|
||||
if (!err) {
|
||||
var coverEl = components.get('groups/cover');
|
||||
@@ -262,7 +267,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
coverEl.addClass('saving');
|
||||
|
||||
socket.emit('groups.cover.update', {
|
||||
groupName: ajaxify.variables.get('group_name'),
|
||||
groupName: ajaxify.data.group.name,
|
||||
imageData: Details.cover.newCover || undefined,
|
||||
position: components.get('groups/cover').css('background-position')
|
||||
}, function(err) {
|
||||
@@ -280,5 +285,84 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/
|
||||
});
|
||||
};
|
||||
|
||||
function handleMemberSearch() {
|
||||
$('[component="groups/members/search"]').on('keyup', function() {
|
||||
var query = $(this).val();
|
||||
if (searchInterval) {
|
||||
clearInterval(searchInterval);
|
||||
searchInterval = 0;
|
||||
}
|
||||
|
||||
searchInterval = setTimeout(function() {
|
||||
socket.emit('groups.searchMembers', {groupName: ajaxify.data.group.name, query: query}, function(err, results) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
infinitescroll.parseAndTranslate('groups/details', 'members', {
|
||||
group: {
|
||||
members: results.users,
|
||||
isOwner: ajaxify.data.group.isOwner
|
||||
}
|
||||
}, function(html) {
|
||||
$('[component="groups/members"] tbody').html(html);
|
||||
$('[component="groups/members"]').attr('data-nextstart', 20);
|
||||
});
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMemberInfiniteScroll() {
|
||||
$('[component="groups/members"] tbody').on('scroll', function() {
|
||||
var $this = $(this);
|
||||
var bottom = ($this[0].scrollHeight - $this.height()) * 0.9;
|
||||
if ($this.scrollTop() > bottom) {
|
||||
loadMoreMembers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadMoreMembers() {
|
||||
var members = $('[component="groups/members"]');
|
||||
if (members.attr('loading')) {
|
||||
return;
|
||||
}
|
||||
members.attr('loading', 1);
|
||||
socket.emit('groups.loadMoreMembers', {
|
||||
groupName: ajaxify.data.group.name,
|
||||
after: members.attr('data-nextstart')
|
||||
}, function(err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (data && data.users.length) {
|
||||
onMembersLoaded(data.users, function() {
|
||||
members.removeAttr('loading');
|
||||
members.attr('data-nextstart', data.nextStart);
|
||||
});
|
||||
} else {
|
||||
members.removeAttr('loading');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onMembersLoaded(users, callback) {
|
||||
users = users.filter(function(user) {
|
||||
return !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length;
|
||||
});
|
||||
|
||||
infinitescroll.parseAndTranslate('groups/details', 'members', {
|
||||
group: {
|
||||
members: users,
|
||||
isOwner: ajaxify.data.group.isOwner
|
||||
}
|
||||
}, function(html) {
|
||||
$('[component="groups/members"] tbody').append(html);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
return Details;
|
||||
});
|
||||
@@ -5,7 +5,7 @@ define('forum/reset_code', function() {
|
||||
var ResetCode = {};
|
||||
|
||||
ResetCode.init = function() {
|
||||
var reset_code = ajaxify.variables.get('reset_code');
|
||||
var reset_code = ajaxify.data.code;
|
||||
|
||||
var resetEl = $('#reset'),
|
||||
password = $('#password'),
|
||||
|
||||
@@ -24,7 +24,7 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function(recent, i
|
||||
}
|
||||
|
||||
infinitescroll.loadMore('topics.loadMoreFromSet', {
|
||||
set: 'tag:' + ajaxify.variables.get('tag') + ':topics',
|
||||
set: 'tag:' + ajaxify.data.tag + ':topics',
|
||||
after: $('[component="category"]').attr('data-nextstart')
|
||||
}, function(data, done) {
|
||||
if (data.topics && data.topics.length) {
|
||||
|
||||
@@ -38,7 +38,7 @@ define('forum/topic', [
|
||||
});
|
||||
|
||||
Topic.init = function() {
|
||||
var tid = ajaxify.variables.get('topic_id');
|
||||
var tid = ajaxify.data.tid;
|
||||
|
||||
$(window).trigger('action:topic.loading');
|
||||
|
||||
@@ -50,7 +50,7 @@ define('forum/topic', [
|
||||
threadTools.init(tid);
|
||||
events.init();
|
||||
|
||||
sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.variables.get('topic_slug'));
|
||||
sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.data.slug);
|
||||
|
||||
enableInfiniteLoadingOrPagination();
|
||||
|
||||
@@ -58,7 +58,7 @@ define('forum/topic', [
|
||||
|
||||
handleBookmark(tid);
|
||||
|
||||
navigator.init('[component="post"]', ajaxify.variables.get('postcount'), Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex);
|
||||
navigator.init('[component="post"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex);
|
||||
|
||||
$(window).on('scroll', updateTopicTitle);
|
||||
|
||||
@@ -107,7 +107,7 @@ define('forum/topic', [
|
||||
};
|
||||
|
||||
Topic.toBottom = function() {
|
||||
socket.emit('topics.postcount', ajaxify.variables.get('topic_id'), function(err, postCount) {
|
||||
socket.emit('topics.postcount', ajaxify.data.tid, function(err, postCount) {
|
||||
if (config.topicPostSort !== 'oldest_to_newest') {
|
||||
postCount = 2;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ define('forum/topic', [
|
||||
|
||||
if (postIndex && window.location.search.indexOf('page=') === -1) {
|
||||
navigator.scrollToPost(postIndex - 1, true);
|
||||
} else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && ajaxify.variables.get('postcount') > 1) {
|
||||
} else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && ajaxify.data.postcount > 1) {
|
||||
app.alert({
|
||||
alert_id: 'bookmark',
|
||||
message: '[[topic:bookmark_instructions]]',
|
||||
@@ -162,14 +162,14 @@ define('forum/topic', [
|
||||
} else {
|
||||
navigator.hide();
|
||||
|
||||
pagination.init(parseInt(ajaxify.variables.get('currentPage'), 10), parseInt(ajaxify.variables.get('pageCount'), 10));
|
||||
pagination.init(parseInt(ajaxify.data.currentPage, 10), parseInt(ajaxify.data.pageCount, 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateTopicTitle() {
|
||||
if($(window).scrollTop() > 50) {
|
||||
components.get('navbar/title').find('span').text(ajaxify.variables.get('topic_name')).show();
|
||||
components.get('navbar/title').find('span').text(ajaxify.data.title).show();
|
||||
} else {
|
||||
components.get('navbar/title').find('span').text('').hide();
|
||||
}
|
||||
@@ -197,10 +197,10 @@ define('forum/topic', [
|
||||
}
|
||||
}
|
||||
|
||||
var currentBookmark = localStorage.getItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark');
|
||||
var currentBookmark = localStorage.getItem('topic:' + ajaxify.data.tid + ':bookmark');
|
||||
|
||||
if (!currentBookmark || parseInt(postIndex, 10) >= parseInt(currentBookmark, 10)) {
|
||||
localStorage.setItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark', postIndex);
|
||||
localStorage.setItem('topic:' + ajaxify.data.tid + ':bookmark', postIndex);
|
||||
app.removeAlert('bookmark');
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ define('forum/topic/browsing', ['translator'], function(translator) {
|
||||
var Browsing = {};
|
||||
|
||||
Browsing.onUpdateUsersInRoom = function(data) {
|
||||
if (data && data.room.indexOf('topic_' + ajaxify.variables.get('topic_id')) !== -1) {
|
||||
if (data && data.room.indexOf('topic_' + ajaxify.data.tid) !== -1) {
|
||||
$('[component="topic/browsing/list"]').parent().toggleClass('hidden', !data.users.length);
|
||||
for(var i=0; i<data.users.length; ++i) {
|
||||
addUserIcon(data.users[i]);
|
||||
|
||||
@@ -82,7 +82,7 @@ define('forum/topic/events', [
|
||||
}
|
||||
|
||||
function onTopicPurged(data) {
|
||||
ajaxify.go('category/' + ajaxify.variables.get('category_id'));
|
||||
ajaxify.go('category/' + ajaxify.data.cid);
|
||||
}
|
||||
|
||||
function onTopicMoved(data) {
|
||||
@@ -202,7 +202,7 @@ define('forum/topic/events', [
|
||||
}
|
||||
|
||||
function onNewNotification(data) {
|
||||
var tid = ajaxify.variables.get('topic_id');
|
||||
var tid = ajaxify.data.tid;
|
||||
if (data && data.tid && parseInt(data.tid, 10) === parseInt(tid, 10)) {
|
||||
socket.emit('topics.markTopicNotificationsRead', tid);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
topicName;
|
||||
|
||||
PostTools.init = function(tid) {
|
||||
topicName = ajaxify.variables.get('topic_name');
|
||||
topicName = ajaxify.data.title;
|
||||
|
||||
addPostHandlers(tid);
|
||||
|
||||
@@ -29,7 +29,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
};
|
||||
|
||||
PostTools.updatePostCount = function() {
|
||||
socket.emit('topics.postcount', ajaxify.variables.get('topic_id'), function(err, postCount) {
|
||||
socket.emit('topics.postcount', ajaxify.data.tid, function(err, postCount) {
|
||||
if (!err) {
|
||||
var postCountEl = components.get('topic/post-count');
|
||||
postCountEl.html(postCount).attr('title', postCount);
|
||||
@@ -108,8 +108,8 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
|
||||
postContainer.on('click', '[component="post/edit"]', function(e) {
|
||||
var btn = $(this);
|
||||
require(['composer'], function(composer) {
|
||||
composer.editPost(getData(btn, 'data-pid'));
|
||||
$(window).trigger('action:composer.post.edit', {
|
||||
pid: getData(btn, 'data-pid')
|
||||
});
|
||||
});
|
||||
|
||||
@@ -135,51 +135,61 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
}
|
||||
|
||||
function onReplyClicked(button, tid, topicName) {
|
||||
require(['composer'], function(composer) {
|
||||
var selectionText = '',
|
||||
selection = window.getSelection ? window.getSelection() : document.selection.createRange(),
|
||||
topicUUID = composer.findByTid(tid);
|
||||
var selectionText = '',
|
||||
selection = window.getSelection ? window.getSelection() : document.selection.createRange();
|
||||
|
||||
if ($(selection.baseNode).parents('[component="post/content"]').length > 0) {
|
||||
var snippet = selection.toString();
|
||||
if (snippet.length) {
|
||||
selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n';
|
||||
}
|
||||
if ($(selection.baseNode).parents('[component="post/content"]').length > 0) {
|
||||
var snippet = selection.toString();
|
||||
if (snippet.length) {
|
||||
selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
var username = getUserName(selectionText ? $(selection.baseNode) : button);
|
||||
if (getData(button, 'data-uid') === '0') {
|
||||
username = '';
|
||||
}
|
||||
if (selectionText.length) {
|
||||
composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), getData(button, 'data-pid'), topicName, username, selectionText, topicUUID);
|
||||
} else {
|
||||
composer.newReply(tid, getData(button, 'data-pid'), topicName, username ? username + ' ' : '');
|
||||
}
|
||||
});
|
||||
|
||||
var username = getUserName(selectionText ? $(selection.baseNode) : button);
|
||||
if (getData(button, 'data-uid') === '0') {
|
||||
username = '';
|
||||
}
|
||||
if (selectionText.length) {
|
||||
$(window).trigger('action:composer.addQuote', {
|
||||
tid: tid,
|
||||
slug: ajaxify.data.slug,
|
||||
index: getData(button, 'data-index'),
|
||||
pid: getData(button, 'data-pid'),
|
||||
topicName: topicName,
|
||||
username: username,
|
||||
text: selectionText
|
||||
});
|
||||
} else {
|
||||
$(window).trigger('action:composer.post.new', {
|
||||
tid: tid,
|
||||
pid: getData(button, 'data-pid'),
|
||||
topicName: topicName,
|
||||
text: username + ' ' || ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onQuoteClicked(button, tid, topicName) {
|
||||
require(['composer'], function(composer) {
|
||||
var username = getUserName(button),
|
||||
pid = getData(button, 'data-pid'),
|
||||
topicUUID = composer.findByTid(tid);
|
||||
var username = getUserName(button),
|
||||
pid = getData(button, 'data-pid');
|
||||
|
||||
socket.emit('posts.getRawPost', pid, function(err, post) {
|
||||
if(err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
var quoted = '';
|
||||
if(post) {
|
||||
quoted = '> ' + post.replace(/\n/g, '\n> ') + '\n\n';
|
||||
}
|
||||
socket.emit('posts.getRawPost', pid, function(err, post) {
|
||||
if(err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
var quoted = '';
|
||||
if(post) {
|
||||
quoted = '> ' + post.replace(/\n/g, '\n> ') + '\n\n';
|
||||
}
|
||||
|
||||
if(topicUUID) {
|
||||
composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), pid, topicName, username, quoted, topicUUID);
|
||||
} else {
|
||||
composer.newReply(tid, pid, topicName, '[[modules:composer.user_said, ' + username + ']]\n' + quoted);
|
||||
}
|
||||
$(window).trigger('action:composer.addQuote', {
|
||||
tid: tid,
|
||||
slug: ajaxify.data.slug,
|
||||
index: getData(button, 'data-index'),
|
||||
pid: pid,
|
||||
username: username,
|
||||
topicName: topicName,
|
||||
text: quoted
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -216,7 +226,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
}
|
||||
|
||||
function showVotes(pid) {
|
||||
socket.emit('posts.getVoters', {pid: pid, cid: ajaxify.variables.get('category_id')}, function(err, data) {
|
||||
socket.emit('posts.getVoters', {pid: pid, cid: ajaxify.data.cid}, function(err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ define('forum/topic/posts', [
|
||||
var Posts = {};
|
||||
|
||||
Posts.onNewPost = function(data) {
|
||||
var tid = ajaxify.variables.get('topic_id');
|
||||
var tid = ajaxify.data.tid;
|
||||
if (data && data.posts && data.posts.length && parseInt(data.posts[0].tid, 10) !== parseInt(tid, 10)) {
|
||||
return;
|
||||
}
|
||||
@@ -133,8 +133,9 @@ define('forum/topic/posts', [
|
||||
|
||||
findInsertionPoint();
|
||||
|
||||
data.title = $('<div></div>').text(ajaxify.variables.get('topic_name')).html();
|
||||
data.viewcount = ajaxify.variables.get('viewcount');
|
||||
data.title = $('<div></div>').text(ajaxify.data.title).html();
|
||||
data.slug = ajaxify.data.slug;
|
||||
data.viewcount = ajaxify.data.viewcount;
|
||||
|
||||
infinitescroll.parseAndTranslate('topic', 'posts', data, function(html) {
|
||||
if (after) {
|
||||
@@ -218,7 +219,7 @@ define('forum/topic/posts', [
|
||||
};
|
||||
|
||||
function loadPostsAfter(after) {
|
||||
var tid = ajaxify.variables.get('topic_id');
|
||||
var tid = ajaxify.data.tid;
|
||||
if (!utils.isNumber(tid) || !utils.isNumber(after) || (after === 0 && components.get('post', 'index', 1).length)) {
|
||||
return;
|
||||
}
|
||||
@@ -274,7 +275,7 @@ define('forum/topic/posts', [
|
||||
function hidePostToolsForDeletedPosts(posts) {
|
||||
posts.each(function() {
|
||||
if ($(this).hasClass('deleted')) {
|
||||
postTools.toggle($(this).attr('data-pid'), true);
|
||||
postTools.toggle($(this).attr('data-pid'), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,22 +23,22 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp
|
||||
});
|
||||
|
||||
components.get('topic/lock').on('click', function() {
|
||||
socket.emit('topics.lock', {tids: [tid], cid: ajaxify.variables.get('category_id')});
|
||||
socket.emit('topics.lock', {tids: [tid], cid: ajaxify.data.cid});
|
||||
return false;
|
||||
});
|
||||
|
||||
components.get('topic/unlock').on('click', function() {
|
||||
socket.emit('topics.unlock', {tids: [tid], cid: ajaxify.variables.get('category_id')});
|
||||
socket.emit('topics.unlock', {tids: [tid], cid: ajaxify.data.cid});
|
||||
return false;
|
||||
});
|
||||
|
||||
components.get('topic/pin').on('click', function() {
|
||||
socket.emit('topics.pin', {tids: [tid], cid: ajaxify.variables.get('category_id')});
|
||||
socket.emit('topics.pin', {tids: [tid], cid: ajaxify.data.cid});
|
||||
return false;
|
||||
});
|
||||
|
||||
components.get('topic/unpin').on('click', function() {
|
||||
socket.emit('topics.unpin', {tids: [tid], cid: ajaxify.variables.get('category_id')});
|
||||
socket.emit('topics.unpin', {tids: [tid], cid: ajaxify.data.cid});
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp
|
||||
});
|
||||
|
||||
components.get('topic/move').on('click', function(e) {
|
||||
move.init([tid], ajaxify.variables.get('category_id'));
|
||||
move.init([tid], ajaxify.data.cid);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -95,7 +95,7 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp
|
||||
translator.translate('[[topic:thread_tools.' + command + '_confirm]]', function(msg) {
|
||||
bootbox.confirm(msg, function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('topics.' + command, {tids: [tid], cid: ajaxify.variables.get('category_id')});
|
||||
socket.emit('topics.' + command, {tids: [tid], cid: ajaxify.data.cid});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,595 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, socket, app, config, ajaxify, utils, templates, bootbox */
|
||||
|
||||
define('composer', [
|
||||
'taskbar',
|
||||
'translator',
|
||||
'composer/controls',
|
||||
'composer/uploads',
|
||||
'composer/formatting',
|
||||
'composer/drafts',
|
||||
'composer/tags',
|
||||
'composer/categoryList',
|
||||
'composer/preview',
|
||||
'composer/resize'
|
||||
], function(taskbar, translator, controls, uploads, formatting, drafts, tags, categoryList, preview, resize) {
|
||||
var composer = {
|
||||
active: undefined,
|
||||
posts: {},
|
||||
bsEnvironment: undefined,
|
||||
formatting: []
|
||||
};
|
||||
|
||||
$(window).off('resize', onWindowResize).on('resize', onWindowResize);
|
||||
|
||||
$(window).on('action:composer.topics.post', function(ev, data) {
|
||||
localStorage.removeItem('category:' + data.data.cid + ':bookmark');
|
||||
localStorage.removeItem('category:' + data.data.cid + ':bookmark:clicked');
|
||||
});
|
||||
|
||||
$(window).on('popstate', function(ev, data) {
|
||||
var env = utils.findBootstrapEnvironment();
|
||||
|
||||
if (composer.active && (env === 'xs' || env ==='sm')) {
|
||||
if (!composer.posts[composer.active].modified) {
|
||||
discard(composer.active);
|
||||
return;
|
||||
}
|
||||
|
||||
translator.translate('[[modules:composer.discard]]', function(translated) {
|
||||
bootbox.confirm(translated, function(confirm) {
|
||||
if (confirm) {
|
||||
discard(composer.active);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function removeComposerHistory() {
|
||||
var env = utils.findBootstrapEnvironment();
|
||||
if (env === 'xs' || env ==='sm') {
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
// Query server for formatting options
|
||||
socket.emit('modules.composer.getFormattingOptions', function(err, options) {
|
||||
composer.formatting = options;
|
||||
});
|
||||
|
||||
function onWindowResize() {
|
||||
if (composer.active !== undefined) {
|
||||
resize.reposition($('#cmp-uuid-' + composer.active));
|
||||
}
|
||||
}
|
||||
|
||||
function alreadyOpen(post) {
|
||||
// If a composer for the same cid/tid/pid is already open, return the uuid, else return bool false
|
||||
var type, id;
|
||||
|
||||
if (post.hasOwnProperty('cid')) {
|
||||
type = 'cid';
|
||||
} else if (post.hasOwnProperty('tid')) {
|
||||
type = 'tid';
|
||||
} else if (post.hasOwnProperty('pid')) {
|
||||
type = 'pid';
|
||||
}
|
||||
|
||||
id = post[type];
|
||||
|
||||
// Find a match
|
||||
for(var uuid in composer.posts) {
|
||||
if (composer.posts[uuid].hasOwnProperty(type) && id === composer.posts[uuid][type]) {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
|
||||
// No matches...
|
||||
return false;
|
||||
}
|
||||
|
||||
function push(post) {
|
||||
var uuid = utils.generateUUID(),
|
||||
existingUUID = alreadyOpen(post);
|
||||
|
||||
if (existingUUID) {
|
||||
taskbar.updateActive(existingUUID);
|
||||
return composer.load(existingUUID);
|
||||
}
|
||||
|
||||
translator.translate('[[topic:composer.new_topic]]', function(newTopicStr) {
|
||||
taskbar.push('composer', uuid, {
|
||||
title: post.title ? post.title : newTopicStr
|
||||
});
|
||||
});
|
||||
|
||||
// Construct a save_id
|
||||
if (0 !== parseInt(app.user.uid, 10)) {
|
||||
if (post.hasOwnProperty('cid')) {
|
||||
post.save_id = ['composer', app.user.uid, 'cid', post.cid].join(':');
|
||||
} else if (post.hasOwnProperty('tid')) {
|
||||
post.save_id = ['composer', app.user.uid, 'tid', post.tid].join(':');
|
||||
} else if (post.hasOwnProperty('pid')) {
|
||||
post.save_id = ['composer', app.user.uid, 'pid', post.pid].join(':');
|
||||
}
|
||||
}
|
||||
|
||||
composer.posts[uuid] = post;
|
||||
composer.load(uuid);
|
||||
}
|
||||
|
||||
function composerAlert(post_uuid, message) {
|
||||
$('#cmp-uuid-' + post_uuid).find('.composer-submit').removeAttr('disabled');
|
||||
app.alert({
|
||||
type: 'danger',
|
||||
timeout: 3000,
|
||||
title: '',
|
||||
message: message,
|
||||
alert_id: 'post_error'
|
||||
});
|
||||
}
|
||||
|
||||
composer.findByTid = function(tid) {
|
||||
// Iterates through the initialised composers and returns the uuid of the matching composer
|
||||
for(var uuid in composer.posts) {
|
||||
if (composer.posts.hasOwnProperty(uuid) && composer.posts[uuid].hasOwnProperty('tid') && parseInt(composer.posts[uuid].tid, 10) === parseInt(tid, 10)) {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
composer.addButton = function(iconClass, onClick) {
|
||||
formatting.addButton(iconClass, onClick);
|
||||
};
|
||||
|
||||
composer.newTopic = function(cid) {
|
||||
socket.emit('categories.isModerator', cid, function(err, isMod) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
push({
|
||||
cid: cid,
|
||||
title: '',
|
||||
body: '',
|
||||
modified: false,
|
||||
isMain: true,
|
||||
isMod: isMod
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
composer.addQuote = function(tid, topicSlug, postIndex, pid, title, username, text, uuid) {
|
||||
uuid = uuid || composer.active;
|
||||
|
||||
if (uuid === undefined) {
|
||||
composer.newReply(tid, pid, title, '[[modules:composer.user_said, ' + username + ']]\n' + text);
|
||||
return;
|
||||
} else if (uuid !== composer.active) {
|
||||
// If the composer is not currently active, activate it
|
||||
composer.load(uuid);
|
||||
}
|
||||
|
||||
var postContainer = $('#cmp-uuid-' + uuid);
|
||||
var bodyEl = postContainer.find('textarea');
|
||||
var prevText = bodyEl.val();
|
||||
if (parseInt(tid, 10) !== parseInt(composer.posts[uuid].tid, 10)) {
|
||||
var link = '[' + title + '](/topic/' + topicSlug + '/' + (parseInt(postIndex, 10) + 1) + ')';
|
||||
translator.translate('[[modules:composer.user_said_in, ' + username + ', ' + link + ']]\n', config.defaultLang, onTranslated);
|
||||
} else {
|
||||
translator.translate('[[modules:composer.user_said, ' + username + ']]\n', config.defaultLang, onTranslated);
|
||||
}
|
||||
|
||||
function onTranslated(translated) {
|
||||
composer.posts[uuid].body = (prevText.length ? prevText + '\n\n' : '') + translated + text;
|
||||
bodyEl.val(composer.posts[uuid].body);
|
||||
focusElements(postContainer);
|
||||
preview.render(postContainer);
|
||||
}
|
||||
};
|
||||
|
||||
composer.newReply = function(tid, pid, title, text) {
|
||||
socket.emit('topics.isModerator', tid, function(err, isMod) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
translator.translate(text, config.defaultLang, function(translated) {
|
||||
push({
|
||||
tid: tid,
|
||||
toPid: pid,
|
||||
title: $('<div/>').text(title).html(),
|
||||
body: translated,
|
||||
modified: false,
|
||||
isMain: false,
|
||||
isMod: isMod
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
composer.editPost = function(pid) {
|
||||
socket.emit('modules.composer.push', pid, function(err, threadData) {
|
||||
if(err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
push({
|
||||
pid: pid,
|
||||
uid: threadData.uid,
|
||||
handle: threadData.handle,
|
||||
title: threadData.title,
|
||||
body: threadData.body,
|
||||
modified: false,
|
||||
isMain: threadData.isMain,
|
||||
topic_thumb: threadData.topic_thumb,
|
||||
tags: threadData.tags
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
composer.load = function(post_uuid) {
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid);
|
||||
if (postContainer.length) {
|
||||
activate(post_uuid);
|
||||
resize.reposition(postContainer);
|
||||
focusElements(postContainer);
|
||||
} else {
|
||||
createNewComposer(post_uuid);
|
||||
}
|
||||
|
||||
startNotifyTyping(composer.posts[post_uuid]);
|
||||
};
|
||||
|
||||
function startNotifyTyping(postData) {
|
||||
function emit() {
|
||||
socket.emit('modules.composer.notifyTyping', {
|
||||
tid: postData.tid,
|
||||
uid: app.user.uid
|
||||
});
|
||||
}
|
||||
|
||||
if (!parseInt(postData.tid, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stopNotifyInterval(postData);
|
||||
|
||||
emit();
|
||||
postData.notifyTypingIntervalId = setInterval(emit, 5000);
|
||||
}
|
||||
|
||||
function stopNotifyTyping(postData) {
|
||||
if (!parseInt(postData.tid, 10)) {
|
||||
return;
|
||||
}
|
||||
socket.emit('modules.composer.stopNotifyTyping', {
|
||||
tid: postData.tid,
|
||||
uid: app.user.uid
|
||||
});
|
||||
}
|
||||
|
||||
function stopNotifyInterval(postData) {
|
||||
if (postData.notifyTypingIntervalId) {
|
||||
clearInterval(postData.notifyTypingIntervalId);
|
||||
postData.notifyTypingIntervalId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function createNewComposer(post_uuid) {
|
||||
var postData = composer.posts[post_uuid];
|
||||
|
||||
var allowTopicsThumbnail = config.allowTopicsThumbnail && postData.isMain && (config.hasImageUploadPlugin || config.allowFileUploads),
|
||||
isTopic = postData ? !!postData.cid : false,
|
||||
isMain = postData ? !!postData.isMain : false,
|
||||
isEditing = postData ? !!postData.pid : false,
|
||||
isGuestPost = postData ? parseInt(postData.uid, 10) === 0 : false;
|
||||
|
||||
composer.bsEnvironment = utils.findBootstrapEnvironment();
|
||||
|
||||
// see
|
||||
// https://github.com/NodeBB/NodeBB/issues/2994 and
|
||||
// https://github.com/NodeBB/NodeBB/issues/1951
|
||||
// remove when 1951 is resolved
|
||||
|
||||
var title = postData.title.replace(/%/g, '%').replace(/,/g, ',');
|
||||
|
||||
var data = {
|
||||
title: title,
|
||||
mobile: composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm',
|
||||
allowTopicsThumbnail: allowTopicsThumbnail,
|
||||
isTopicOrMain: isTopic || isMain,
|
||||
minimumTagLength: config.minimumTagLength,
|
||||
maximumTagLength: config.maximumTagLength,
|
||||
isTopic: isTopic,
|
||||
isEditing: isEditing,
|
||||
showHandleInput: config.allowGuestHandles && (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)),
|
||||
handle: postData ? postData.handle || '' : undefined,
|
||||
formatting: composer.formatting,
|
||||
isAdminOrMod: app.user.isAdmin || postData.isMod
|
||||
};
|
||||
|
||||
if (data.mobile) {
|
||||
var qs = '?p=' + window.location.pathname;
|
||||
ajaxify.go('compose', function() {
|
||||
renderComposer();
|
||||
}, false, qs);
|
||||
} else {
|
||||
renderComposer();
|
||||
}
|
||||
|
||||
function renderComposer() {
|
||||
parseAndTranslate('composer', data, function(composerTemplate) {
|
||||
if ($('#cmp-uuid-' + post_uuid).length) {
|
||||
return;
|
||||
}
|
||||
composerTemplate = $(composerTemplate);
|
||||
|
||||
composerTemplate.attr('id', 'cmp-uuid-' + post_uuid);
|
||||
|
||||
$(document.body).append(composerTemplate);
|
||||
|
||||
var postContainer = $(composerTemplate[0]),
|
||||
bodyEl = postContainer.find('textarea'),
|
||||
draft = drafts.getDraft(postData.save_id),
|
||||
submitBtn = postContainer.find('.composer-submit');
|
||||
|
||||
preview.handleToggler(postContainer);
|
||||
tags.init(postContainer, composer.posts[post_uuid]);
|
||||
categoryList.init(postContainer, composer.posts[post_uuid]);
|
||||
|
||||
activate(post_uuid);
|
||||
resize.reposition(postContainer);
|
||||
|
||||
if (config.allowFileUploads || config.hasImageUploadPlugin) {
|
||||
uploads.initialize(post_uuid);
|
||||
}
|
||||
|
||||
formatting.addHandler(postContainer);
|
||||
|
||||
if (allowTopicsThumbnail) {
|
||||
uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || '');
|
||||
}
|
||||
|
||||
postContainer.on('change', 'input, textarea', function() {
|
||||
composer.posts[post_uuid].modified = true;
|
||||
});
|
||||
|
||||
submitBtn.on('click', function() {
|
||||
var action = $(this).attr('data-action');
|
||||
|
||||
switch(action) {
|
||||
case 'post-lock':
|
||||
$(this).attr('disabled', true);
|
||||
post(post_uuid, {lock: true});
|
||||
break;
|
||||
|
||||
case 'post': // intentional fall-through
|
||||
default:
|
||||
$(this).attr('disabled', true);
|
||||
post(post_uuid);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
postContainer.on('click', 'a[data-switch-action]', function() {
|
||||
var action = $(this).attr('data-switch-action'),
|
||||
label = $(this).html();
|
||||
|
||||
submitBtn.attr('data-action', action).html(label);
|
||||
});
|
||||
|
||||
postContainer.find('.composer-discard').on('click', function() {
|
||||
if (!composer.posts[post_uuid].modified) {
|
||||
removeComposerHistory();
|
||||
discard(post_uuid);
|
||||
return;
|
||||
}
|
||||
var btn = $(this).prop('disabled', true);
|
||||
translator.translate('[[modules:composer.discard]]', function(translated) {
|
||||
bootbox.confirm(translated, function(confirm) {
|
||||
if (confirm) {
|
||||
removeComposerHistory();
|
||||
discard(post_uuid);
|
||||
}
|
||||
btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
postContainer.on('click', function() {
|
||||
if (!taskbar.isActive(post_uuid)) {
|
||||
taskbar.updateActive(post_uuid);
|
||||
}
|
||||
});
|
||||
|
||||
bodyEl.on('input propertychange', function() {
|
||||
preview.render(postContainer);
|
||||
});
|
||||
|
||||
bodyEl.on('scroll', function() {
|
||||
preview.matchScroll(postContainer);
|
||||
});
|
||||
|
||||
bodyEl.val(draft ? draft : postData.body);
|
||||
|
||||
preview.render(postContainer, function() {
|
||||
preview.matchScroll(postContainer);
|
||||
});
|
||||
|
||||
drafts.init(postContainer, postData);
|
||||
|
||||
resize.handleResize(postContainer);
|
||||
|
||||
handleHelp(postContainer);
|
||||
|
||||
$(window).trigger('action:composer.loaded', {
|
||||
post_uuid: post_uuid,
|
||||
composerData: composer.posts[post_uuid]
|
||||
});
|
||||
|
||||
formatting.addComposerButtons();
|
||||
focusElements(postContainer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function parseAndTranslate(template, data, callback) {
|
||||
templates.parse(template, data, function(composerTemplate) {
|
||||
translator.translate(composerTemplate, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function handleHelp(postContainer) {
|
||||
var helpBtn = postContainer.find('.help');
|
||||
socket.emit('modules.composer.renderHelp', function(err, html) {
|
||||
if (!err && html && html.length > 0) {
|
||||
helpBtn.removeClass('hidden');
|
||||
helpBtn.on('click', function() {
|
||||
bootbox.alert(html);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function activate(post_uuid) {
|
||||
if(composer.active && composer.active !== post_uuid) {
|
||||
composer.minimize(composer.active);
|
||||
}
|
||||
|
||||
composer.active = post_uuid;
|
||||
}
|
||||
|
||||
function focusElements(postContainer) {
|
||||
var title = postContainer.find('input.title');
|
||||
if (title.length) {
|
||||
title.focus();
|
||||
} else {
|
||||
postContainer.find('textarea').focus().putCursorAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
function post(post_uuid, options) {
|
||||
var postData = composer.posts[post_uuid],
|
||||
postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
handleEl = postContainer.find('.handle'),
|
||||
titleEl = postContainer.find('.title'),
|
||||
bodyEl = postContainer.find('textarea'),
|
||||
thumbEl = postContainer.find('input#topic-thumb-url');
|
||||
|
||||
options = options || {};
|
||||
|
||||
titleEl.val(titleEl.val().trim());
|
||||
bodyEl.val(bodyEl.val().trim());
|
||||
if (thumbEl.length) {
|
||||
thumbEl.val(thumbEl.val().trim());
|
||||
}
|
||||
|
||||
var checkTitle = (parseInt(postData.cid, 10) || parseInt(postData.pid, 10)) && postContainer.find('input.title').length;
|
||||
|
||||
if (uploads.inProgress[post_uuid] && uploads.inProgress[post_uuid].length) {
|
||||
return composerAlert(post_uuid, '[[error:still-uploading]]');
|
||||
} else if (checkTitle && titleEl.val().length < parseInt(config.minimumTitleLength, 10)) {
|
||||
return composerAlert(post_uuid, '[[error:title-too-short, ' + config.minimumTitleLength + ']]');
|
||||
} else if (checkTitle && titleEl.val().length > parseInt(config.maximumTitleLength, 10)) {
|
||||
return composerAlert(post_uuid, '[[error:title-too-long, ' + config.maximumTitleLength + ']]');
|
||||
} else if (checkTitle && !utils.slugify(titleEl.val()).length) {
|
||||
return composerAlert(post_uuid, '[[error:invalid-title]]');
|
||||
} else if (bodyEl.val().length < parseInt(config.minimumPostLength, 10)) {
|
||||
return composerAlert(post_uuid, '[[error:content-too-short, ' + config.minimumPostLength + ']]');
|
||||
} else if (bodyEl.val().length > parseInt(config.maximumPostLength, 10)) {
|
||||
return composerAlert(post_uuid, '[[error:content-too-long, ' + config.maximumPostLength + ']]');
|
||||
}
|
||||
|
||||
var composerData = {}, action;
|
||||
|
||||
if (parseInt(postData.cid, 10) > 0) {
|
||||
action = 'topics.post';
|
||||
composerData = {
|
||||
handle: handleEl ? handleEl.val() : undefined,
|
||||
title: titleEl.val(),
|
||||
content: bodyEl.val(),
|
||||
topic_thumb: thumbEl.val() || '',
|
||||
category_id: postData.cid,
|
||||
tags: tags.getTags(post_uuid),
|
||||
lock: options.lock || false
|
||||
};
|
||||
} else if (parseInt(postData.tid, 10) > 0) {
|
||||
action = 'posts.reply';
|
||||
composerData = {
|
||||
tid: postData.tid,
|
||||
handle: handleEl ? handleEl.val() : undefined,
|
||||
content: bodyEl.val(),
|
||||
toPid: postData.toPid,
|
||||
lock: options.lock || false
|
||||
};
|
||||
} else if (parseInt(postData.pid, 10) > 0) {
|
||||
action = 'posts.edit';
|
||||
composerData = {
|
||||
pid: postData.pid,
|
||||
handle: handleEl ? handleEl.val() : undefined,
|
||||
content: bodyEl.val(),
|
||||
title: titleEl.val(),
|
||||
topic_thumb: thumbEl.val() || '',
|
||||
tags: tags.getTags(post_uuid)
|
||||
};
|
||||
}
|
||||
|
||||
socket.emit(action, composerData, function (err, data) {
|
||||
postContainer.find('.composer-submit').removeAttr('disabled');
|
||||
if (err) {
|
||||
if (err.message === '[[error:email-not-confirmed]]') {
|
||||
return app.showEmailConfirmWarning(err);
|
||||
}
|
||||
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
discard(post_uuid);
|
||||
drafts.removeDraft(postData.save_id);
|
||||
|
||||
if (action === 'topics.post') {
|
||||
ajaxify.go('topic/' + data.slug);
|
||||
} else {
|
||||
removeComposerHistory();
|
||||
}
|
||||
|
||||
$(window).trigger('action:composer.' + action, {composerData: composerData, data: data});
|
||||
});
|
||||
}
|
||||
|
||||
function discard(post_uuid) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$('#cmp-uuid-' + post_uuid).remove();
|
||||
drafts.removeDraft(composer.posts[post_uuid].save_id);
|
||||
stopNotifyInterval(composer.posts[post_uuid]);
|
||||
stopNotifyTyping(composer.posts[post_uuid]);
|
||||
|
||||
delete composer.posts[post_uuid];
|
||||
composer.active = undefined;
|
||||
taskbar.discard('composer', post_uuid);
|
||||
$('body').css({'margin-bottom': 0});
|
||||
$('[data-action="post"]').removeAttr('disabled');
|
||||
|
||||
|
||||
$('html').removeClass('composing mobile');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
composer.minimize = function(post_uuid) {
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid);
|
||||
postContainer.css('visibility', 'hidden');
|
||||
composer.active = undefined;
|
||||
taskbar.minimize('composer', post_uuid);
|
||||
|
||||
stopNotifyInterval(composer.posts[post_uuid]);
|
||||
stopNotifyTyping(composer.posts[post_uuid]);
|
||||
|
||||
$('body').css({'margin-bottom': '0px'});
|
||||
};
|
||||
|
||||
return composer;
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/*globals define, config, socket, app*/
|
||||
|
||||
define('composer/categoryList', function() {
|
||||
var categoryList = {};
|
||||
|
||||
categoryList.init = function(postContainer, postData) {
|
||||
var listEl = postContainer.find('.category-list');
|
||||
if (!listEl.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
// Remove categories that are just external links
|
||||
categories = categories.filter(function(category) {
|
||||
return !category.link;
|
||||
});
|
||||
|
||||
categories.forEach(function(category) {
|
||||
$('<option value="' + category.cid + '">' + category.name + '</option>').appendTo(listEl);
|
||||
});
|
||||
|
||||
if (postData.cid) {
|
||||
listEl.find('option[value="' + postData.cid + '"]').prop('selected', true);
|
||||
}
|
||||
});
|
||||
|
||||
listEl.on('change', function() {
|
||||
if (postData.cid) {
|
||||
postData.cid = this.value;
|
||||
}
|
||||
|
||||
$('[tabindex=' + (parseInt($(this).attr('tabindex'), 10) + 1) + ']').trigger('focus');
|
||||
});
|
||||
};
|
||||
|
||||
return categoryList;
|
||||
});
|
||||
46
public/src/modules/composer/controls.js
vendored
46
public/src/modules/composer/controls.js
vendored
@@ -1,46 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
/*global define*/
|
||||
|
||||
define('composer/controls', function() {
|
||||
var controls = {};
|
||||
|
||||
/*************************************************/
|
||||
/* Rich Textarea Controls */
|
||||
/*************************************************/
|
||||
controls.insertIntoTextarea = function(textarea, value) {
|
||||
var $textarea = $(textarea);
|
||||
var currentVal = $textarea.val();
|
||||
|
||||
$textarea.val(
|
||||
currentVal.slice(0, textarea.selectionStart) +
|
||||
value +
|
||||
currentVal.slice(textarea.selectionStart)
|
||||
);
|
||||
};
|
||||
|
||||
controls.wrapSelectionInTextareaWith = function(textarea, leading, trailing){
|
||||
if(trailing === undefined){
|
||||
trailing = leading;
|
||||
}
|
||||
|
||||
var $textarea = $(textarea);
|
||||
var currentVal = $textarea.val();
|
||||
|
||||
$textarea.val(
|
||||
currentVal.slice(0, textarea.selectionStart) +
|
||||
leading +
|
||||
currentVal.slice(textarea.selectionStart, textarea.selectionEnd) +
|
||||
trailing +
|
||||
currentVal.slice(textarea.selectionEnd)
|
||||
);
|
||||
};
|
||||
|
||||
controls.updateTextareaSelection = function(textarea, start, end){
|
||||
textarea.setSelectionRange(start, end);
|
||||
$(textarea).focus();
|
||||
};
|
||||
|
||||
|
||||
return controls;
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define */
|
||||
|
||||
define('composer/drafts', function() {
|
||||
|
||||
var drafts = {};
|
||||
var saveThrottleId;
|
||||
var saving = false;
|
||||
|
||||
drafts.init = function(postContainer, postData) {
|
||||
|
||||
var bodyEl = postContainer.find('textarea');
|
||||
bodyEl.on('keyup', function() {
|
||||
resetTimeout();
|
||||
|
||||
saveThrottleId = setTimeout(function() {
|
||||
saveDraft(postContainer, postData);
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
function resetTimeout() {
|
||||
if (saveThrottleId) {
|
||||
clearTimeout(saveThrottleId);
|
||||
saveThrottleId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
drafts.getDraft = function(save_id) {
|
||||
return localStorage.getItem(save_id);
|
||||
};
|
||||
|
||||
function saveDraft(postContainer, postData) {
|
||||
var raw;
|
||||
|
||||
if (canSave() && postData && postData.save_id && postContainer.length) {
|
||||
raw = postContainer.find('textarea').val();
|
||||
if (raw.length) {
|
||||
localStorage.setItem(postData.save_id, raw);
|
||||
} else {
|
||||
drafts.removeDraft(postData.save_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drafts.removeDraft = function(save_id) {
|
||||
resetTimeout();
|
||||
return localStorage.removeItem(save_id);
|
||||
};
|
||||
|
||||
function canSave() {
|
||||
if (saving) {
|
||||
return saving;
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem('test', 'test');
|
||||
localStorage.removeItem('test');
|
||||
saving = true;
|
||||
return true;
|
||||
} catch(e) {
|
||||
saving = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return drafts;
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define */
|
||||
|
||||
define('composer/formatting', ['composer/controls', 'composer/preview'], function(controls, preview) {
|
||||
|
||||
var formatting = {};
|
||||
|
||||
var formattingDispatchTable = {
|
||||
'picture': function(){
|
||||
$('#files').click();
|
||||
},
|
||||
|
||||
upload: function(){
|
||||
$('#files').click();
|
||||
},
|
||||
|
||||
tags: function() {
|
||||
$('.tags-container').toggleClass('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
var buttons = [];
|
||||
|
||||
formatting.addComposerButtons = function() {
|
||||
for(var x=0,numButtons=buttons.length;x<numButtons;x++) {
|
||||
$('.formatting-bar .btn-group form').before('<span class="btn btn-link" tabindex="-1" data-format="' + buttons[x].name + '"><i class="' + buttons[x].iconClass + '"></i></span>');
|
||||
}
|
||||
};
|
||||
|
||||
formatting.addButton = function(iconClass, onClick) {
|
||||
var name = iconClass.replace('fa fa-', '');
|
||||
|
||||
formattingDispatchTable[name] = onClick;
|
||||
buttons.push({
|
||||
name: name,
|
||||
iconClass: iconClass
|
||||
});
|
||||
};
|
||||
|
||||
formatting.addButtonDispatch = function(name, onClick) {
|
||||
formattingDispatchTable[name] = onClick;
|
||||
};
|
||||
|
||||
formatting.addHandler = function(postContainer) {
|
||||
postContainer.on('click', '.formatting-bar span', function () {
|
||||
var format = $(this).attr('data-format'),
|
||||
textarea = $(this).parents('.composer').find('textarea')[0];
|
||||
|
||||
if(formattingDispatchTable.hasOwnProperty(format)){
|
||||
formattingDispatchTable[format](textarea, textarea.selectionStart, textarea.selectionEnd);
|
||||
preview.render(postContainer);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return formatting;
|
||||
});
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, socket*/
|
||||
|
||||
define('composer/preview', function() {
|
||||
var preview = {};
|
||||
|
||||
var timeoutId = 0;
|
||||
|
||||
preview.render = function(postContainer, callback) {
|
||||
callback = callback || function() {};
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = 0;
|
||||
}
|
||||
var textarea = postContainer.find('textarea');
|
||||
|
||||
timeoutId = setTimeout(function() {
|
||||
socket.emit('modules.composer.renderPreview', textarea.val(), function(err, preview) {
|
||||
timeoutId = 0;
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
preview = $(preview);
|
||||
preview.find('img').addClass('img-responsive');
|
||||
postContainer.find('.preview').html(preview);
|
||||
$(window).trigger('action:composer.preview');
|
||||
callback();
|
||||
});
|
||||
}, 250);
|
||||
};
|
||||
|
||||
preview.matchScroll = function(postContainer) {
|
||||
var textarea = postContainer.find('textarea');
|
||||
var preview = postContainer.find('.preview');
|
||||
var diff = textarea[0].scrollHeight - textarea.height();
|
||||
|
||||
if (diff === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var scrollPercent = textarea.scrollTop() / diff;
|
||||
|
||||
preview.scrollTop(Math.max(preview[0].scrollHeight - preview.height(), 0) * scrollPercent);
|
||||
};
|
||||
|
||||
preview.handleToggler = function(postContainer) {
|
||||
function hidePreview() {
|
||||
togglePreview(false);
|
||||
localStorage.setItem('composer:previewToggled', true);
|
||||
}
|
||||
|
||||
function showPreview() {
|
||||
togglePreview(true);
|
||||
localStorage.removeItem('composer:previewToggled');
|
||||
}
|
||||
|
||||
function togglePreview(show) {
|
||||
previewContainer.toggleClass('hide', !show);
|
||||
writeContainer.toggleClass('maximized', !show);
|
||||
showBtn.toggleClass('hide', show);
|
||||
|
||||
$('.write').focus();
|
||||
preview.matchScroll(postContainer);
|
||||
}
|
||||
|
||||
var showBtn = postContainer.find('.write-container .toggle-preview'),
|
||||
hideBtn = postContainer.find('.preview-container .toggle-preview'),
|
||||
previewContainer = $('.preview-container'),
|
||||
writeContainer = $('.write-container');
|
||||
|
||||
hideBtn.on('click', hidePreview);
|
||||
showBtn.on('click', showPreview);
|
||||
|
||||
if (localStorage.getItem('composer:previewToggled')) {
|
||||
hidePreview();
|
||||
} else {
|
||||
showPreview();
|
||||
}
|
||||
};
|
||||
|
||||
return preview;
|
||||
});
|
||||
@@ -1,195 +0,0 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/* globals app, define, config, utils*/
|
||||
|
||||
define('composer/resize', ['autosize'], function(autosize) {
|
||||
var resize = {},
|
||||
oldPercentage = 0;
|
||||
|
||||
resize.reposition = function(postContainer) {
|
||||
var percentage = localStorage.getItem('composer:resizePercentage') || 0.5;
|
||||
|
||||
doResize(postContainer, percentage);
|
||||
};
|
||||
|
||||
function doResize(postContainer, percentage) {
|
||||
var env = utils.findBootstrapEnvironment();
|
||||
|
||||
|
||||
// todo, lump in browsers that don't support transform (ie8) here
|
||||
// at this point we should use modernizr
|
||||
if (env === 'sm' || env === 'xs' || window.innerHeight < 480) {
|
||||
$('html').addClass('composing mobile');
|
||||
autosize(postContainer.find('textarea')[0]);
|
||||
percentage = 1;
|
||||
} else {
|
||||
$('html').removeClass('composing mobile');
|
||||
}
|
||||
|
||||
if (percentage) {
|
||||
var max = getMaximumPercentage();
|
||||
|
||||
if (percentage < 0.25) {
|
||||
percentage = 0.25;
|
||||
} else if (percentage > max) {
|
||||
percentage = max;
|
||||
}
|
||||
|
||||
if (env === 'md' || env === 'lg') {
|
||||
var transform = 'translate(0, ' + (Math.abs(1-percentage) * 100) + '%)';
|
||||
postContainer.css({
|
||||
'-webkit-transform': transform,
|
||||
'-moz-transform': transform,
|
||||
'-ms-transform': transform,
|
||||
'-o-transform': transform,
|
||||
'transform': transform
|
||||
});
|
||||
} else {
|
||||
postContainer.removeAttr('style');
|
||||
}
|
||||
}
|
||||
|
||||
postContainer.percentage = percentage;
|
||||
|
||||
if (config.hasImageUploadPlugin) {
|
||||
postContainer.find('.img-upload-btn').removeClass('hide');
|
||||
postContainer.find('#files.lt-ie9').removeClass('hide');
|
||||
}
|
||||
|
||||
if (config.allowFileUploads) {
|
||||
postContainer.find('.file-upload-btn').removeClass('hide');
|
||||
postContainer.find('#files.lt-ie9').removeClass('hide');
|
||||
}
|
||||
|
||||
postContainer.css('visibility', 'visible');
|
||||
|
||||
// Add some extra space at the bottom of the body so that the user can still scroll to the last post w/ composer open
|
||||
$('body').css({'margin-bottom': postContainer.css('height')});
|
||||
|
||||
resizeWritePreview(postContainer);
|
||||
}
|
||||
|
||||
resize.handleResize = function(postContainer) {
|
||||
function resizeStart(e) {
|
||||
var resizeRect = resizeEl[0].getBoundingClientRect(),
|
||||
resizeCenterY = resizeRect.top + (resizeRect.height / 2);
|
||||
|
||||
resizeOffset = (resizeCenterY - e.clientY) / 2;
|
||||
resizeActive = true;
|
||||
resizeDown = e.clientY;
|
||||
|
||||
$(window).on('mousemove', resizeAction);
|
||||
$(window).on('mouseup', resizeStop);
|
||||
$('body').on('touchmove', resizeTouchAction);
|
||||
}
|
||||
|
||||
function resizeStop(e) {
|
||||
resizeActive = false;
|
||||
|
||||
postContainer.find('textarea').focus();
|
||||
$(window).off('mousemove', resizeAction);
|
||||
$(window).off('mouseup', resizeStop);
|
||||
$('body').off('touchmove', resizeTouchAction);
|
||||
|
||||
var position = (e.clientY - resizeOffset),
|
||||
newHeight = $(window).height() - position,
|
||||
windowHeight = $(window).height();
|
||||
|
||||
if (newHeight > windowHeight - $('#header-menu').height() - (windowHeight / 15)) {
|
||||
snapToTop = true;
|
||||
} else {
|
||||
snapToTop = false;
|
||||
}
|
||||
|
||||
toggleMaximize(e);
|
||||
}
|
||||
|
||||
function toggleMaximize(e) {
|
||||
if (e.clientY - resizeDown === 0 || snapToTop) {
|
||||
var newPercentage = getMaximumPercentage();
|
||||
|
||||
if (!postContainer.hasClass('maximized') || !snapToTop) {
|
||||
oldPercentage = postContainer.percentage;
|
||||
doResize(postContainer, newPercentage);
|
||||
postContainer.addClass('maximized');
|
||||
} else {
|
||||
doResize(postContainer, oldPercentage);
|
||||
postContainer.removeClass('maximized');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resizeTouchAction(e) {
|
||||
e.preventDefault();
|
||||
resizeAction(e.touches[0]);
|
||||
}
|
||||
|
||||
function resizeAction(e) {
|
||||
if (resizeActive) {
|
||||
var position = (e.clientY - resizeOffset),
|
||||
newHeight = $(window).height() - position;
|
||||
|
||||
doResize(postContainer, newHeight / $(window).height());
|
||||
|
||||
resizeWritePreview(postContainer);
|
||||
resizeSavePosition(newHeight);
|
||||
|
||||
if (Math.abs(e.clientY - resizeDown) > 0) {
|
||||
postContainer.removeClass('maximized');
|
||||
}
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
function resizeSavePosition(px) {
|
||||
var percentage = px / $(window).height(),
|
||||
max = getMaximumPercentage();
|
||||
localStorage.setItem('composer:resizePercentage', percentage < max ? percentage : max);
|
||||
}
|
||||
|
||||
var resizeActive = false,
|
||||
resizeOffset = 0,
|
||||
resizeDown = 0,
|
||||
snapToTop = false,
|
||||
resizeEl = postContainer.find('.resizer');
|
||||
|
||||
resizeEl
|
||||
.on('mousedown', resizeStart)
|
||||
.on('touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
resizeStart(e.touches[0]);
|
||||
})
|
||||
.on('touchend', function(e) {
|
||||
e.preventDefault();
|
||||
resizeStop();
|
||||
});
|
||||
};
|
||||
|
||||
function getMaximumPercentage() {
|
||||
return ($(window).height() - $('#header-menu').height() - 1) / $(window).height();
|
||||
}
|
||||
|
||||
function resizeWritePreview(postContainer) {
|
||||
var total = getFormattingHeight(postContainer);
|
||||
postContainer
|
||||
.find('.write-preview-container')
|
||||
.css('height', postContainer.percentage * $(window).height() - $('#header-menu').height() - total);
|
||||
}
|
||||
|
||||
function getFormattingHeight(postContainer) {
|
||||
return [
|
||||
postContainer.find('.title-container').outerHeight(true),
|
||||
postContainer.find('.formatting-bar').outerHeight(true),
|
||||
postContainer.find('.topic-thumb-container').outerHeight(true),
|
||||
$('.taskbar').height()
|
||||
].reduce(function(a, b) {
|
||||
return a + b;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return resize;
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/*globals define, config, socket, app*/
|
||||
|
||||
define('composer/tags', function() {
|
||||
var tags = {};
|
||||
|
||||
tags.init = function(postContainer, postData) {
|
||||
var tagEl = postContainer.find('.tags');
|
||||
if (!tagEl.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
tagEl.tagsinput({
|
||||
maxTags: config.tagsPerTopic,
|
||||
maxChars: config.maximumTagLength,
|
||||
confirmKeys: [13, 44],
|
||||
trimValue: true
|
||||
});
|
||||
|
||||
tagEl.on('beforeItemAdd', function(event) {
|
||||
event.cancel = event.item.length < config.minimumTagLength || event.item.length > config.maximumTagLength;
|
||||
if (event.item.length < config.minimumTagLength) {
|
||||
app.alertError('[[error:tag-too-short, ' + config.minimumTagLength + ']]');
|
||||
} else if (event.item.length > config.maximumTagLength) {
|
||||
app.alertError('[[error:tag-too-long, ' + config.maximumTagLength + ']]');
|
||||
}
|
||||
});
|
||||
|
||||
tagEl.on('itemAdded', function(event) {
|
||||
$(window).trigger('action:tag.added', {cid: postData.cid, tagEl: tagEl, tag: event.item});
|
||||
});
|
||||
|
||||
addTags(postData.tags, tagEl);
|
||||
|
||||
var input = postContainer.find('.bootstrap-tagsinput input');
|
||||
|
||||
app.loadJQueryUI(function() {
|
||||
input.autocomplete({
|
||||
delay: 100,
|
||||
open: function() {
|
||||
$(this).autocomplete('widget').css('z-index', 20000);
|
||||
},
|
||||
source: function(request, response) {
|
||||
socket.emit('topics.searchTags', {query: request.term, cid: postData.cid}, function(err, tags) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
if (tags) {
|
||||
response(tags);
|
||||
}
|
||||
$('.ui-autocomplete a').attr('data-ajaxify', 'false');
|
||||
});
|
||||
},
|
||||
select: function(event, ui) {
|
||||
// when autocomplete is selected from the dropdown simulate a enter key down to turn it into a tag
|
||||
triggerEnter(input);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
input.attr('tabIndex', tagEl.attr('tabIndex'));
|
||||
input.on('blur', function() {
|
||||
triggerEnter(input);
|
||||
});
|
||||
};
|
||||
|
||||
function triggerEnter(input) {
|
||||
// http://stackoverflow.com/a/3276819/583363
|
||||
var e = jQuery.Event('keypress');
|
||||
e.which = 13;
|
||||
e.keyCode = 13;
|
||||
setTimeout(function() {
|
||||
input.trigger(e);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function addTags(tags, tagEl) {
|
||||
if (tags && tags.length) {
|
||||
for(var i=0; i<tags.length; ++i) {
|
||||
tagEl.tagsinput('add', tags[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags.getTags = function(post_uuid) {
|
||||
return $('#cmp-uuid-' + post_uuid + ' .tags').tagsinput('items');
|
||||
};
|
||||
|
||||
return tags;
|
||||
});
|
||||
@@ -1,324 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, utils, config, app */
|
||||
|
||||
define('composer/uploads', ['composer/preview', 'csrf'], function(preview, csrf) {
|
||||
var uploads = {
|
||||
inProgress: {}
|
||||
};
|
||||
|
||||
uploads.initialize = function(post_uuid) {
|
||||
|
||||
initializeDragAndDrop(post_uuid);
|
||||
initializePaste(post_uuid);
|
||||
|
||||
addChangeHandlers(post_uuid);
|
||||
addTopicThumbHandlers(post_uuid);
|
||||
};
|
||||
|
||||
function addChangeHandlers(post_uuid) {
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid);
|
||||
|
||||
postContainer.find('#files').on('change', function(e) {
|
||||
var files = (e.target || {}).files || ($(this).val() ? [{name: $(this).val(), type: utils.fileMimeType($(this).val())}] : null);
|
||||
if(files) {
|
||||
uploadContentFiles({files: files, post_uuid: post_uuid, route: '/api/post/upload'});
|
||||
}
|
||||
});
|
||||
|
||||
postContainer.find('#topic-thumb-file').on('change', function(e) {
|
||||
var files = (e.target || {}).files || ($(this).val() ? [{name: $(this).val(), type: utils.fileMimeType($(this).val())}] : null),
|
||||
fd;
|
||||
|
||||
if(files) {
|
||||
if (window.FormData) {
|
||||
fd = new FormData();
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
fd.append('files[]', files[i], files[i].name);
|
||||
}
|
||||
}
|
||||
uploadTopicThumb({files: files, post_uuid: post_uuid, route: '/api/topic/thumb/upload', formData: fd});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addTopicThumbHandlers(post_uuid) {
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid);
|
||||
|
||||
postContainer.on('click', '.topic-thumb-clear-btn', function(e) {
|
||||
postContainer.find('input#topic-thumb-url').val('').trigger('change');
|
||||
resetInputFile(postContainer.find('input#topic-thumb-file'));
|
||||
$(this).addClass('hide');
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
postContainer.on('paste change keypress', 'input#topic-thumb-url', function() {
|
||||
var urlEl = $(this);
|
||||
setTimeout(function(){
|
||||
var url = urlEl.val();
|
||||
if (url) {
|
||||
postContainer.find('.topic-thumb-clear-btn').removeClass('hide');
|
||||
} else {
|
||||
resetInputFile(postContainer.find('input#topic-thumb-file'));
|
||||
postContainer.find('.topic-thumb-clear-btn').addClass('hide');
|
||||
}
|
||||
postContainer.find('img.topic-thumb-preview').attr('src', url);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
uploads.toggleThumbEls = function(postContainer, url) {
|
||||
var thumbToggleBtnEl = postContainer.find('.topic-thumb-toggle-btn');
|
||||
|
||||
postContainer.find('input#topic-thumb-url').val(url);
|
||||
postContainer.find('img.topic-thumb-preview').attr('src', url);
|
||||
if (url) {
|
||||
postContainer.find('.topic-thumb-clear-btn').removeClass('hide');
|
||||
}
|
||||
thumbToggleBtnEl.removeClass('hide');
|
||||
thumbToggleBtnEl.off('click').on('click', function() {
|
||||
var container = postContainer.find('.topic-thumb-container');
|
||||
container.toggleClass('hide', !container.hasClass('hide'));
|
||||
});
|
||||
};
|
||||
|
||||
function resetInputFile($el) {
|
||||
$el.wrap('<form />').closest('form').get(0).reset();
|
||||
$el.unwrap();
|
||||
}
|
||||
|
||||
function initializeDragAndDrop(post_uuid) {
|
||||
|
||||
function onDragEnter() {
|
||||
if(draggingDocument) {
|
||||
return;
|
||||
}
|
||||
drop.css('top', postContainer.find('.write-preview-container').position().top + 'px');
|
||||
drop.css('height', textarea.height());
|
||||
drop.css('line-height', textarea.height() + 'px');
|
||||
drop.show();
|
||||
|
||||
drop.on('dragleave', function() {
|
||||
drop.hide();
|
||||
drop.off('dragleave');
|
||||
});
|
||||
}
|
||||
|
||||
function onDragDrop(e) {
|
||||
e.preventDefault();
|
||||
var files = e.files || (e.dataTransfer || {}).files || (e.target.value ? [e.target.value] : []),
|
||||
fd;
|
||||
|
||||
if(files.length) {
|
||||
if (window.FormData) {
|
||||
fd = new FormData();
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
fd.append('files[]', files[i], files[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
uploadContentFiles({
|
||||
files: files,
|
||||
post_uuid: post_uuid,
|
||||
route: '/api/post/upload',
|
||||
formData: fd
|
||||
});
|
||||
}
|
||||
|
||||
drop.hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
function cancel(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
if($.event.props.indexOf('dataTransfer') === -1) {
|
||||
$.event.props.push('dataTransfer');
|
||||
}
|
||||
|
||||
var draggingDocument = false;
|
||||
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
drop = postContainer.find('.imagedrop'),
|
||||
textarea = postContainer.find('textarea');
|
||||
|
||||
$(document).off('dragstart').on('dragstart', function() {
|
||||
draggingDocument = true;
|
||||
}).off('dragend').on('dragend', function() {
|
||||
draggingDocument = false;
|
||||
});
|
||||
|
||||
textarea.on('dragenter', onDragEnter);
|
||||
|
||||
drop.on('dragover', cancel);
|
||||
drop.on('dragenter', cancel);
|
||||
drop.on('drop', onDragDrop);
|
||||
}
|
||||
|
||||
function initializePaste(post_uuid) {
|
||||
$(window).off('paste').on('paste', function(event) {
|
||||
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData || {}).items,
|
||||
fd;
|
||||
|
||||
if(items && items.length) {
|
||||
|
||||
var blob = items[0].getAsFile();
|
||||
if(blob) {
|
||||
blob.name = 'upload-' + utils.generateUUID();
|
||||
|
||||
if (window.FormData) {
|
||||
fd = new FormData();
|
||||
fd.append('files[]', blob, blob.name);
|
||||
}
|
||||
|
||||
uploadContentFiles({
|
||||
files: [blob],
|
||||
post_uuid: post_uuid,
|
||||
route: '/api/post/upload',
|
||||
formData: fd
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escapeRegExp(text) {
|
||||
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
}
|
||||
|
||||
function maybeParse(response) {
|
||||
if (typeof response === 'string') {
|
||||
try {
|
||||
return $.parseJSON(response);
|
||||
} catch (e) {
|
||||
return {status: 500, message: 'Something went wrong while parsing server response'};
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
function insertText(str, index, insert) {
|
||||
return str.slice(0, index) + insert + str.slice(index);
|
||||
}
|
||||
|
||||
function uploadContentFiles(params) {
|
||||
var files = params.files,
|
||||
post_uuid = params.post_uuid,
|
||||
formData = params.formData,
|
||||
postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
textarea = postContainer.find('textarea'),
|
||||
text = textarea.val(),
|
||||
uploadForm = postContainer.find('#fileForm');
|
||||
|
||||
uploadForm.attr('action', config.relative_path + params.route);
|
||||
|
||||
for(var i = 0; i < files.length; ++i) {
|
||||
var isImage = files[i].type.match(/image./);
|
||||
|
||||
text = insertText(text, textarea.getCursorPosition(), (isImage ? '!' : '') + '[' + files[i].name + '](uploading...) ');
|
||||
|
||||
if(files[i].size > parseInt(config.maximumFileSize, 10) * 1024) {
|
||||
uploadForm[0].reset();
|
||||
return app.alertError('[[error:file-too-big, ' + config.maximumFileSize + ']]');
|
||||
}
|
||||
}
|
||||
|
||||
textarea.val(text);
|
||||
|
||||
uploadForm.off('submit').submit(function() {
|
||||
function updateTextArea(filename, text) {
|
||||
var current = textarea.val();
|
||||
var re = new RegExp(escapeRegExp(filename) + "]\\([^)]+\\)", 'g');
|
||||
textarea.val(current.replace(re, filename + '](' + text + ')'));
|
||||
}
|
||||
|
||||
uploads.inProgress[post_uuid] = uploads.inProgress[post_uuid] || [];
|
||||
uploads.inProgress[post_uuid].push(1);
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
headers: {
|
||||
'x-csrf-token': csrf.get()
|
||||
},
|
||||
resetForm: true,
|
||||
clearForm: true,
|
||||
formData: formData,
|
||||
|
||||
error: onUploadError,
|
||||
|
||||
uploadProgress: function(event, position, total, percent) {
|
||||
for(var i=0; i < files.length; ++i) {
|
||||
updateTextArea(files[i].name, 'uploading ' + percent + '%');
|
||||
}
|
||||
},
|
||||
|
||||
success: function(uploads) {
|
||||
uploads = maybeParse(uploads);
|
||||
|
||||
if(uploads && uploads.length) {
|
||||
for(var i=0; i<uploads.length; ++i) {
|
||||
updateTextArea(uploads[i].name, uploads[i].url);
|
||||
}
|
||||
}
|
||||
preview.render(postContainer);
|
||||
textarea.focus();
|
||||
},
|
||||
|
||||
complete: function() {
|
||||
uploadForm[0].reset();
|
||||
uploads.inProgress[post_uuid].pop();
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
uploadForm.submit();
|
||||
}
|
||||
|
||||
function uploadTopicThumb(params) {
|
||||
var post_uuid = params.post_uuid,
|
||||
formData = params.formData,
|
||||
postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
spinner = postContainer.find('.topic-thumb-spinner'),
|
||||
thumbForm = postContainer.find('#thumbForm');
|
||||
|
||||
thumbForm.attr('action', config.relative_path + params.route);
|
||||
|
||||
thumbForm.off('submit').submit(function() {
|
||||
spinner.removeClass('hide');
|
||||
|
||||
uploads.inProgress[post_uuid] = uploads.inProgress[post_uuid] || [];
|
||||
uploads.inProgress[post_uuid].push(1);
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
headers: {
|
||||
'x-csrf-token': csrf.get()
|
||||
},
|
||||
formData: formData,
|
||||
error: onUploadError,
|
||||
success: function(uploads) {
|
||||
uploads = maybeParse(uploads);
|
||||
|
||||
postContainer.find('#topic-thumb-url').val((uploads[0] || {}).url || '').trigger('change');
|
||||
},
|
||||
complete: function() {
|
||||
uploads.inProgress[post_uuid].pop();
|
||||
spinner.addClass('hide');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
thumbForm.submit();
|
||||
}
|
||||
|
||||
function onUploadError(xhr) {
|
||||
xhr = maybeParse(xhr);
|
||||
app.alertError(xhr.responseText);
|
||||
}
|
||||
|
||||
return uploads;
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ define('search', ['navigator', 'translator'], function(nav, translator) {
|
||||
};
|
||||
|
||||
Search.checkPagePresence = function(tid, callback) {
|
||||
if (parseInt(ajaxify.variables.get('topic_id'), 10) !== parseInt(tid, 10)) {
|
||||
if (parseInt(ajaxify.data.tid, 10) !== parseInt(tid, 10)) {
|
||||
ajaxify.go('topic/' + tid, callback);
|
||||
} else {
|
||||
callback();
|
||||
|
||||
@@ -7,10 +7,16 @@
|
||||
ajaxify.variables = {};
|
||||
|
||||
ajaxify.variables.set = function(key, value) {
|
||||
if (console && console.warn) {
|
||||
console.warn('[deprecated] variables.set is deprecated, please use ajaxify.data, key=' + key);
|
||||
}
|
||||
parsedVariables[key] = value;
|
||||
};
|
||||
|
||||
ajaxify.variables.get = function(key) {
|
||||
if (console && console.warn) {
|
||||
console.warn('[deprecated] variables.get is deprecated, please use ajaxify.data, key=' + key);
|
||||
}
|
||||
return parsedVariables[key];
|
||||
};
|
||||
|
||||
@@ -37,5 +43,9 @@
|
||||
|
||||
ajaxify.variables.set($(element).attr('template-variable'), value);
|
||||
});
|
||||
var dataEl = $('#content [ajaxify-data]');
|
||||
if (dataEl.length) {
|
||||
ajaxify.data = JSON.parse(decodeURIComponent(dataEl.attr('ajaxify-data')));
|
||||
}
|
||||
};
|
||||
}(ajaxify || {}));
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
ajaxify.widgets.render = function(template, url, callback) {
|
||||
if (template.match(/^admin/)) {
|
||||
return false;
|
||||
return callback();
|
||||
}
|
||||
|
||||
var widgetLocations = ['sidebar', 'footer', 'header'], numLocations;
|
||||
|
||||
@@ -63,5 +63,7 @@ var cronJob = require('cron').CronJob,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Analytics.getUnwrittenPageviews = function() {
|
||||
return pageViews;
|
||||
};
|
||||
}(exports));
|
||||
@@ -16,7 +16,14 @@ module.exports = function(Categories) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:category.update', modified[cid], function(err, category) {
|
||||
var modifiedFields = modified[cid];
|
||||
|
||||
if(modifiedFields.hasOwnProperty('name')){
|
||||
modifiedFields.slug = cid + '/' + utils.slugify(modifiedFields.name);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:category.update', {category:modifiedFields}, function(err, categoryData) {
|
||||
var category = categoryData.category;
|
||||
var fields = Object.keys(category);
|
||||
async.each(fields, function(key, next) {
|
||||
updateCategoryField(cid, key, category[key], next);
|
||||
@@ -44,10 +51,7 @@ module.exports = function(Categories) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (key === 'name') {
|
||||
var slug = cid + '/' + utils.slugify(value);
|
||||
db.setObjectField('category:' + cid, 'slug', slug, callback);
|
||||
} else if (key === 'order') {
|
||||
if (key === 'order') {
|
||||
db.sortedSetAdd('categories:cid', value, cid, callback);
|
||||
} else {
|
||||
callback();
|
||||
|
||||
@@ -90,6 +90,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) {
|
||||
userData.status = require('../socket.io').isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline';
|
||||
userData.banned = parseInt(userData.banned, 10) === 1;
|
||||
userData.website = validator.escape(userData.website);
|
||||
userData.websiteLink = !userData.website.startsWith('http') ? 'http://' + userData.website : userData.website;
|
||||
userData.websiteName = userData.website.replace(validator.escape('http://'), '').replace(validator.escape('https://'), '');
|
||||
userData.followingCount = parseInt(userData.followingCount, 10) || 0;
|
||||
userData.followerCount = parseInt(userData.followerCount, 10) || 0;
|
||||
|
||||
@@ -275,7 +275,12 @@ adminController.plugins.get = function(req, res, next) {
|
||||
}
|
||||
|
||||
res.render('admin/extend/plugins' , {
|
||||
plugins: plugins
|
||||
installed: plugins.filter(function(plugin) {
|
||||
return plugin.installed;
|
||||
}),
|
||||
download: plugins.filter(function(plugin) {
|
||||
return !plugin.installed;
|
||||
})
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ groupsController.get = function(req, res, next) {
|
||||
},
|
||||
function(exists, next) {
|
||||
if (!exists) {
|
||||
helpers.notFound(req, res);
|
||||
return helpers.notFound(req, res);
|
||||
}
|
||||
groups.get(groupName, {uid: req.uid}, next);
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ groupsController.getGroupsFromSet = function(uid, sort, start, stop, callback) {
|
||||
|
||||
callback(null, {
|
||||
groups: groups,
|
||||
allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1,
|
||||
nextStart: stop + 1
|
||||
allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1,
|
||||
nextStart: stop + 1
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -79,12 +79,15 @@ groupsController.details = function(req, res, next) {
|
||||
async.parallel({
|
||||
group: function(next) {
|
||||
groups.get(res.locals.groupName, {
|
||||
uid: req.uid
|
||||
uid: req.uid,
|
||||
truncateUserList: true,
|
||||
userListCount: 20
|
||||
}, next);
|
||||
},
|
||||
posts: function(next) {
|
||||
groups.getLatestMemberPosts(res.locals.groupName, 10, req.uid, next);
|
||||
}
|
||||
},
|
||||
isAdmin: async.apply(user.isAdministrator, req.uid)
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
|
||||
@@ -135,6 +135,9 @@ Controllers.register = function(req, res, next) {
|
||||
|
||||
Controllers.compose = function(req, res, next) {
|
||||
if (req.query.p && !res.locals.isAPI) {
|
||||
if (req.query.p.startsWith(nconf.get('relative_path'))) {
|
||||
req.query.p = req.query.p.replace(nconf.get('relative_path'), '');
|
||||
}
|
||||
return helpers.redirect(res, req.query.p);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,9 +89,9 @@ topicsController.get = function(req, res, next) {
|
||||
}
|
||||
if (!settings.usePagination) {
|
||||
if (reverse) {
|
||||
postIndex = Math.max(0, postCount - (req.params.post_index || postCount) - (settings.postsPerPage / 2));
|
||||
postIndex = Math.max(0, postCount - (req.params.post_index || postCount) - Math.ceil(settings.postsPerPage / 2));
|
||||
} else {
|
||||
postIndex = Math.max(0, (req.params.post_index || 1) - (settings.postsPerPage / 2));
|
||||
postIndex = Math.max(0, (req.params.post_index || 1) - Math.ceil(settings.postsPerPage / 2));
|
||||
}
|
||||
} else if (!req.query.page) {
|
||||
var index = 0;
|
||||
|
||||
@@ -115,27 +115,18 @@ var async = require('async'),
|
||||
}
|
||||
|
||||
options.escape = options.hasOwnProperty('escape') ? options.escape : true;
|
||||
var stop = -1;
|
||||
|
||||
async.parallel({
|
||||
base: function (next) {
|
||||
db.getObject('group:' + groupName, next);
|
||||
},
|
||||
owners: function (next) {
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
db.getSetMembers('group:' + groupName + ':owners', next);
|
||||
},
|
||||
function(uids, next) {
|
||||
user.getUsers(uids, options.uid, next);
|
||||
}
|
||||
], next);
|
||||
},
|
||||
members: function (next) {
|
||||
var stop = -1;
|
||||
if (options.truncateUserList) {
|
||||
stop = (parseInt(options.userListCount, 10) || 4) - 1;
|
||||
}
|
||||
user.getUsersFromSet('group:' + groupName + ':members', options.uid, 0, stop, next);
|
||||
|
||||
Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next);
|
||||
},
|
||||
pending: function (next) {
|
||||
async.waterfall([
|
||||
@@ -164,19 +155,6 @@ var async = require('async'),
|
||||
results.base['cover:position'] = '50% 50%';
|
||||
}
|
||||
|
||||
var ownerUids = [];
|
||||
results.owners.forEach(function(user) {
|
||||
if (user) {
|
||||
user.isOwner = true;
|
||||
ownerUids.push(user.uid.toString());
|
||||
}
|
||||
});
|
||||
|
||||
results.members = results.members.filter(function(user, index, array) {
|
||||
return user && user.uid && ownerUids.indexOf(user.uid.toString()) === -1;
|
||||
});
|
||||
results.members = results.owners.concat(results.members);
|
||||
|
||||
plugins.fireHook('filter:parse.raw', results.base.description, function(err, descriptionParsed) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -190,6 +168,7 @@ var async = require('async'),
|
||||
results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true;
|
||||
results.base.createtimeISO = utils.toISOString(results.base.createtime);
|
||||
results.base.members = results.members;
|
||||
results.base.membersNextStart = stop + 1;
|
||||
results.base.pending = results.pending.filter(Boolean);
|
||||
results.base.deleted = !!parseInt(results.base.deleted, 10);
|
||||
results.base.hidden = !!parseInt(results.base.hidden, 10);
|
||||
@@ -207,6 +186,43 @@ var async = require('async'),
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getOwnersAndMembers = function(groupName, uid, start, stop, callback) {
|
||||
async.parallel({
|
||||
owners: function (next) {
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
db.getSetMembers('group:' + groupName + ':owners', next);
|
||||
},
|
||||
function(uids, next) {
|
||||
user.getUsers(uids, uid, next);
|
||||
}
|
||||
], next);
|
||||
},
|
||||
members: function (next) {
|
||||
user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var ownerUids = [];
|
||||
results.owners.forEach(function(user) {
|
||||
if (user) {
|
||||
user.isOwner = true;
|
||||
ownerUids.push(user.uid.toString());
|
||||
}
|
||||
});
|
||||
|
||||
results.members = results.members.filter(function(user, index, array) {
|
||||
return user && user.uid && ownerUids.indexOf(user.uid.toString()) === -1;
|
||||
});
|
||||
results.members = results.owners.concat(results.members);
|
||||
|
||||
callback(null, results.members);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.escapeGroupData = function(group) {
|
||||
if (group) {
|
||||
group.nameEncoded = encodeURIComponent(group.name);
|
||||
|
||||
@@ -13,6 +13,14 @@ module.exports = function(Groups) {
|
||||
db.isSetMember('group:' + groupName + ':owners', uid, callback);
|
||||
};
|
||||
|
||||
Groups.ownership.isOwners = function(uids, groupName, callback) {
|
||||
if (!Array.isArray(uids)) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
db.isSetMembers('group:' + groupName + ':owners', uids, callback);
|
||||
};
|
||||
|
||||
Groups.ownership.grant = function(toUid, groupName, callback) {
|
||||
// Note: No ownership checking is done here on purpose!
|
||||
db.setAdd('group:' + groupName + ':owners', toUid, callback);
|
||||
|
||||
@@ -26,9 +26,8 @@ module.exports = function(Groups) {
|
||||
return group && !group.hidden;
|
||||
});
|
||||
groupsData.forEach(Groups.escapeGroupData);
|
||||
next(null, groupsData);
|
||||
},
|
||||
async.apply(Groups.sort, options.sort)
|
||||
Groups.sort(options.sort, groupsData, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
@@ -38,13 +37,13 @@ module.exports = function(Groups) {
|
||||
groups = groups.sort(function(a, b) {
|
||||
return a.slug > b.slug;
|
||||
}).sort(function(a, b) {
|
||||
return a.memberCount < b.memberCount;
|
||||
return b.memberCount - a.memberCount;
|
||||
});
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
groups = groups.sort(function(a, b) {
|
||||
return a.createtime < b.createtime;
|
||||
return b.createtime - a.createtime;
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -87,7 +86,48 @@ module.exports = function(Groups) {
|
||||
], callback);
|
||||
}
|
||||
|
||||
if (!data.query) {
|
||||
Groups.getOwnersAndMembers(data.groupName, data.uid, 0, 19, function(err, users) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {users: users});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
data.findUids = findUids;
|
||||
user.search(data, callback);
|
||||
var results;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
user.search(data, next);
|
||||
},
|
||||
function(_results, next) {
|
||||
results = _results;
|
||||
var uids = results.users.map(function(user) {
|
||||
return user && user.uid;
|
||||
});
|
||||
Groups.ownership.isOwners(uids, data.groupName, next);
|
||||
},
|
||||
function(isOwners, next) {
|
||||
|
||||
results.users.forEach(function(user, index) {
|
||||
if (user) {
|
||||
user.isOwner = isOwners[index];
|
||||
}
|
||||
});
|
||||
|
||||
results.users.sort(function(a,b) {
|
||||
if (a.isOwner && !b.isOwner) {
|
||||
return -1;
|
||||
} else if (!a.isOwner && b.isOwner) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
next(null, results);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -390,7 +390,8 @@ var db = require('./database'),
|
||||
bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]',
|
||||
bodyLong: messageObj.content,
|
||||
nid: 'chat_' + fromuid + '_' + touid,
|
||||
from: fromuid
|
||||
from: fromuid,
|
||||
path: '/chats/' + messageObj.fromUser.username
|
||||
}, function(err, notification) {
|
||||
if (!err && notification) {
|
||||
notifications.push(notification, [touid], callback);
|
||||
|
||||
@@ -24,6 +24,7 @@ var async = require('async'),
|
||||
require('./meta/settings')(Meta);
|
||||
require('./meta/logs')(Meta);
|
||||
require('./meta/tags')(Meta);
|
||||
require('./meta/dependencies')(Meta);
|
||||
Meta.templates = require('./meta/templates');
|
||||
|
||||
/* Assorted */
|
||||
|
||||
41
src/meta/dependencies.js
Normal file
41
src/meta/dependencies.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
async = require('async'),
|
||||
semver = require('semver'),
|
||||
winston = require('winston'),
|
||||
|
||||
pkg = require.main.require('./package.json');
|
||||
|
||||
module.exports = function(Meta) {
|
||||
Meta.dependencies = {};
|
||||
|
||||
Meta.dependencies.check = function(callback) {
|
||||
var modules = Object.keys(pkg.dependencies);
|
||||
winston.verbose('Checking dependencies for outdated modules');
|
||||
|
||||
async.every(modules, function(module, next) {
|
||||
fs.readFile(path.join(__dirname, '../../node_modules/', module, 'package.json'), {
|
||||
encoding: 'utf-8'
|
||||
}, function(err, pkgData) {
|
||||
try {
|
||||
var pkgData = JSON.parse(pkgData),
|
||||
ok = semver.satisfies(pkgData.version, pkg.dependencies[module]);
|
||||
|
||||
if (ok) {
|
||||
next(true);
|
||||
} else {
|
||||
process.stdout.write('[' + 'outdated'.yellow + '] ' + module.bold + ' v' + pkgData.version + ', requires ' + pkg.dependencies[module] + '\n')
|
||||
next(false);
|
||||
}
|
||||
} catch(e) {
|
||||
winston.error('[meta.dependencies] Could not read: ' + module);
|
||||
process.exit();
|
||||
}
|
||||
})
|
||||
}, function(ok) {
|
||||
callback(!ok && global.env !== 'development' ? new Error('dependencies-out-of-date') : null);
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -141,6 +141,7 @@ module.exports = function(Meta) {
|
||||
switch(message.type) {
|
||||
case 'end':
|
||||
Meta.js.cache = message.minified;
|
||||
Meta.js.map = message.sourceMap;
|
||||
onComplete();
|
||||
break;
|
||||
case 'hash':
|
||||
|
||||
@@ -191,7 +191,7 @@ middleware.buildHeader = function(req, res, next) {
|
||||
};
|
||||
|
||||
middleware.renderHeader = function(req, res, callback) {
|
||||
var registrationType = meta.config.registrationType || 'normal'
|
||||
var registrationType = meta.config.registrationType || 'normal';
|
||||
var templateValues = {
|
||||
bootswatchCSS: meta.config['theme:src'],
|
||||
title: meta.config.title || '',
|
||||
@@ -274,7 +274,7 @@ middleware.renderHeader = function(req, res, callback) {
|
||||
templateValues.linkTags = results.tags.link;
|
||||
templateValues.isAdmin = results.user.isAdmin;
|
||||
templateValues.user = results.user;
|
||||
templateValues.userJSON = JSON.stringify(results.user).replace(/'/g, "\\'");
|
||||
templateValues.userJSON = JSON.stringify(results.user);
|
||||
templateValues.customCSS = results.customCSS;
|
||||
templateValues.customJS = results.customJS;
|
||||
templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin;
|
||||
@@ -327,7 +327,7 @@ middleware.processRender = function(req, res, next) {
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
// str = str + '<input type="hidden" ajaxify-data="' + encodeURIComponent(JSON.stringify(options)) + '" />';
|
||||
str = str + '<input type="hidden" ajaxify-data="' + encodeURIComponent(JSON.stringify(options)) + '" />';
|
||||
str = (res.locals.postHeader ? res.locals.postHeader : '') + str + (res.locals.preFooter ? res.locals.preFooter : '');
|
||||
|
||||
if (res.locals.footer) {
|
||||
|
||||
131
src/plugins.js
131
src/plugins.js
@@ -164,81 +164,100 @@ var fs = require('fs'),
|
||||
});
|
||||
};
|
||||
|
||||
Plugins.get = function(id, callback) {
|
||||
var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins/' + id;
|
||||
|
||||
require('request')(url, {
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
Plugins.normalise([body.payload], function(err, normalised) {
|
||||
normalised = normalised.filter(function(plugin) {
|
||||
return plugin.id = id;
|
||||
});
|
||||
return callback(err, !err ? normalised[0] : undefined);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Plugins.getAll = function(callback) {
|
||||
var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins?version=' + require('../package.json').version;
|
||||
|
||||
require('request')(url, function(err, res, body) {
|
||||
require('request')(url, {
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
var plugins = [];
|
||||
|
||||
try {
|
||||
plugins = JSON.parse(body);
|
||||
} catch(err) {
|
||||
if (err) {
|
||||
winston.error('Error parsing plugins : ' + err.message);
|
||||
plugins = [];
|
||||
}
|
||||
|
||||
var pluginMap = {};
|
||||
for(var i=0; i<plugins.length; ++i) {
|
||||
plugins[i].id = plugins[i].name;
|
||||
plugins[i].installed = false;
|
||||
plugins[i].active = false;
|
||||
plugins[i].url = plugins[i].url ? plugins[i].url : plugins[i].repository ? plugins[i].repository.url : '';
|
||||
plugins[i].latest = plugins[i].latest;
|
||||
pluginMap[plugins[i].name] = plugins[i];
|
||||
Plugins.normalise(body, callback);
|
||||
});
|
||||
};
|
||||
|
||||
Plugins.normalise = function(apiReturn, callback) {
|
||||
var pluginMap = {};
|
||||
for(var i=0; i<apiReturn.length; ++i) {
|
||||
apiReturn[i].id = apiReturn[i].name;
|
||||
apiReturn[i].installed = false;
|
||||
apiReturn[i].active = false;
|
||||
apiReturn[i].url = apiReturn[i].url ? apiReturn[i].url : apiReturn[i].repository ? apiReturn[i].repository.url : '';
|
||||
apiReturn[i].latest = apiReturn[i].latest;
|
||||
pluginMap[apiReturn[i].name] = apiReturn[i];
|
||||
}
|
||||
|
||||
Plugins.showInstalled(function(err, installedPlugins) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
Plugins.showInstalled(function(err, installedPlugins) {
|
||||
async.each(installedPlugins, function(plugin, next) {
|
||||
// If it errored out because a package.json or plugin.json couldn't be read, no need to do this stuff
|
||||
if (plugin.error) {
|
||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||
pluginMap[plugin.id].installed = true;
|
||||
pluginMap[plugin.id].error = true;
|
||||
return next();
|
||||
}
|
||||
|
||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||
pluginMap[plugin.id].id = pluginMap[plugin.id].id || plugin.id;
|
||||
pluginMap[plugin.id].name = plugin.name || pluginMap[plugin.id].name;
|
||||
pluginMap[plugin.id].description = plugin.description;
|
||||
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
|
||||
pluginMap[plugin.id].installed = true;
|
||||
pluginMap[plugin.id].isTheme = !!plugin.id.match('nodebb-theme-');
|
||||
pluginMap[plugin.id].error = plugin.error || false;
|
||||
pluginMap[plugin.id].active = plugin.active;
|
||||
pluginMap[plugin.id].version = plugin.version;
|
||||
pluginMap[plugin.id].latest = pluginMap[plugin.id].latest || plugin.version;
|
||||
pluginMap[plugin.id].outdated = semver.gt(pluginMap[plugin.id].latest, pluginMap[plugin.id].version);
|
||||
next();
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.each(installedPlugins, function(plugin, next) {
|
||||
// If it errored out because a package.json or plugin.json couldn't be read, no need to do this stuff
|
||||
if (plugin.error) {
|
||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||
pluginMap[plugin.id].installed = true;
|
||||
pluginMap[plugin.id].error = true;
|
||||
return next();
|
||||
var pluginArray = [];
|
||||
|
||||
for (var key in pluginMap) {
|
||||
if (pluginMap.hasOwnProperty(key)) {
|
||||
pluginArray.push(pluginMap[key]);
|
||||
}
|
||||
}
|
||||
|
||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||
pluginMap[plugin.id].id = pluginMap[plugin.id].id || plugin.id;
|
||||
pluginMap[plugin.id].name = plugin.name || pluginMap[plugin.id].name;
|
||||
pluginMap[plugin.id].description = plugin.description;
|
||||
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
|
||||
pluginMap[plugin.id].installed = true;
|
||||
pluginMap[plugin.id].isTheme = !!plugin.id.match('nodebb-theme-');
|
||||
pluginMap[plugin.id].error = plugin.error || false;
|
||||
pluginMap[plugin.id].active = plugin.active;
|
||||
pluginMap[plugin.id].version = plugin.version;
|
||||
pluginMap[plugin.id].latest = pluginMap[plugin.id].latest || plugin.version;
|
||||
pluginMap[plugin.id].outdated = semver.gt(pluginMap[plugin.id].latest, pluginMap[plugin.id].version);
|
||||
next();
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
pluginArray.sort(function(a, b) {
|
||||
if (a.name > b.name ) {
|
||||
return 1;
|
||||
} else if (a.name < b.name ){
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var pluginArray = [];
|
||||
|
||||
for (var key in pluginMap) {
|
||||
if (pluginMap.hasOwnProperty(key)) {
|
||||
pluginArray.push(pluginMap[key]);
|
||||
}
|
||||
}
|
||||
|
||||
pluginArray.sort(function(a, b) {
|
||||
if (a.name > b.name ) {
|
||||
return 1;
|
||||
} else if (a.name < b.name ){
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, pluginArray);
|
||||
});
|
||||
|
||||
callback(null, pluginArray);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user