Merge pull request from BenLubar/yamikuronue-coverage-fix

fixed the merge conflict by merging
v1.18.x
Yami
commit 5158314ac4

@ -149,9 +149,7 @@ function start() {
meta.reload();
break;
case 'js-propagate':
meta.js.target[message.target] = meta.js.target[message.target] || {};
meta.js.target[message.target].cache = message.cache;
meta.js.target[message.target].map = message.map;
meta.js.target = message.data;
emitter.emit('meta:js.compiled');
winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid);
break;

@ -34,8 +34,9 @@ var pidFilePath = __dirname + '/pidfile',
Loader.init = function(callback) {
if (silent) {
console.log = function(value) {
output.write(value + '\n');
console.log = function() {
var args = Array.prototype.slice.call(arguments);
output.write(args.join(' ') + '\n');
};
}
@ -122,15 +123,11 @@ Loader.addWorkerEvents = function(worker) {
Loader.reload();
break;
case 'js-propagate':
Loader.js.target[message.target] = Loader.js.target[message.target] || {};
Loader.js.target[message.target].cache = message.cache;
Loader.js.target[message.target].map = message.map;
Loader.js.target = message.data;
Loader.notifyWorkers({
action: 'js-propagate',
cache: message.cache,
map: message.map,
target: message.target
data: message.data
}, worker.pid);
break;
case 'css-propagate':

@ -86,7 +86,7 @@
},
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-watch": "^0.6.1",
"grunt-contrib-watch": "^1.0.0",
"istanbul": "^0.4.2",
"mocha": "~1.13.0"
},

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "Качване на файла…",
"select-file-to-upload": "Изберете файл за качване!",
"upload-success": "Файлът е качен успешно!",
"maximum-file-size": "Най-много %1 КБ"
}

@ -17,7 +17,7 @@
"reset.notify.text1": "Wir benachrichtigen dich, dass dein Passwort am %1 erfolgreich geändert wurde.",
"reset.notify.text2": "Bitte benachrichtige umgehend einen Administrator, wenn du dies nicht autorisiert hast.",
"digest.notifications": "Du hast ungelesene Benachrichtigungen von %1:",
"digest.latest_topics": "Neueste Themen vom %1",
"digest.latest_topics": "Neueste Themen auf %1",
"digest.cta": "Klicke hier, um %1 zu besuchen",
"digest.unsub.info": "Diese Zusammenfassung wurde dir aufgrund deiner Abonnement-Einstellungen gesendet.",
"digest.no_topics": "Es gab keine aktiven Themen innerhalb %1",

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "Lade Datei hoch...",
"select-file-to-upload": "Wähle eine Datei zum Hochladen aus!",
"upload-success": "Datei erfolgreich hochgeladen!",
"maximum-file-size": "Maximal %1 kb"
}

@ -65,7 +65,7 @@
"show_email": "Zeige meine E-Mail Adresse an.",
"show_fullname": "Zeige meinen kompletten Namen an",
"restrict_chats": "Nur Chatnachrichten von Benutzern, denen ich folge, erlauben",
"digest_label": "Auszug abonnieren",
"digest_label": "Zusammenfassung abonnieren",
"digest_description": "Abonniere E-Mail-Benachrichtigungen für dieses Forum (neue Benachrichtigungen und Themen) nach einem festen Zeitplan.",
"digest_off": "Aus",
"digest_daily": "Täglich",

@ -31,6 +31,7 @@
"digest.day": "day",
"digest.week": "week",
"digest.month": "month",
"digest.subject": "Digest for %1",
"notif.chat.subject": "New chat message received from %1",
"notif.chat.cta": "Click here to continue the conversation",

@ -109,6 +109,7 @@
"cant-remove-last-user": "You can't remove the last user",
"cant-delete-chat-message": "You are not allowed to delete this message",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",
"not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post",

