Merge pull request #1 from BenLubar/yamikuronue-coverage-fix

fixed the merge conflict by merging
v1.18.x
Yami 9 years ago
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,78 +237,79 @@ 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 threshold is defined, images loaded above this threshold will modify
the user's scroll position so they are not scrolled away from content
they were reading. Images loaded below this threshold will push down content.
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() {
return utils.isElementInViewport(this);
}),
scrollTop = $(window).scrollTop(),
adjusting = false,
adjustQueue = [],
adjustPosition = function() {
adjusting = true;
oldHeight = document.body.clientHeight;
// Display the image
$(this).attr('data-state', 'loaded');
newHeight = document.body.clientHeight;
var imageRect = this.getBoundingClientRect();
if (imageRect.top < Posts._threshold) {
scrollTop = scrollTop + (newHeight - oldHeight);
$(window).scrollTop(scrollTop);
}
if (Posts._imageLoaderTimeout) {
clearTimeout(Posts._imageLoaderTimeout);
}
if (adjustQueue.length) {
adjustQueue.pop()();
} else {
adjusting = false;
}
},
oldHeight, newHeight;
// For each image, reset the source and adjust scrollTop when loaded
visible.attr('data-state', 'loading');
visible.each(function(index, image) {
image = $(image);
image.on('load', function() {
if (!adjusting) {
adjustPosition.call(this);
} else {
adjustQueue.push(adjustPosition.bind(this));
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
they were reading. Images loaded below this threshold will push down content.
If no threshold is defined, loaded images will push down content, as per
default
*/
var images = components.get('post/content').find('img[data-state="unloaded"]'),
visible = images.filter(function() {
return utils.isElementInViewport(this);
}),
scrollTop = $(window).scrollTop(),
adjusting = false,
adjustQueue = [],
adjustPosition = function() {
adjusting = true;
oldHeight = document.body.clientHeight;
// Display the image
$(this).attr('data-state', 'loaded');
newHeight = document.body.clientHeight;
var imageRect = this.getBoundingClientRect();
if (imageRect.top < threshold) {
scrollTop = scrollTop + (newHeight - oldHeight);
$(window).scrollTop(scrollTop);
}
if (adjustQueue.length) {
adjustQueue.pop()();
} else {
adjusting = false;
}
},
oldHeight, newHeight;
// For each image, reset the source and adjust scrollTop when loaded
visible.attr('data-state', 'loading');
visible.each(function(index, image) {
image = $(image);
image.on('load', function() {
if (!adjusting) {
adjustPosition.call(this);
} else {
adjustQueue.push(adjustPosition.bind(this));
}
});
image.attr('src', image.attr('data-src'));
if (image.parent().attr('href') === 'about:blank') {
image.parent().attr('href', image.attr('data-src'));
}
image.removeAttr('data-src');
});
image.attr('src', image.attr('data-src'));
if (image.parent().attr('href')) {
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,20 +121,24 @@ 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) {
var anchorEl = components.get('post/anchor', index - 1);
var anchorRect = anchorEl.get(0).getBoundingClientRect();
threshold = anchorRect.top;
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') {

@ -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,20 +61,16 @@ var async = require('async'),
async.apply(plugins.clearRequireCache),
async.apply(plugins.reload),
async.apply(plugins.reloadRoutes),
async.apply(Meta.css.minify),
async.apply(Meta.js.minify, 'nodebb.min.js'),
async.apply(Meta.js.minify, 'acp.min.js'),
async.apply(Meta.sounds.init),
async.apply(Meta.templates.compile),
async.apply(auth.reloadRoutes),
function(next) {
async.parallel([
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),
function(next) {
Meta.config['cache-buster'] = utils.generateUUID();
templates.flush();
next();
}
], next);
Meta.config['cache-buster'] = utils.generateUUID();
templates.flush();
next();
}
], function(err) {
if (!err) {

@ -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();
}
});
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,16 +1,16 @@
"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) {
if (!data) {
return callback(new Error('[[error:invalid-data]]'));
}
@ -46,7 +46,7 @@ Categories.purge = function(socket, cid, callback) {
};
Categories.update = function(socket, data, callback) {
if(!data) {
if (!data) {
return callback(new Error('[[error:invalid-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