Merge remote-tracking branch 'upstream/master'

v1.18.x
Andrea Cardinale 10 years ago
commit 423c6cc0ec

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

@ -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',

@ -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;
}
}

@ -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);
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 selectionText = '',
selection = window.getSelection ? window.getSelection() : document.selection.createRange();
var username = getUserName(selectionText ? $(selection.baseNode) : button);
if (getData(button, 'data-uid') === '0') {
username = '';
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 (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);
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';
}
var username = getUserName(button),
pid = getData(button, 'data-pid');
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);
}
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';
}
$(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, '&#37;').replace(/,/g, '&#44;');
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;
});

@ -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 */

@ -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) {

@ -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 = [];
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);
for (var key in pluginMap) {
if (pluginMap.hasOwnProperty(key)) {
pluginArray.push(pluginMap[key]);
}
}
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;
}
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

Loading…
Cancel
Save