@ -14,7 +14,7 @@
"invalid-password": "Contraseña no válida",
"invalid-username-or-password": "Por favor especifica tanto un usuario como contraseña",
"invalid-search-term": "Término de búsqueda inválido",
"invalid-pagination-value": "Invalid pagination value, must be at least %1 and at most %2",
"invalid-pagination-value": "Número de página inválido, debe estar entre %1 y %2",
"username-taken": "Nombre de usuario ocupado",
"email-taken": "Correo electrónico ocupado",
"email-not-confirmed": "Su cuenta de correo electrónico no ha sido confirmada aún, por favor haga click aquí para confirmarla.",
@ -84,7 +84,7 @@
"chat-message-too-long": "Mensaje de Chat es demasiado largo",
"cant-edit-chat-message": "No tienes permiso para editar este mensaje",
"cant-remove-last-user": "No puedes eliminar el último usuario",
"cant-delete-chat-message": "You are not allowed to delete this message",
"cant-delete-chat-message": "No tienes permiso para eliminar este mensaje",
"reputation-system-disabled": "El sistema de reputación está deshabilitado.",
"downvoting-disabled": "La votación negativa está deshabilitada.",
"not-enough-reputation-to-downvote": "No tienes suficiente reputación para votar negativo este post",
@ -97,7 +97,7 @@
"wrong-login-type-username": "Por favor introduce tu nombre de usuario para acceder",
"invite-maximum-met": "Has alcanzado el número máximo de personas invitadas (%1 de %2).",
"no-session-found": "¡No se ha encontrado ningún inicio de sesión!",
"not-in-room": "User not in room",
"no-users-in-room": "No users in this room",
"cant-kick-self": "You can't kick yourself from the group"
"not-in-room": "El usuario no está en la sala",
"no-users-in-room": "No hay usuarios en esta sala",
"cant-kick-self": "No te puedes expulsar a ti mismo del grupo"
}

@ -65,7 +65,7 @@
"posted_in_ago_by": "publicado en %1 %2 por %3",
"user_posted_ago": "%1 publicó %2",
"guest_posted_ago": "Invitado publicó %1",
"last_edited_by": "last edited by %1",
"last_edited_by": "Última edición por %1",
"norecentposts": "No hay publicaciones recientes",
"norecenttopics": "No hay temas recientes",
"recentposts": "Publicaciones recientes",
@ -87,8 +87,8 @@
"map": "Mapa",
"sessions": "Inicios de sesión",
"ip_address": "Direcciones IP",
"enter_page_number": "Enter page number",
"upload_file": "Upload file",
"upload": "Upload",
"allowed-file-types": "Allowed file types are %1"
"enter_page_number": "Escribe el número de página",
"upload_file": "Subir archivo",
"upload": "Subir",
"allowed-file-types": "Los tipos de archivos permitidos son: %1"
}

@ -41,7 +41,7 @@
"details.hidden": "Oculto",
"details.hidden_help": "Si está habilitado, este grupo no aparecerá en los listados de grupos, y los usuarios tendrán que ser invitados manualmente",
"details.delete_group": "Eliminar grupo",
"details.private_system_help": "Private groups is disabled at system level, this option does not do anything",
"details.private_system_help": "Los grupos privados están desactivados a nivel de sistema, esta opción no cambiará nada.",
"event.updated": "Los detalles del grupo han sido actualizados",
"event.deleted": "El grupo \"%1\" ha sido eliminado",
"membership.accept-invitation": "Aceptar Invitación",
@ -50,5 +50,5 @@
"membership.leave-group": "Dejar el grupo",
"membership.reject": "Rechazar",
"new-group.group_name": "Nombre de Grupo:",
"upload-group-cover": "Upload group cover"
"upload-group-cover": "Cargar foto para el grupo"
}

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "Envoi du fichier…",
"select-file-to-upload": "Sélectionnez un ficher à envoyer",
"upload-success": "Fichier envoyé",
"maximum-file-size": "%1 Ko maximum"
}

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "파일 업로드 중...",
"select-file-to-upload": "업로드할 파일을 선택해 주세요!",
"upload-success": "파일이 성공적으로 업로드 되었습니다!",
"maximum-file-size": "최대 %1 kb"
}

@ -52,7 +52,7 @@
"best": "Beste",
"upvoted": "Omhoog gestemd",
"downvoted": "Omlaag gestemd",
"views": "Gezien",
"views": "Weergaven",
"reputation": "Reputatie",
"read_more": "Lees meer",
"more": "Meer",

@ -104,9 +104,9 @@
"stale.title": "Een nieuw onderwerp maken in de plaats?",
"stale.warning": "Het onderwerp waar je op antwoord is vrij oud. Zou je graag een nieuw onderwerp maken met een referentie naar dit onderwerp in je antwoord?",
"stale.create": "Maak een nieuw onderwerp",
"stale.reply_anyway": "Antwoord toch op dit onderwerp",
"stale.reply_anyway": "Reageer toch op dit onderwerp",
"link_back": "Re: [%1](%2)",
"spam": "Spam",
"offensive": "Onbeleefd",
"offensive": "Aanstootgevend",
"custom-flag-reason": "Geef een reden voor de melding."
}

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "Fazendo upload do arquivo...",
"select-file-to-upload": "Escolha um arquivo para fazer upload!",
"upload-success": "Upload realizado com sucesso!",
"maximum-file-size": "No máximo %1 kb"
}

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "Dosya yükleniyor...",
"select-file-to-upload": "Bir dosya seç!",
"upload-success": "Dosya yüklenmesi tamamlandı!",
"maximum-file-size": "Maksimum %1 kb"
}

@ -27,7 +27,7 @@
"password-too-long": "密码太长",
"user-banned": "用户已禁止",
"user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!",
"blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.",
"blacklisted-ip": "对不起您的IP地址已被社区禁用。如果您认为这是一个错误请与管理员联系。",
"no-category": "版块不存在",
"no-topic": "主题不存在",
"no-post": "帖子不存在",
@ -99,5 +99,5 @@
"no-session-found": "未登录!",
"not-in-room": "用户已不在聊天室中",
"no-users-in-room": "这个聊天室中没有用户",
"cant-kick-self": "You can't kick yourself from the group"
"cant-kick-self": "你不能把自己踢出群组"
}

@ -41,7 +41,7 @@
"details.hidden": "隐藏",
"details.hidden_help": "启用此选项后,小组将不在小组列表中展现,成员只能通过邀请加入。",
"details.delete_group": "删除小组",
"details.private_system_help": "Private groups is disabled at system level, this option does not do anything",
"details.private_system_help": "系统禁用了私有群组,这个选项不起任何作用",
"event.updated": "小组信息已更新",
"event.deleted": "小组 \"%1\" 已被删除",
"membership.accept-invitation": "接受邀请",

@ -6,7 +6,7 @@
"chat.user_typing": "%1 正在输入……",
"chat.user_has_messaged_you": "%1 向您发送了消息。",
"chat.see_all": "查看所有对话",
"chat.mark_all_read": "Mark all chats read",
"chat.mark_all_read": "将所有聊天标为已读",
"chat.no-messages": "请选择接收人,以查看聊天消息历史",
"chat.no-users-in-room": "此聊天室中没有用户",
"chat.recent-chats": "最近聊天",

@ -1,6 +1,6 @@
{
"uploading-file": "Uploading the file...",
"select-file-to-upload": "Select a file to upload!",
"upload-success": "File uploaded successfully!",
"maximum-file-size": "Maximum %1 kb"
"uploading-file": "正在上传文件...",
"select-file-to-upload": "请选择需要上传的文件!",
"upload-success": "文件上传成功!",
"maximum-file-size": "最大 %1 kb"
}

@ -39,7 +39,7 @@
"change_username": "更改用户名",
"change_email": "更改电子邮箱",
"edit": "编辑",
"edit-profile": "Edit Profile",
"edit-profile": "编辑资料",
"default_picture": "缺省图标",
"uploaded_picture": "已有头像",
"upload_new_picture": "上传新头像",
@ -92,7 +92,7 @@
"open_links_in_new_tab": "在新标签打开外部链接",
"enable_topic_searching": "启用主题内搜索",
"topic_search_help": "如果启用此项,主题内搜索会替代浏览器默认的页面搜索,您将可以在整个主题内搜索,而不仅仅只搜索页面上展现的内容。",
"scroll_to_my_post": "After posting a reply, show the new post",
"scroll_to_my_post": "在提交回复之后显示新帖子",
"follow_topics_you_reply_to": "关注您回复的主题",
"follow_topics_you_create": "关注您创建的主题",
"grouptitle": "选择展示的小组称号",

@ -14,7 +14,7 @@
&[data-state="unloaded"], &[data-state="loading"] {
display: inherit;
height: 2rem;
height: 0;
opacity: 0;
}

@ -99,6 +99,48 @@ define('admin/manage/category', [
});
});
$('.copy-settings').on('click', function(e) {
e.preventDefault();
socket.emit('admin.categories.getNames', function(err, categories) {
if (err) {
return app.alertError(err.message);
}
templates.parse('admin/partials/categories/select-category', {
categories: categories
}, function(html) {
function submit() {
var formData = modal.find('form').serializeObject();
socket.emit('admin.categories.copySettingsFrom', {fromCid: formData['select-cid'], toCid: ajaxify.data.category.cid}, function(err) {
if (err) {
return app.alertError(err.message);
}
app.alertSuccess('Settings Copied!');
ajaxify.refresh();
});
modal.modal('hide');
return false;
}
var modal = bootbox.dialog({
title: 'Select a Category',
message: html,
buttons: {
save: {
label: 'Copy',
className: 'btn-primary',
callback: submit
}
}
});
modal.find('form').on('submit', submit);
});
});
});
$('.upload-button').on('click', function() {
var inputEl = $(this);
var cid = inputEl.attr('data-cid');

@ -118,6 +118,8 @@ define('forum/topic/events', [
editedPostEl.find('img:not(.not-responsive)').addClass('img-responsive');
app.replaceSelfLinks(editedPostEl.find('a'));
posts.wrapImagesInLinks(editedPostEl.parent());
posts.unloadImages(editedPostEl.parent());
posts.loadImages();
editedPostEl.fadeIn(250);
$(window).trigger('action:posts.edited', data);
});

@ -11,7 +11,7 @@ define('forum/topic/posts', [
], function(pagination, infinitescroll, postTools, navigator, components) {
var Posts = {
_threshold: 0
_imageLoaderTimeout: undefined
};
Posts.onNewPost = function(data) {
@ -181,10 +181,13 @@ define('forum/topic/posts', [
}
Posts.loadMorePosts = function(direction) {
if (!components.get('topic').length || navigator.scrollActive) {
if (!components.get('topic').length || navigator.scrollActive || Posts._infiniteScrollTimeout) {
return;
}
Posts._infiniteScrollTimeout = setTimeout(function() {
delete Posts._infiniteScrollTimeout;
}, 1000);
var replies = components.get('post').not('[data-index=0]').not('.new');
var afterEl = direction > 0 ? replies.last() : replies.first();
var after = parseInt(afterEl.attr('data-index'), 10) || 0;
@ -204,7 +207,6 @@ define('forum/topic/posts', [
after: after,
direction: direction
}, function (data, done) {
indicatorEl.fadeOut();
if (data && data.posts && data.posts.length) {
@ -235,20 +237,21 @@ define('forum/topic/posts', [
};
Posts.unloadImages = function(posts) {
var images = posts.find('[component="post/content"] img:not(.not-responsive)'),
scrollTop = $(window).scrollTop(),
height = $(document).height();
var images = posts.find('[component="post/content"] img:not(.not-responsive)');
images.each(function() {
$(this).attr('data-src', $(this).attr('src'));
$(this).attr('data-state', 'unloaded');
$(this).attr('src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
$(this).attr('src', 'about:blank');
});
$(window).scrollTop(scrollTop + $(document).height() - height);
};
Posts.loadImages = function(threshold) {
if (Posts._imageLoaderTimeout) {
clearTimeout(Posts._imageLoaderTimeout);
}
Posts._imageLoaderTimeout = setTimeout(function() {
/*
If threshold is defined, images loaded above this threshold will modify
the user's scroll position so they are not scrolled away from content
@ -257,7 +260,6 @@ define('forum/topic/posts', [
If no threshold is defined, loaded images will push down content, as per
default
*/
Posts._threshold = threshold;
var images = components.get('post/content').find('img[data-state="unloaded"]'),
visible = images.filter(function() {
@ -275,7 +277,7 @@ define('forum/topic/posts', [
newHeight = document.body.clientHeight;
var imageRect = this.getBoundingClientRect();
if (imageRect.top < Posts._threshold) {
if (imageRect.top < threshold) {
scrollTop = scrollTop + (newHeight - oldHeight);
$(window).scrollTop(scrollTop);
}
@ -302,11 +304,12 @@ define('forum/topic/posts', [
});
image.attr('src', image.attr('data-src'));
if (image.parent().attr('href')) {
if (image.parent().attr('href') === 'about:blank') {
image.parent().attr('href', image.attr('data-src'));
}
image.removeAttr('data-src');
});
}, 250);
};
Posts.wrapImagesInLinks = function(posts) {
@ -321,13 +324,10 @@ define('forum/topic/posts', [
Posts.showBottomPostBar = function() {
var mainPost = components.get('post', 'index', 0);
var posts = $('[component="post"]');
var height = $(document).height();
if (!!mainPost.length && posts.length > 1 && $('.post-bar').length < 2) {
$('.post-bar').clone().appendTo(mainPost);
$(window).scrollTop($(window).scrollTop() + $(document).height() - height);
} else if (mainPost.length && posts.length < 2) {
mainPost.find('.post-bar').remove();
$(window).scrollTop($(window).scrollTop() - $(document).height() - height);
}
};

@ -121,21 +121,25 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
}
});
var atTop = scrollTop === 0 && parseInt(els.first().attr('data-index'), 10) === 0,
nearBottom = scrollTop + windowHeight > documentHeight - 100 && parseInt(els.last().attr('data-index'), 10) === count - 1;
// check if we are at the top
if (scrollTop === 0 && parseInt(els.first().attr('data-index'), 10) === 0) {
if (atTop) {
index = 1;
// check if we are near the bottom
} else if (scrollTop + windowHeight > documentHeight - 100 && parseInt(els.last().attr('data-index'), 10) === count - 1) {
} else if (nearBottom) {
index = count;
}
// If a threshold is undefined, try to determine one based on new index
if (threshold === undefined) {
if (atTop) {
threshold = 0;
} else {
var anchorEl = components.get('post/anchor', index - 1);
var anchorRect = anchorEl.get(0).getBoundingClientRect();
threshold = anchorRect.top;
}
}
if (typeof navigator.callback === 'function') {
navigator.callback(index, count, threshold);

@ -16,9 +16,10 @@ define('uploader', ['csrf', 'translator'], function(csrf, translator) {
};
module.show = function(data, callback) {
var fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false;
parseModal({
showHelp: data.hasOwnProperty('showHelp') && data.showHelp !== undefined ? data.showHelp : true,
fileSize: data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false,
fileSize: fileSize,
title: data.title || '[[global:upload_file]]',
description: data.description || '',
button: data.button || '[[global:upload]]',
@ -40,13 +41,17 @@ define('uploader', ['csrf', 'translator'], function(csrf, translator) {
});
uploadForm.submit(function() {
onSubmit(uploadModal, callback);
onSubmit(uploadModal, fileSize, callback);
return false;
});
});
};
function onSubmit(uploadModal, callback) {
module.hideAlerts = function(modal) {
$(modal).find('#alert-status, #alert-success, #alert-error, #upload-progress-box').addClass('hide');
};
function onSubmit(uploadModal, fileSize, callback) {
function showAlert(type, message) {
module.hideAlerts(uploadModal);
uploadModal.find('#alert-' + type).translateText(message).removeClass('hide');
@ -57,9 +62,13 @@ define('uploader', ['csrf', 'translator'], function(csrf, translator) {
uploadModal.find('#upload-progress-bar').css('width', '0%');
uploadModal.find('#upload-progress-box').show().removeClass('hide');
if (!uploadModal.find('#fileInput').val()) {
var fileInput = uploadModal.find('#fileInput');
if (!fileInput.val()) {
return showAlert('error', '[[uploads:select-file-to-upload]]');
}
if (!hasValidFileSize(fileInput[0], fileSize)) {
return showAlert('error', '[[error:file-too-big, ' + fileSize + ']]');
}
uploadModal.find('#uploadForm').ajaxSubmit({
headers: {
@ -107,9 +116,12 @@ define('uploader', ['csrf', 'translator'], function(csrf, translator) {
return response;
}
module.hideAlerts = function(modal) {
$(modal).find('#alert-status, #alert-success, #alert-error, #upload-progress-box').addClass('hide');
};
function hasValidFileSize(fileElement, maxSize) {
if (window.FileReader && maxSize) {
return fileElement.files[0].size <= maxSize * 1000;
}
return true;
}
return module;
});

@ -1,10 +1,12 @@
'use strict';
var async = require('async'),
db = require('../database'),
privileges = require('../privileges'),
plugins = require('../plugins'),
utils = require('../../public/src/utils');
var async = require('async');
var db = require('../database');
var privileges = require('../privileges');
var groups = require('../groups');
var plugins = require('../plugins');
var utils = require('../../public/src/utils');
module.exports = function(Categories) {
@ -17,6 +19,7 @@ module.exports = function(Categories) {
db.incrObjectField('global', 'nextCid', next);
},
function(cid, next) {
data.name = data.name || 'Category ' + cid;
var slug = cid + '/' + utils.slugify(data.name);
var order = data.order || cid; // If no order provided, place it at the end
var colours = Categories.assignColours();
@ -58,6 +61,12 @@ module.exports = function(Categories) {
], next);
},
function(results, next) {
if (data.cloneFromCid && parseInt(data.cloneFromCid, 10)) {
return Categories.copySettingsFrom(data.cloneFromCid, category.cid, next);
}
next(null, category);
},
function(category, next) {
plugins.fireHook('action:category.create', category);
next(null, category);
}
@ -65,10 +74,94 @@ module.exports = function(Categories) {
};
Categories.assignColours = function() {
var backgrounds = ['#AB4642', '#DC9656', '#F7CA88', '#A1B56C', '#86C1B9', '#7CAFC2', '#BA8BAF', '#A16946'],
text = ['#fff', '#fff', '#333', '#fff', '#333', '#fff', '#fff', '#fff'],
index = Math.floor(Math.random() * backgrounds.length);
var backgrounds = ['#AB4642', '#DC9656', '#F7CA88', '#A1B56C', '#86C1B9', '#7CAFC2', '#BA8BAF', '#A16946'];
var text = ['#fff', '#fff', '#333', '#fff', '#333', '#fff', '#fff', '#fff'];
var index = Math.floor(Math.random() * backgrounds.length);
return [backgrounds[index], text[index]];
};
Categories.copySettingsFrom = function(fromCid, toCid, callback) {
var destination;
async.waterfall([
function (next) {
async.parallel({
source: async.apply(db.getObject, 'category:' + fromCid),
destination: async.apply(db.getObject, 'category:' + toCid)
}, next);
},
function (results, next) {
if (!results.source) {
return next(new Error('[[error:invalid-cid]]'));
}
destination = results.destination;
var tasks = [];
if (parseInt(results.source.parentCid, 10)) {
tasks.push(async.apply(db.sortedSetAdd, 'cid:' + results.source.parentCid + ':children', results.source.order, toCid));
}
if (destination && parseInt(destination.parentCid, 10)) {
tasks.push(async.apply(db.sortedSetRemove, 'cid:' + destination.parentCid + ':children', toCid));
}
destination.description = results.source.description;
destination.descriptionParsed = results.source.descriptionParsed;
destination.icon = results.source.icon;
destination.bgColor = results.source.bgColor;
destination.color = results.source.color;
destination.link = results.source.link;
destination.numRecentReplies = results.source.numRecentReplies;
destination.class = results.source.class;
destination.imageClass = results.source.imageClass;
destination.parentCid = results.source.parentCid || 0;
tasks.push(async.apply(db.setObject, 'category:' + toCid, destination));
async.series(tasks, next);
},
function (results, next) {
Categories.copyPrivilegesFrom(fromCid, toCid, next);
}
], function(err) {
callback(err, destination);
});
};
Categories.copyPrivilegesFrom = function(fromCid, toCid, callback) {
var privilegeList = [
'find', 'read', 'topics:create', 'topics:reply', 'purge', 'mods',
'groups:find', 'groups:read', 'groups:topics:create', 'groups:topics:reply', 'groups:purge', 'groups:moderate'
];
async.each(privilegeList, function(privilege, next) {
copyPrivilege(privilege, fromCid, toCid, next);
}, callback);
};
function copyPrivilege(privilege, fromCid, toCid, callback) {
async.waterfall([
function (next) {
db.getSortedSetRange('group:cid:' + toCid + ':privileges:' + privilege + ':members', 0, -1, next);
},
function (currentMembers, next) {
async.eachSeries(currentMembers, function(member, next) {
groups.leave('cid:' + toCid + ':privileges:' + privilege, member, next);
}, next);
},
function (next) {
db.getSortedSetRange('group:cid:' + fromCid + ':privileges:' + privilege + ':members', 0, -1, next);
},
function (members, next) {
if (!members || !members.length) {
return callback();
}
async.eachSeries(members, function(member, next) {
groups.join('cid:' + toCid + ':privileges:' + privilege, member, next);
}, next);
}
], callback);
}
};

@ -440,7 +440,8 @@ function enableDefaultPlugins(next) {
'nodebb-widget-essentials',
'nodebb-rewards-essentials',
'nodebb-plugin-soundpack-default',
'nodebb-plugin-emoji-extended'
'nodebb-plugin-emoji-extended',
'nodebb-plugin-emoji-apple'
],
customDefaults = nconf.get('defaultPlugins');

@ -61,11 +61,9 @@ var async = require('async'),
async.apply(plugins.clearRequireCache),
async.apply(plugins.reload),
async.apply(plugins.reloadRoutes),
function(next) {
async.parallel([
async.apply(Meta.css.minify),
async.apply(Meta.js.minify, 'nodebb.min.js'),
async.apply(Meta.js.minify, 'acp.min.js'),
async.apply(Meta.css.minify),
async.apply(Meta.sounds.init),
async.apply(Meta.templates.compile),
async.apply(auth.reloadRoutes),
@ -74,8 +72,6 @@ var async = require('async'),
templates.flush();
next();
}
], next);
}
], function(err) {
if (!err) {
emitter.emit('nodebb:ready');

@ -43,7 +43,8 @@ module.exports = function(Meta) {
path.join(__dirname, '../../public/vendor/fontawesome/less'),
path.join(__dirname, '../../public/vendor/bootstrap/less')
],
source = '@import "font-awesome";';
source = '@import "font-awesome";',
acpSource = '@import "font-awesome";';
plugins.lessFiles = filterMissingFiles(plugins.lessFiles);
plugins.cssFiles = filterMissingFiles(plugins.cssFiles);
@ -67,20 +68,20 @@ module.exports = function(Meta) {
source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui-1.10.4.custom.min.css";';
source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";';
source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";';
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/flags.less";';
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";';
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";';
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";';
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/global.less";';
source = '@import "./theme";\n' + source;
var acpSource = '\n@import "..' + path.sep + 'public/less/admin/admin";\n' + source;
acpSource += '\n@import "..' + path.sep + 'public/less/admin/admin";\n';
acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";';
acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";';
source = '@import "./theme";\n' + source;
var fromFile = nconf.get('from-file') || '';
async.series([
function(next) {
if (fromFile.match('clientLess')) {
@ -149,7 +150,7 @@ module.exports = function(Meta) {
});
}
Meta.css.commitToFile = function(filename) {
Meta.css.commitToFile = function(filename, callback) {
var file = (filename === 'acpCache' ? 'admin' : 'stylesheet') + '.css';
fs.writeFile(path.join(__dirname, '../../public/' + file), Meta.css[filename], function(err) {
@ -159,6 +160,8 @@ module.exports = function(Meta) {
winston.error('[meta/css] ' + err.message);
process.exit(0);
}
callback();
});
};
@ -184,7 +187,6 @@ module.exports = function(Meta) {
return;
}
winston.verbose('[meta/css] Running PostCSS Plugins');
postcss([ autoprefixer ]).process(lessOutput.css).then(function (result) {
result.warnings().forEach(function (warn) {
winston.verbose(warn.toString());
@ -194,7 +196,11 @@ module.exports = function(Meta) {
// Save the compiled CSS in public/ so things like nginx can serve it
if (nconf.get('isPrimary') === 'true') {
Meta.css.commitToFile(destination);
return Meta.css.commitToFile(destination, function() {
if (typeof callback === 'function') {
callback(null, result.css);
}
});
}
if (typeof callback === 'function') {

@ -89,6 +89,8 @@ module.exports = function(Meta) {
return;
}
winston.verbose('[meta/js] Minifying ' + target);
var forkProcessParams = setupDebugging();
var minifier = Meta.js.minifierProc = fork('minifier.js', [], forkProcessParams);
@ -110,20 +112,19 @@ module.exports = function(Meta) {
winston.verbose('[meta/js] ' + target + ' minification complete');
minifier.kill();
if (process.send) {
if (process.send && Meta.js.target['nodebb.min.js'] && Meta.js.target['acp.min.js']) {
process.send({
action: 'js-propagate',
cache: Meta.js.target[target].cache,
map: Meta.js.target[target].map,
target: target
data: Meta.js.target
});
}
Meta.js.commitToFile(target);
Meta.js.commitToFile(target, function() {
if (typeof callback === 'function') {
callback();
}
});
break;
case 'error':
winston.error('[meta/js] Could not compile ' + target + ': ' + message.message);
@ -185,15 +186,15 @@ module.exports = function(Meta) {
}
};
Meta.js.commitToFile = function(target) {
Meta.js.commitToFile = function(target, callback) {
fs.writeFile(path.join(__dirname, '../../public/' + target), Meta.js.target[target].cache, function (err) {
if (err) {
winston.error('[meta/js] ' + err.message);
process.exit(0);
}
winston.verbose('[meta/js] ' + target + ' committed to disk.');
emitter.emit('meta:js.compiled');
callback();
});
};

@ -472,7 +472,7 @@ var async = require('async'),
return true;
}
return !(notifObj.mergeId === (mergeId + '|' + differentiator) && idx !== modifyIndex);
return !(notifObj.mergeId === (mergeId + (differentiator ? '|' + differentiator : '')) && idx !== modifyIndex);
});
});

@ -1,13 +1,13 @@
"use strict";
var async = require('async'),
var async = require('async');
db = require('../../database'),
groups = require('../../groups'),
categories = require('../../categories'),
privileges = require('../../privileges'),
plugins = require('../../plugins'),
Categories = {};
var db = require('../../database');
var groups = require('../../groups');
var categories = require('../../categories');
var privileges = require('../../privileges');
var plugins = require('../../plugins');
var Categories = {};
Categories.create = function(socket, data, callback) {
if (!data) {
@ -108,4 +108,8 @@ function copyPrivilegesToChildrenRecursive(category, privilegeGroups, callback)
});
}
Categories.copySettingsFrom = function(socket, data, callback) {
categories.copySettingsFrom(data.fromCid, data.toCid, callback);
};
module.exports = Categories;

@ -1,17 +1,16 @@
"use strict";
var async = require('async'),
winston = require('winston'),
nconf = require('nconf'),
db = require('../database'),
meta = require('../meta'),
user = require('../user'),
topics = require('../topics'),
batch = require('../batch'),
plugins = require('../plugins'),
emailer = require('../emailer'),
utils = require('../../public/src/utils');
var async = require('async');
var winston = require('winston');
var nconf = require('nconf');
var db = require('../database');
var meta = require('../meta');
var user = require('../user');
var topics = require('../topics');
var plugins = require('../plugins');
var emailer = require('../emailer');
var utils = require('../../public/src/utils');
(function(Digest) {
Digest.execute = function(interval) {
@ -100,7 +99,7 @@ var async = require('async'),
}
emailer.send('digest', userObj.uid, {
subject: '[' + meta.config.title + '] Digest for ' + now.getFullYear()+ '/' + (now.getMonth()+1) + '/' + now.getDate(),
subject: '[' + meta.config.title + '] [[email:digest.subject, ' + (now.getFullYear()+ '/' + (now.getMonth()+1) + '/' + now.getDate()) + ']]',
username: userObj.username,
userslug: userObj.userslug,
url: nconf.get('url'),

@ -105,7 +105,8 @@
<button type="button" class="btn btn-default btn-block <!-- IF category.parent.name -->hide<!-- ENDIF category.parent.name -->" data-action="setParent"><i class="fa fa-sitemap"></i> (None)</button>
</div>
</fieldset>
<hr/>
<button class="btn btn-info btn-block copy-settings"><i class="fa fa-files-o"></i> Copy Settings From</button>
<hr />
<button class="btn btn-danger btn-block purge"><i class="fa fa-eraser"></i> Purge Category</button>
</div>

@ -12,4 +12,14 @@
<!-- END categories -->
</select>
</div>
<div class="form-group">
<label for="cloneFromCid">(Optional) Clone Settings From Category</label>
<select class="form-control" name="cloneFromCid" id="cloneFromCid">
<option value=""></option>
<!-- BEGIN categories -->
<option value="{categories.cid}">{categories.name}</option>
<!-- END categories -->
</select>
</div>
</form>

@ -0,0 +1,10 @@
<form type="form">
<div class="form-group">
<label for="select-cid">Select Category</label>
<select class="form-control" name="select-cid" id="select-cid">
<!-- BEGIN categories -->
<option value="{categories.cid}">{categories.name}</option>
<!-- END categories -->
</select>
</div>
</form>

@ -85,7 +85,7 @@ function initializeNodeBB(callback) {
plugins.init(app, middleware, next);
},
function(next) {
async.parallel([
async.series([
async.apply(meta.templates.compile),
async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, 'nodebb.min.js'),
async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, 'acp.min.js'),

@ -36,14 +36,14 @@
' "host": "127.0.0.1",' + '\n' +
' "port": "6379",' + '\n' +
' "password": "",' + '\n' +
' "database": "redis"' + '\n' +
' "database": "1"' + '\n' +
'}\n'+
' or (mongo):\n' +
'"test_database": {' + '\n' +
' "host": "127.0.0.1",' + '\n' +
' "port": "27017",' + '\n' +
' "password": "",' + '\n' +
' "database": "mongo"' + '\n' +
' "database": "1"' + '\n' +
'}\n'+
' or (mongo) in a replicaset' + '\n' +
'"test_database": {' + '\n' +

Loading…
Cancel
Save