mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-02 20:16:04 +01:00
Merge branch 'master' into develop
This commit is contained in:
@@ -58,7 +58,7 @@
|
|||||||
"nodebb-plugin-emoji-one": "1.1.5",
|
"nodebb-plugin-emoji-one": "1.1.5",
|
||||||
"nodebb-plugin-markdown": "7.1.0",
|
"nodebb-plugin-markdown": "7.1.0",
|
||||||
"nodebb-plugin-mentions": "2.0.1",
|
"nodebb-plugin-mentions": "2.0.1",
|
||||||
"nodebb-plugin-soundpack-default": "0.1.6",
|
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||||
"nodebb-plugin-spam-be-gone": "0.4.10",
|
"nodebb-plugin-spam-be-gone": "0.4.10",
|
||||||
"nodebb-rewards-essentials": "0.0.9",
|
"nodebb-rewards-essentials": "0.0.9",
|
||||||
"nodebb-theme-lavender": "3.0.15",
|
"nodebb-theme-lavender": "3.0.15",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"title": "未読",
|
"title": "未読",
|
||||||
"no_unread_topics": "未読のスレッドがあります。",
|
"no_unread_topics": "未読のスレッドはありません。",
|
||||||
"load_more": "もっと見る",
|
"load_more": "もっと見る",
|
||||||
"mark_as_read": "既読にする",
|
"mark_as_read": "既読にする",
|
||||||
"selected": "選択済み",
|
"selected": "選択済み",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"post-cache": "Кэш записи",
|
"post-cache": "Кэш записи",
|
||||||
"posts-in-cache": "Записей в кэше",
|
"posts-in-cache": "Записей в кэше",
|
||||||
"average-post-size": "Average Post Size",
|
"average-post-size": "Средний размер записи",
|
||||||
"length-to-max": "Length / Max",
|
"length-to-max": "Длина / Максимальная",
|
||||||
"percent-full": "%1% Full",
|
"percent-full": "%1% Full",
|
||||||
"post-cache-size": "Post Cache Size",
|
"post-cache-size": "Размер записи в кэше",
|
||||||
"items-in-cache": "Items in Cache",
|
"items-in-cache": "Items in Cache",
|
||||||
"control-panel": "Control Panel",
|
"control-panel": "Панель управления",
|
||||||
"update-settings": "Update Cache Settings"
|
"update-settings": "Обновить настройки кэша"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#navigation {
|
#navigation {
|
||||||
#active-navigation {
|
#active-navigation {
|
||||||
width: 100%;
|
float: none;
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
/* global app, define, socket */
|
/* global app, define, socket */
|
||||||
|
|
||||||
define('admin/general/sounds', ['sounds', 'settings'], function (Sounds, Settings) {
|
define('admin/general/sounds', ['sounds', 'settings', 'admin/settings'], function (Sounds, Settings, AdminSettings) {
|
||||||
var SoundsAdmin = {};
|
var SoundsAdmin = {};
|
||||||
|
|
||||||
SoundsAdmin.init = function () {
|
SoundsAdmin.init = function () {
|
||||||
@@ -9,8 +9,8 @@ define('admin/general/sounds', ['sounds', 'settings'], function (Sounds, Setting
|
|||||||
$('.sounds').find('button[data-action="play"]').on('click', function (e) {
|
$('.sounds').find('button[data-action="play"]').on('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var fileName = $(this).parent().parent().find('select').val();
|
var soundName = $(this).parent().parent().find('select').val();
|
||||||
Sounds.playFile(fileName);
|
Sounds.playSound(soundName);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load Form Values
|
// Load Form Values
|
||||||
@@ -26,6 +26,8 @@ define('admin/general/sounds', ['sounds', 'settings'], function (Sounds, Setting
|
|||||||
app.alertSuccess('[[admin/general/sounds:saved]]');
|
app.alertSuccess('[[admin/general/sounds:saved]]');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AdminSettings.prepare();
|
||||||
};
|
};
|
||||||
|
|
||||||
return SoundsAdmin;
|
return SoundsAdmin;
|
||||||
|
|||||||
@@ -73,7 +73,9 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
|
|||||||
function handleImageChange() {
|
function handleImageChange() {
|
||||||
|
|
||||||
$('#changePictureBtn').on('click', function () {
|
$('#changePictureBtn').on('click', function () {
|
||||||
socket.emit('user.getProfilePictures', {uid: ajaxify.data.uid}, function (err, pictures) {
|
socket.emit('user.getProfilePictures', {
|
||||||
|
uid: ajaxify.data.uid
|
||||||
|
}, function (err, pictures) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
@@ -216,10 +218,13 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
|
|||||||
|
|
||||||
pictureCropper.show({
|
pictureCropper.show({
|
||||||
socketMethod: 'user.uploadCroppedPicture',
|
socketMethod: 'user.uploadCroppedPicture',
|
||||||
aspectRatio: '1 / 1',
|
aspectRatio: 1 / 1,
|
||||||
paramName: 'uid',
|
paramName: 'uid',
|
||||||
paramValue: ajaxify.data.theirid,
|
paramValue: ajaxify.data.theirid,
|
||||||
fileSize: ajaxify.data.maximumProfileImageSize,
|
fileSize: ajaxify.data.maximumProfileImageSize,
|
||||||
|
allowSkippingCrop: false,
|
||||||
|
restrictImageDimension: true,
|
||||||
|
imageDimension: ajaxify.data.profileImageDimension,
|
||||||
title: '[[user:upload_picture]]',
|
title: '[[user:upload_picture]]',
|
||||||
description: '[[user:upload_a_picture]]',
|
description: '[[user:upload_a_picture]]',
|
||||||
accept: '.png,.jpg,.bmp'
|
accept: '.png,.jpg,.bmp'
|
||||||
@@ -249,6 +254,9 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
|
|||||||
url: url,
|
url: url,
|
||||||
socketMethod: 'user.uploadCroppedPicture',
|
socketMethod: 'user.uploadCroppedPicture',
|
||||||
aspectRatio: '1 / 1',
|
aspectRatio: '1 / 1',
|
||||||
|
allowSkippingCrop: false,
|
||||||
|
restrictImageDimension: true,
|
||||||
|
imageDimension: ajaxify.data.profileImageDimension,
|
||||||
paramName: 'uid',
|
paramName: 'uid',
|
||||||
paramValue: ajaxify.data.theirid,
|
paramValue: ajaxify.data.theirid,
|
||||||
}, onUploadComplete);
|
}, onUploadComplete);
|
||||||
@@ -262,7 +270,9 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
|
|||||||
});
|
});
|
||||||
|
|
||||||
modal.find('[data-action="remove-uploaded"]').on('click', function () {
|
modal.find('[data-action="remove-uploaded"]').on('click', function () {
|
||||||
socket.emit('user.removeUploadedPicture', {uid: ajaxify.data.theirid}, function (err) {
|
socket.emit('user.removeUploadedPicture', {
|
||||||
|
uid: ajaxify.data.theirid
|
||||||
|
}, function (err) {
|
||||||
modal.modal('hide');
|
modal.modal('hide');
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ define('forum/account/header', [
|
|||||||
pictureCropper.show({
|
pictureCropper.show({
|
||||||
title: '[[user:upload_cover_picture]]',
|
title: '[[user:upload_cover_picture]]',
|
||||||
socketMethod: 'user.updateCover',
|
socketMethod: 'user.updateCover',
|
||||||
aspectRatio: '16 / 9',
|
aspectRatio: NaN,
|
||||||
|
allowSkippingCrop: true,
|
||||||
|
restrictImageDimension: false,
|
||||||
paramName: 'uid',
|
paramName: 'uid',
|
||||||
paramValue: ajaxify.data.theirid,
|
paramValue: ajaxify.data.theirid,
|
||||||
accept: '.png,.jpg,.bmp'
|
accept: '.png,.jpg,.bmp'
|
||||||
@@ -132,7 +134,11 @@ define('forum/account/header', [
|
|||||||
}, {});
|
}, {});
|
||||||
var until = parseInt(formData.length, 10) ? (Date.now() + formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1)) : 0;
|
var until = parseInt(formData.length, 10) ? (Date.now() + formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1)) : 0;
|
||||||
|
|
||||||
socket.emit('user.banUsers', { uids: [ajaxify.data.theirid], until: until, reason: formData.reason || '' }, function (err) {
|
socket.emit('user.banUsers', {
|
||||||
|
uids: [ajaxify.data.theirid],
|
||||||
|
until: until,
|
||||||
|
reason: formData.reason || ''
|
||||||
|
}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ define('forum/account/settings', ['forum/account/header', 'components', 'sounds'
|
|||||||
$('.account').find('button[data-action="play"]').on('click', function (e) {
|
$('.account').find('button[data-action="play"]').on('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var fileName = $(this).parent().parent().find('select').val();
|
var soundName = $(this).parent().parent().find('select').val();
|
||||||
sounds.playFile(fileName);
|
sounds.playSound(soundName);
|
||||||
});
|
});
|
||||||
|
|
||||||
toggleCustomRoute();
|
toggleCustomRoute();
|
||||||
@@ -89,7 +89,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'sounds'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sounds.reloadMapping();
|
sounds.loadMap();
|
||||||
|
|
||||||
if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) {
|
if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) {
|
||||||
app.alert({
|
app.alert({
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ define('forum/groups/details', [
|
|||||||
pictureCropper.show({
|
pictureCropper.show({
|
||||||
title: '[[groups:upload-group-cover]]',
|
title: '[[groups:upload-group-cover]]',
|
||||||
socketMethod: 'groups.cover.update',
|
socketMethod: 'groups.cover.update',
|
||||||
aspectRatio: '16 / 9',
|
aspectRatio: NaN,
|
||||||
|
allowSkippingCrop: true,
|
||||||
|
restrictImageDimension: false,
|
||||||
paramName: 'groupName',
|
paramName: 'groupName',
|
||||||
paramValue: groupName
|
paramValue: groupName
|
||||||
}, function (imageUrlOnServer) {
|
}, function (imageUrlOnServer) {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ define('chat', [
|
|||||||
|
|
||||||
if (!isSelf && (!modal.is(':visible') || !app.isFocused)) {
|
if (!isSelf && (!modal.is(':visible') || !app.isFocused)) {
|
||||||
app.alternatingTitle('[[modules:chat.user_has_messaged_you, ' + username + ']]');
|
app.alternatingTitle('[[modules:chat.user_has_messaged_you, ' + username + ']]');
|
||||||
sounds.play('chat-incoming');
|
sounds.play('chat-incoming', 'chat.incoming:' + data.message.mid);
|
||||||
|
|
||||||
taskbar.push('chat', modal.attr('UUID'), {
|
taskbar.push('chat', modal.attr('UUID'), {
|
||||||
title: data.roomName || username,
|
title: data.roomName || username,
|
||||||
@@ -89,7 +89,7 @@ define('chat', [
|
|||||||
module.toggleNew(modal.attr('UUID'), !isSelf, true);
|
module.toggleNew(modal.attr('UUID'), !isSelf, true);
|
||||||
if (!isSelf) {
|
if (!isSelf) {
|
||||||
app.alternatingTitle('[[modules:chat.user_has_messaged_you, ' + username + ']]');
|
app.alternatingTitle('[[modules:chat.user_has_messaged_you, ' + username + ']]');
|
||||||
sounds.play('chat-incoming');
|
sounds.play('chat-incoming', 'chat.incoming:' + data.message.mid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
/* globals define, socket, app, ajaxify, templates, Tinycon*/
|
/* globals define, socket, app, ajaxify, templates, Tinycon*/
|
||||||
|
|
||||||
define('notifications', ['sounds', 'translator', 'components'], function (sound, translator, components) {
|
define('notifications', ['sounds', 'translator', 'components'], function (sounds, translator, components) {
|
||||||
var Notifications = {};
|
var Notifications = {};
|
||||||
|
|
||||||
var unreadNotifs = {};
|
var unreadNotifs = {};
|
||||||
@@ -105,7 +105,7 @@ define('notifications', ['sounds', 'translator', 'components'], function (sound,
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!unreadNotifs[notifData.nid]) {
|
if (!unreadNotifs[notifData.nid]) {
|
||||||
sound.play('notification');
|
sounds.play('notification', notifData.nid);
|
||||||
unreadNotifs[notifData.nid] = true;
|
unreadNotifs[notifData.nid] = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,7 +44,30 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe
|
|||||||
var cropperTool = new cropper.default(img, {
|
var cropperTool = new cropper.default(img, {
|
||||||
aspectRatio: data.aspectRatio,
|
aspectRatio: data.aspectRatio,
|
||||||
viewMode: 1,
|
viewMode: 1,
|
||||||
|
cropmove: function () {
|
||||||
|
if (data.restrictImageDimension) {
|
||||||
|
if (cropperTool.cropBoxData.width > data.imageDimension) {
|
||||||
|
cropperTool.setCropBoxData({
|
||||||
|
width: data.imageDimension
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (cropperTool.cropBoxData.height > data.imageDimension) {
|
||||||
|
cropperTool.setCropBoxData({
|
||||||
|
height: data.imageDimension
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
ready: function () {
|
ready: function () {
|
||||||
|
if (data.restrictImageDimension) {
|
||||||
|
var origDimension = (img.width < img.height) ? img.width : img.height;
|
||||||
|
var dimension = (origDimension > data.imageDimension) ? data.imageDimension : origDimension;
|
||||||
|
cropperTool.setCropBoxData({
|
||||||
|
width: dimension,
|
||||||
|
height: dimension
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
cropperModal.find('.rotate').on('click', function () {
|
cropperModal.find('.rotate').on('click', function () {
|
||||||
var degrees = this.getAttribute("data-degrees");
|
var degrees = this.getAttribute("data-degrees");
|
||||||
cropperTool.rotate(degrees);
|
cropperTool.rotate(degrees);
|
||||||
@@ -132,6 +155,9 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe
|
|||||||
imageType: imageType,
|
imageType: imageType,
|
||||||
socketMethod: data.socketMethod,
|
socketMethod: data.socketMethod,
|
||||||
aspectRatio: data.aspectRatio,
|
aspectRatio: data.aspectRatio,
|
||||||
|
allowSkippingCrop: data.allowSkippingCrop,
|
||||||
|
restrictImageDimension: data.restrictImageDimension,
|
||||||
|
imageDimension: data.imageDimension,
|
||||||
paramName: data.paramName,
|
paramName: data.paramName,
|
||||||
paramValue: data.paramValue
|
paramValue: data.paramValue
|
||||||
}, callback);
|
}, callback);
|
||||||
|
|||||||
@@ -1,90 +1,94 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
/* global app, define, socket, config */
|
/* global app, define, socket, config */
|
||||||
|
|
||||||
define('sounds', ['buzz'], function (buzz) {
|
define('sounds', function () {
|
||||||
var Sounds = {};
|
var Sounds = {};
|
||||||
|
|
||||||
var loadedSounds = {};
|
var fileMap;
|
||||||
var eventSoundMapping;
|
var soundMap;
|
||||||
var files;
|
var cache = {};
|
||||||
|
|
||||||
socket.on('event:sounds.reloadMapping', function () {
|
Sounds.loadMap = function loadMap(callback) {
|
||||||
Sounds.reloadMapping();
|
socket.emit('modules.sounds.getUserSoundMap', function (err, map) {
|
||||||
});
|
|
||||||
|
|
||||||
Sounds.reloadMapping = function () {
|
|
||||||
socket.emit('modules.sounds.getMapping', function (err, mapping) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
eventSoundMapping = mapping;
|
soundMap = map;
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadData(callback) {
|
function loadData(callback) {
|
||||||
socket.emit('modules.sounds.getData', function (err, data) {
|
var outstanding = 2;
|
||||||
if (err) {
|
function after() {
|
||||||
return app.alertError('[sounds] Could not load sound mapping!');
|
outstanding -= 1;
|
||||||
}
|
if (outstanding === 0 && callback) {
|
||||||
eventSoundMapping = data.mapping;
|
|
||||||
files = data.files;
|
|
||||||
callback();
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileMap) {
|
||||||
|
outstanding -= 1;
|
||||||
|
} else {
|
||||||
|
$.getJSON(config.relative_path + '/assets/sounds/fileMap.json', function (map) {
|
||||||
|
fileMap = map;
|
||||||
|
after();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSoundLoaded(fileName) {
|
Sounds.loadMap(after);
|
||||||
return loadedSounds[fileName];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFile(fileName, callback) {
|
Sounds.playSound = function playSound(soundName) {
|
||||||
function createSound() {
|
if (!soundMap || !fileMap) {
|
||||||
if (files && files[fileName]) {
|
return loadData(after);
|
||||||
loadedSounds[fileName] = new buzz.sound(files[fileName]);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSoundLoaded(fileName)) {
|
function after() {
|
||||||
return callback();
|
if (!fileMap[soundName]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var audio = cache[soundName] = cache[soundName] || new Audio(config.relative_path + '/assets/sounds/' + fileMap[soundName]);
|
||||||
|
audio.pause();
|
||||||
|
audio.currentTime = 0;
|
||||||
|
audio.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!files || !files[fileName]) {
|
after();
|
||||||
return loadData(createSound);
|
|
||||||
}
|
|
||||||
createSound();
|
|
||||||
}
|
|
||||||
|
|
||||||
Sounds.play = function (name) {
|
|
||||||
function play() {
|
|
||||||
Sounds.playFile(eventSoundMapping[name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eventSoundMapping) {
|
|
||||||
return loadData(play);
|
|
||||||
}
|
|
||||||
|
|
||||||
play();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Sounds.playFile = function (fileName) {
|
Sounds.play = function play(type, id) {
|
||||||
if (!fileName) {
|
function after() {
|
||||||
|
if (!soundMap[type]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function play() {
|
if (id) {
|
||||||
if (loadedSounds[fileName]) {
|
var item = 'sounds.handled:' + id;
|
||||||
loadedSounds[fileName].play();
|
if (sessionStorage.getItem(item)) {
|
||||||
} else {
|
return;
|
||||||
app.alertError('[sounds] Not found: ' + fileName);
|
|
||||||
}
|
}
|
||||||
|
sessionStorage.setItem(item, true);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
sessionStorage.removeItem(item);
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSoundLoaded(fileName)) {
|
Sounds.playSound(soundMap[type]);
|
||||||
play();
|
|
||||||
} else {
|
|
||||||
loadFile(fileName, play);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!soundMap || !fileMap) {
|
||||||
|
return loadData(after);
|
||||||
|
}
|
||||||
|
|
||||||
|
after();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
socket.on('event:sounds.reloadMapping', function () {
|
||||||
|
Sounds.loadMap();
|
||||||
|
});
|
||||||
|
|
||||||
return Sounds;
|
return Sounds;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ editController.get = function (req, res, callback) {
|
|||||||
userData.maximumProfileImageSize = parseInt(meta.config.maximumProfileImageSize, 10);
|
userData.maximumProfileImageSize = parseInt(meta.config.maximumProfileImageSize, 10);
|
||||||
userData.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads) === 1;
|
userData.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads) === 1;
|
||||||
userData.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1;
|
userData.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1;
|
||||||
|
userData.profileImageDimension = parseInt(meta.config.profileImageDimension, 10) || 128;
|
||||||
|
|
||||||
userData.groups = userData.groups.filter(function (group) {
|
userData.groups = userData.groups.filter(function (group) {
|
||||||
return group && group.userTitleEnabled && !groups.isPrivilegeGroup(group.name) && group.name !== 'registered-users';
|
return group && group.userTitleEnabled && !groups.isPrivilegeGroup(group.name) && group.name !== 'registered-users';
|
||||||
@@ -36,7 +37,12 @@ editController.get = function (req, res, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
|
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
|
||||||
userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]);
|
userData.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||||
|
text: userData.username,
|
||||||
|
url: '/user/' + userData.userslug
|
||||||
|
}, {
|
||||||
|
text: '[[user:edit]]'
|
||||||
|
}]);
|
||||||
userData.editButtons = [];
|
userData.editButtons = [];
|
||||||
|
|
||||||
plugins.fireHook('filter:user.account.edit', userData, function (err, userData) {
|
plugins.fireHook('filter:user.account.edit', userData, function (err, userData) {
|
||||||
@@ -75,11 +81,15 @@ function renderRoute(name, req, res, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';
|
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';
|
||||||
userData.breadcrumbs = helpers.buildBreadcrumbs([
|
userData.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||||
{text: userData.username, url: '/user/' + userData.userslug},
|
text: userData.username,
|
||||||
{text: '[[user:edit]]', url: '/user/' + userData.userslug + '/edit'},
|
url: '/user/' + userData.userslug
|
||||||
{text: '[[user:' + name + ']]'}
|
}, {
|
||||||
]);
|
text: '[[user:edit]]',
|
||||||
|
url: '/user/' + userData.userslug + '/edit'
|
||||||
|
}, {
|
||||||
|
text: '[[user:' + name + ']]'
|
||||||
|
}]);
|
||||||
|
|
||||||
res.render('account/edit/' + name, userData);
|
res.render('account/edit/' + name, userData);
|
||||||
});
|
});
|
||||||
@@ -139,7 +149,10 @@ editController.uploadPicture = function (req, res, next) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json([{name: userPhoto.name, url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url}]);
|
res.json([{
|
||||||
|
name: userPhoto.name,
|
||||||
|
url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url
|
||||||
|
}]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -154,7 +167,9 @@ editController.uploadCoverPicture = function (req, res, next) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json([{ url: image.url }]);
|
res.json([{
|
||||||
|
url: image.url
|
||||||
|
}]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,8 @@ settingsController.get = function (req, res, callback) {
|
|||||||
homePageRoutes: function (next) {
|
homePageRoutes: function (next) {
|
||||||
getHomePageRoutes(next);
|
getHomePageRoutes(next);
|
||||||
},
|
},
|
||||||
sounds: function (next) {
|
|
||||||
meta.sounds.getFiles(next);
|
|
||||||
},
|
|
||||||
soundsMapping: function (next) {
|
soundsMapping: function (next) {
|
||||||
meta.sounds.getMapping(userData.uid, next);
|
meta.sounds.getUserSoundMap(userData.uid, next);
|
||||||
}
|
}
|
||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
@@ -50,16 +47,44 @@ settingsController.get = function (req, res, callback) {
|
|||||||
userData.languages = results.languages;
|
userData.languages = results.languages;
|
||||||
userData.homePageRoutes = results.homePageRoutes;
|
userData.homePageRoutes = results.homePageRoutes;
|
||||||
|
|
||||||
var soundSettings = {
|
var types = [
|
||||||
'notificationSound': 'notification',
|
'notification',
|
||||||
'incomingChatSound': 'chat-incoming',
|
'chat-incoming',
|
||||||
'outgoingChatSound': 'chat-outgoing'
|
'chat-outgoing',
|
||||||
|
];
|
||||||
|
var aliases = {
|
||||||
|
'notification': 'notificationSound',
|
||||||
|
'chat-incoming': 'incomingChatSound',
|
||||||
|
'chat-outgoing': 'outgoingChatSound',
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(soundSettings).forEach(function (setting) {
|
types.forEach(function (type) {
|
||||||
userData[setting] = Object.keys(results.sounds).map(function (name) {
|
var soundpacks = plugins.soundpacks.map(function (pack) {
|
||||||
return {name: name, selected: name === results.soundsMapping[soundSettings[setting]]};
|
var sounds = Object.keys(pack.sounds).map(function (soundName) {
|
||||||
|
var value = pack.name + ' | ' + soundName;
|
||||||
|
return {
|
||||||
|
name: soundName,
|
||||||
|
value: value,
|
||||||
|
selected: value === results.soundsMapping[type],
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: pack.name,
|
||||||
|
sounds: sounds,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
userData[type + '-sound'] = soundpacks;
|
||||||
|
// fallback
|
||||||
|
userData[aliases[type]] = soundpacks.concat.apply([], soundpacks.map(function (pack) {
|
||||||
|
return pack.sounds.map(function (sound) {
|
||||||
|
return {
|
||||||
|
name: sound.value,
|
||||||
|
selected: sound.selected,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
plugins.fireHook('filter:user.customSettings', { settings: results.settings, customSettings: [], uid: req.uid }, next);
|
plugins.fireHook('filter:user.customSettings', { settings: results.settings, customSettings: [], uid: req.uid }, next);
|
||||||
|
|||||||
@@ -1,24 +1,46 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var meta = require('../../meta');
|
var plugins = require('../../plugins');
|
||||||
|
var db = require('../../database');
|
||||||
|
|
||||||
var soundsController = {};
|
var soundsController = {};
|
||||||
|
|
||||||
soundsController.get = function (req, res, next) {
|
soundsController.get = function (req, res, next) {
|
||||||
meta.sounds.getFiles(function (err, sounds) {
|
db.getObject('settings:sounds', function (err, settings) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
sounds = Object.keys(sounds).map(function (name) {
|
settings = settings || {};
|
||||||
|
|
||||||
|
var types = [
|
||||||
|
'notification',
|
||||||
|
'chat-incoming',
|
||||||
|
'chat-outgoing',
|
||||||
|
];
|
||||||
|
var output = {};
|
||||||
|
|
||||||
|
types.forEach(function (type) {
|
||||||
|
var soundpacks = plugins.soundpacks.map(function (pack) {
|
||||||
|
var sounds = Object.keys(pack.sounds).map(function (soundName) {
|
||||||
|
var value = pack.name + ' | ' + soundName;
|
||||||
return {
|
return {
|
||||||
name: name
|
name: soundName,
|
||||||
|
value: value,
|
||||||
|
selected: value === settings[type],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
res.render('admin/general/sounds', {
|
return {
|
||||||
sounds: sounds
|
name: pack.name,
|
||||||
|
sounds: sounds,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
output[type + '-sound'] = soundpacks;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.render('admin/general/sounds', output);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ var path = require('path');
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
|
|
||||||
|
var meta = require('../../meta');
|
||||||
var file = require('../../file');
|
var file = require('../../file');
|
||||||
var image = require('../../image');
|
var image = require('../../image');
|
||||||
var plugins = require('../../plugins');
|
var plugins = require('../../plugins');
|
||||||
@@ -105,12 +107,7 @@ uploadsController.uploadSound = function (req, res, next) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var soundsPath = path.join(__dirname, '../../../build/public/sounds'),
|
meta.sounds.build(function (err) {
|
||||||
filePath = path.join(nconf.get('upload_path'), 'sounds', uploadedFile.name);
|
|
||||||
|
|
||||||
file.link(filePath, path.join(soundsPath, path.basename(filePath)));
|
|
||||||
|
|
||||||
fs.unlink(uploadedFile.path, function (err) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,26 +14,21 @@ module.exports = function (Messaging) {
|
|||||||
|
|
||||||
Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser
|
Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser
|
||||||
|
|
||||||
|
Messaging.notificationSendDelay = 1000 * 60;
|
||||||
|
|
||||||
Messaging.notifyUsersInRoom = function (fromUid, roomId, messageObj) {
|
Messaging.notifyUsersInRoom = function (fromUid, roomId, messageObj) {
|
||||||
async.parallel({
|
async.waterfall([
|
||||||
uids: function (next) {
|
function (next) {
|
||||||
Messaging.getUidsInRoom(roomId, 0, -1, next);
|
Messaging.getUidsInRoom(roomId, 0, -1, next);
|
||||||
},
|
},
|
||||||
roomData: function (next) {
|
function (uids, next) {
|
||||||
Messaging.getRoomData(roomId, next);
|
|
||||||
}
|
|
||||||
}, function (err, results) {
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
fromUid: fromUid,
|
fromUid: fromUid,
|
||||||
message: messageObj,
|
message: messageObj
|
||||||
roomName: results.roomData.roomName
|
|
||||||
};
|
};
|
||||||
results.uids.forEach(function (uid) {
|
|
||||||
|
uids.forEach(function (uid) {
|
||||||
data.self = parseInt(uid, 10) === parseInt(fromUid) ? 1 : 0;
|
data.self = parseInt(uid, 10) === parseInt(fromUid) ? 1 : 0;
|
||||||
Messaging.pushUnreadCount(uid);
|
Messaging.pushUnreadCount(uid);
|
||||||
sockets.in('uid_' + uid).emit('event:chats.receive', data);
|
sockets.in('uid_' + uid).emit('event:chats.receive', data);
|
||||||
@@ -51,27 +46,25 @@ module.exports = function (Messaging) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queueObj.timeout = setTimeout(function () {
|
queueObj.timeout = setTimeout(function () {
|
||||||
sendNotifications(fromUid, results.uids, roomId, queueObj.message, function (err) {
|
sendNotifications(fromUid, uids, roomId, queueObj.message);
|
||||||
if (!err) {
|
}, Messaging.notificationSendDelay);
|
||||||
delete Messaging.notifyQueue[fromUid + ':' + roomId];
|
next();
|
||||||
}
|
}
|
||||||
});
|
]);
|
||||||
}, 1000 * 60); // wait 60s before sending
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function sendNotifications(fromuid, uids, roomId, messageObj, callback) {
|
function sendNotifications(fromuid, uids, roomId, messageObj) {
|
||||||
user.isOnline(uids, function (err, isOnline) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
user.isOnline(uids, next);
|
||||||
}
|
},
|
||||||
|
function (isOnline, next) {
|
||||||
uids = uids.filter(function (uid, index) {
|
uids = uids.filter(function (uid, index) {
|
||||||
return !isOnline[index] && parseInt(fromuid, 10) !== parseInt(uid, 10);
|
return !isOnline[index] && parseInt(fromuid, 10) !== parseInt(uid, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!uids.length) {
|
if (!uids.length) {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifications.create({
|
notifications.create({
|
||||||
@@ -80,13 +73,16 @@ module.exports = function (Messaging) {
|
|||||||
nid: 'chat_' + fromuid + '_' + roomId,
|
nid: 'chat_' + fromuid + '_' + roomId,
|
||||||
from: fromuid,
|
from: fromuid,
|
||||||
path: '/chats/' + messageObj.roomId
|
path: '/chats/' + messageObj.roomId
|
||||||
}, function (err, notification) {
|
}, next);
|
||||||
if (!err && notification) {
|
}
|
||||||
notifications.push(notification, uids, callback);
|
], function (err, notification) {
|
||||||
|
if (!err) {
|
||||||
|
delete Messaging.notifyQueue[fromuid + ':' + roomId];
|
||||||
|
if (notification) {
|
||||||
|
notifications.push(notification, uids);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
sendNotificationEmails(uids, messageObj);
|
sendNotificationEmails(uids, messageObj);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +91,8 @@ module.exports = function (Messaging) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
userData: function (next) {
|
userData: function (next) {
|
||||||
user.getUsersFields(uids, ['uid', 'username', 'userslug'], next);
|
user.getUsersFields(uids, ['uid', 'username', 'userslug'], next);
|
||||||
@@ -102,15 +100,12 @@ module.exports = function (Messaging) {
|
|||||||
userSettings: function (next) {
|
userSettings: function (next) {
|
||||||
user.getMultipleUserSettings(uids, next);
|
user.getMultipleUserSettings(uids, next);
|
||||||
}
|
}
|
||||||
}, function (err, results) {
|
}, next);
|
||||||
if (err) {
|
},
|
||||||
return winston.error(err);
|
function (results, next) {
|
||||||
}
|
|
||||||
|
|
||||||
results.userData = results.userData.filter(function (userData, index) {
|
results.userData = results.userData.filter(function (userData, index) {
|
||||||
return userData && results.userSettings[index] && results.userSettings[index].sendChatNotifications;
|
return userData && results.userSettings[index] && results.userSettings[index].sendChatNotifications;
|
||||||
});
|
});
|
||||||
|
|
||||||
async.each(results.userData, function (userData, next) {
|
async.each(results.userData, function (userData, next) {
|
||||||
emailer.send('notif_chat', userData.uid, {
|
emailer.send('notif_chat', userData.uid, {
|
||||||
subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]',
|
subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]',
|
||||||
@@ -122,11 +117,12 @@ module.exports = function (Messaging) {
|
|||||||
username: userData.username,
|
username: userData.username,
|
||||||
userslug: userData.userslug
|
userslug: userData.userslug
|
||||||
}, next);
|
}, next);
|
||||||
}, function (err) {
|
}, next);
|
||||||
if (err) {
|
}
|
||||||
winston.error(err);
|
], function (err) {
|
||||||
|
if (err) {
|
||||||
|
return winston.error(err);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3,15 +3,18 @@
|
|||||||
var ip = require('ip');
|
var ip = require('ip');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
|
var pubsub = require('../pubsub');
|
||||||
|
|
||||||
var Blacklist = {
|
var Blacklist = {
|
||||||
_rules: []
|
_rules: []
|
||||||
};
|
};
|
||||||
|
|
||||||
Blacklist.load = function (callback) {
|
Blacklist.load = function (callback) {
|
||||||
|
callback = callback || function () {};
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
async.apply(db.get, 'ip-blacklist-rules'),
|
async.apply(Blacklist.get),
|
||||||
async.apply(Blacklist.validate)
|
async.apply(Blacklist.validate)
|
||||||
], function (err, rules) {
|
], function (err, rules) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -33,13 +36,18 @@ Blacklist.load = function (callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pubsub.on('blacklist:reload', Blacklist.load);
|
||||||
|
|
||||||
Blacklist.save = function (rules, callback) {
|
Blacklist.save = function (rules, callback) {
|
||||||
db.set('ip-blacklist-rules', rules, function (err) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.set('ip-blacklist-rules', rules, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
Blacklist.load(next);
|
||||||
|
pubsub.publish('blacklist:reload');
|
||||||
}
|
}
|
||||||
Blacklist.load(callback);
|
], callback);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Blacklist.get = function (callback) {
|
Blacklist.get = function (callback) {
|
||||||
@@ -48,14 +56,14 @@ Blacklist.get = function (callback) {
|
|||||||
|
|
||||||
Blacklist.test = function (clientIp, callback) {
|
Blacklist.test = function (clientIp, callback) {
|
||||||
if (
|
if (
|
||||||
Blacklist._rules.ipv4.indexOf(clientIp) === -1 // not explicitly specified in ipv4 list
|
Blacklist._rules.ipv4.indexOf(clientIp) === -1 &&// not explicitly specified in ipv4 list
|
||||||
&& Blacklist._rules.ipv6.indexOf(clientIp) === -1 // not explicitly specified in ipv6 list
|
Blacklist._rules.ipv6.indexOf(clientIp) === -1 &&// not explicitly specified in ipv6 list
|
||||||
&& !Blacklist._rules.cidr.some(function (subnet) {
|
!Blacklist._rules.cidr.some(function (subnet) {
|
||||||
return ip.cidrSubnet(subnet).contains(clientIp);
|
return ip.cidrSubnet(subnet).contains(clientIp);
|
||||||
}) // not in a blacklisted cidr range
|
}) // not in a blacklisted cidr range
|
||||||
) {
|
) {
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
callback();
|
setImmediate(callback);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -64,7 +72,7 @@ Blacklist.test = function (clientIp, callback) {
|
|||||||
err.code = 'blacklisted-ip';
|
err.code = 'blacklisted-ip';
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
callback(err);
|
setImmediate(callback, err);
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -78,9 +86,9 @@ Blacklist.validate = function (rules, callback) {
|
|||||||
var cidr = [];
|
var cidr = [];
|
||||||
var invalid = [];
|
var invalid = [];
|
||||||
|
|
||||||
var isCidrSubnet = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/,
|
var isCidrSubnet = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/;
|
||||||
inlineCommentMatch = /#.*$/,
|
var inlineCommentMatch = /#.*$/;
|
||||||
whitelist = ['127.0.0.1', '::1', '::ffff:0:127.0.0.1'];
|
var whitelist = ['127.0.0.1', '::1', '::ffff:0:127.0.0.1'];
|
||||||
|
|
||||||
// Filter out blank lines and lines starting with the hash character (comments)
|
// Filter out blank lines and lines starting with the hash character (comments)
|
||||||
// Also trim inputs and remove inline comments
|
// Also trim inputs and remove inline comments
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ var winston = require('winston');
|
|||||||
|
|
||||||
var buildStart;
|
var buildStart;
|
||||||
|
|
||||||
var valid = ['js', 'clientCSS', 'acpCSS', 'tpl', 'lang'];
|
var valid = ['js', 'clientCSS', 'acpCSS', 'tpl', 'lang', 'sound'];
|
||||||
|
|
||||||
exports.buildAll = function (callback) {
|
exports.buildAll = function (callback) {
|
||||||
exports.build(valid.join(','), callback);
|
exports.build(valid.join(','), callback);
|
||||||
@@ -46,7 +46,7 @@ exports.buildTargets = function (targets, callback) {
|
|||||||
var cacheBuster = require('./cacheBuster');
|
var cacheBuster = require('./cacheBuster');
|
||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
var numCpus = require('os').cpus().length;
|
var numCpus = require('os').cpus().length;
|
||||||
var strategy = (targets.length > 1 && numCpus > 1);
|
var parallel = targets.length > 1 && numCpus > 1;
|
||||||
|
|
||||||
buildStart = buildStart || Date.now();
|
buildStart = buildStart || Date.now();
|
||||||
|
|
||||||
@@ -59,19 +59,19 @@ exports.buildTargets = function (targets, callback) {
|
|||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (strategy) {
|
if (parallel) {
|
||||||
winston.verbose('[build] Utilising multiple cores/processes');
|
winston.verbose('[build] Utilising multiple cores/processes');
|
||||||
} else {
|
} else {
|
||||||
winston.verbose('[build] Utilising single-core');
|
winston.verbose('[build] Utilising single-core');
|
||||||
}
|
}
|
||||||
|
|
||||||
async[strategy ? 'parallel' : 'series']([
|
async[parallel ? 'parallel' : 'series']([
|
||||||
function (next) {
|
function (next) {
|
||||||
if (targets.indexOf('js') !== -1) {
|
if (targets.indexOf('js') !== -1) {
|
||||||
winston.info('[build] Building javascript');
|
winston.info('[build] Building javascript');
|
||||||
var startTime = Date.now();
|
var startTime = Date.now();
|
||||||
async.series([
|
async.series([
|
||||||
meta.js.linkModules,
|
meta.js.buildModules,
|
||||||
meta.js.linkStatics,
|
meta.js.linkStatics,
|
||||||
async.apply(meta.js.minify, 'nodebb.min.js'),
|
async.apply(meta.js.minify, 'nodebb.min.js'),
|
||||||
async.apply(meta.js.minify, 'acp.min.js')
|
async.apply(meta.js.minify, 'acp.min.js')
|
||||||
@@ -111,6 +111,12 @@ exports.buildTargets = function (targets, callback) {
|
|||||||
meta.languages.build(step.bind(this, startTime, target, next));
|
meta.languages.build(step.bind(this, startTime, target, next));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'sound':
|
||||||
|
winston.info('[build] Linking sound files');
|
||||||
|
startTime = Date.now();
|
||||||
|
meta.sounds.build(step.bind(this, startTime, target, next));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
winston.warn('[build] Unknown build target: \'' + target + '\'');
|
winston.warn('[build] Unknown build target: \'' + target + '\'');
|
||||||
setImmediate(next);
|
setImmediate(next);
|
||||||
|
|||||||
112
src/meta/js.js
112
src/meta/js.js
@@ -7,11 +7,14 @@ var async = require('async');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var mkdirp = require('mkdirp');
|
var mkdirp = require('mkdirp');
|
||||||
var rimraf = require('rimraf');
|
var rimraf = require('rimraf');
|
||||||
|
var uglifyjs = require('uglify-js');
|
||||||
|
|
||||||
var file = require('../file');
|
var file = require('../file');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
var utils = require('../../public/src/utils');
|
var utils = require('../../public/src/utils');
|
||||||
|
|
||||||
|
var minifierPath = path.join(__dirname, 'minifier.js');
|
||||||
|
|
||||||
module.exports = function (Meta) {
|
module.exports = function (Meta) {
|
||||||
|
|
||||||
Meta.js = {
|
Meta.js = {
|
||||||
@@ -79,7 +82,7 @@ module.exports = function (Meta) {
|
|||||||
'public/src/modules/flags.js'
|
'public/src/modules/flags.js'
|
||||||
],
|
],
|
||||||
|
|
||||||
// modules listed below are routed through express (/src/modules) so they can be defined anonymously
|
// modules listed below are built (/src/modules) so they can be defined anonymously
|
||||||
modules: {
|
modules: {
|
||||||
"Chart.js": './node_modules/chart.js/dist/Chart.min.js',
|
"Chart.js": './node_modules/chart.js/dist/Chart.min.js',
|
||||||
"mousetrap.js": './node_modules/mousetrap/mousetrap.min.js',
|
"mousetrap.js": './node_modules/mousetrap/mousetrap.min.js',
|
||||||
@@ -90,13 +93,48 @@ module.exports = function (Meta) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Meta.js.linkModules = function (callback) {
|
function minifyModules(modules, callback) {
|
||||||
rimraf(path.join(__dirname, '../../build/public/src/modules'), function (err) {
|
async.eachLimit(modules, 500, function (mod, next) {
|
||||||
|
var filePath = mod.filePath;
|
||||||
|
var destPath = mod.destPath;
|
||||||
|
var minified;
|
||||||
|
|
||||||
|
async.parallel([
|
||||||
|
function (cb) {
|
||||||
|
mkdirp(path.dirname(destPath), cb);
|
||||||
|
},
|
||||||
|
function (cb) {
|
||||||
|
fs.readFile(filePath, function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
async.eachLimit(Object.keys(Meta.js.scripts.modules), 1000, function (relPath, next) {
|
try {
|
||||||
var filePath = path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]);
|
minified = uglifyjs.minify(buffer.toString(), {
|
||||||
|
fromString: true,
|
||||||
|
compress: false,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function (err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFile(destPath, minified.code, next);
|
||||||
|
});
|
||||||
|
}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function linkModules(callback) {
|
||||||
|
var modules = Meta.js.scripts.modules;
|
||||||
|
|
||||||
|
async.eachLimit(Object.keys(modules), 1000, function (relPath, next) {
|
||||||
|
var filePath = path.join(__dirname, '../../', modules[relPath]);
|
||||||
var destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
|
var destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
|
||||||
|
|
||||||
mkdirp(path.dirname(destPath), function (err) {
|
mkdirp(path.dirname(destPath), function (err) {
|
||||||
@@ -107,7 +145,67 @@ module.exports = function (Meta) {
|
|||||||
file.link(filePath, destPath, next);
|
file.link(filePath, destPath, next);
|
||||||
});
|
});
|
||||||
}, callback);
|
}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
var moduleDirs = ['modules', 'admin', 'client'];
|
||||||
|
|
||||||
|
function getModuleList(callback) {
|
||||||
|
var modules = Object.keys(Meta.js.scripts.modules).map(function (relPath) {
|
||||||
|
return {
|
||||||
|
filePath: path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]),
|
||||||
|
destPath: path.join(__dirname, '../../build/public/src/modules', relPath),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var dirs = moduleDirs.map(function (dir) {
|
||||||
|
return path.join(__dirname, '../../public/src', dir);
|
||||||
|
});
|
||||||
|
|
||||||
|
async.each(dirs, function (dir, next) {
|
||||||
|
utils.walk(dir, function (err, files) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
modules = modules.concat(files.map(function (filePath) {
|
||||||
|
return {
|
||||||
|
filePath: filePath,
|
||||||
|
destPath: path.join(__dirname, '../../build/public/src', path.relative(path.dirname(dir), filePath)),
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
callback(err, modules);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearModules(callback) {
|
||||||
|
var builtPaths = moduleDirs.map(function (p) {
|
||||||
|
return '../../build/public/src/' + p;
|
||||||
|
});
|
||||||
|
async.each(builtPaths, function (builtPath, next) {
|
||||||
|
rimraf(path.join(__dirname, builtPath), next);
|
||||||
|
}, function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Meta.js.buildModules = function (callback) {
|
||||||
|
async.waterfall([
|
||||||
|
clearModules,
|
||||||
|
function (next) {
|
||||||
|
if (global.env === 'development') {
|
||||||
|
return linkModules(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
getModuleList(next);
|
||||||
|
},
|
||||||
|
function (modules, next) {
|
||||||
|
minifyModules(modules, next);
|
||||||
|
}
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Meta.js.linkStatics = function (callback) {
|
Meta.js.linkStatics = function (callback) {
|
||||||
@@ -134,7 +232,7 @@ module.exports = function (Meta) {
|
|||||||
winston.verbose('[meta/js] Minifying ' + target);
|
winston.verbose('[meta/js] Minifying ' + target);
|
||||||
|
|
||||||
var forkProcessParams = setupDebugging();
|
var forkProcessParams = setupDebugging();
|
||||||
var minifier = Meta.js.minifierProc = fork('minifier.js', [], forkProcessParams);
|
var minifier = Meta.js.minifierProc = fork(minifierPath, [], forkProcessParams);
|
||||||
|
|
||||||
Meta.js.target[target] = {};
|
Meta.js.target[target] = {};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
var uglifyjs = require('uglify-js');
|
var uglifyjs = require('uglify-js');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var file = require('./src/file');
|
var file = require('../file');
|
||||||
|
|
||||||
var Minifier = {
|
var Minifier = {
|
||||||
js: {}
|
js: {}
|
||||||
@@ -2,71 +2,101 @@
|
|||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var nconf = require('nconf');
|
|
||||||
var winston = require('winston');
|
|
||||||
var rimraf = require('rimraf');
|
var rimraf = require('rimraf');
|
||||||
var mkdirp = require('mkdirp');
|
var mkdirp = require('mkdirp');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var file = require('../file');
|
var file = require('../file');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
|
var user = require('../user');
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
|
|
||||||
module.exports = function (Meta) {
|
var soundsPath = path.join(__dirname, '../../build/public/sounds');
|
||||||
|
var uploadsPath = path.join(__dirname, '../../public/uploads/sounds');
|
||||||
|
|
||||||
|
module.exports = function (Meta) {
|
||||||
Meta.sounds = {};
|
Meta.sounds = {};
|
||||||
|
|
||||||
Meta.sounds.init = function (callback) {
|
Meta.sounds.addUploads = function addUploads(callback) {
|
||||||
if (nconf.get('isPrimary') === 'true') {
|
fs.readdir(uploadsPath, function (err, files) {
|
||||||
setupSounds(callback);
|
if (err) {
|
||||||
} else {
|
if (err.code !== 'ENOENT') {
|
||||||
if (typeof callback === 'function') {
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
files = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var uploadSounds = files.reduce(function (prev, fileName) {
|
||||||
|
var name = fileName.split('.');
|
||||||
|
if (!name.length || !name[0].length) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
name = name[0];
|
||||||
|
name = name[0].toUpperCase() + name.slice(1);
|
||||||
|
|
||||||
|
prev[name] = fileName;
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
plugins.soundpacks = plugins.soundpacks.filter(function (pack) {
|
||||||
|
return pack.name !== 'Uploads';
|
||||||
|
});
|
||||||
|
if (Object.keys(uploadSounds).length) {
|
||||||
|
plugins.soundpacks.push({
|
||||||
|
name: 'Uploads',
|
||||||
|
id: 'uploads',
|
||||||
|
dir: uploadsPath,
|
||||||
|
sounds: uploadSounds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Meta.sounds.getFiles = function (callback) {
|
Meta.sounds.build = function build(callback) {
|
||||||
async.waterfall([
|
Meta.sounds.addUploads(function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var map = plugins.soundpacks.map(function (pack) {
|
||||||
|
return Object.keys(pack.sounds).reduce(function (prev, soundName) {
|
||||||
|
var soundPath = pack.sounds[soundName];
|
||||||
|
prev[pack.name + ' | ' + soundName] = pack.id + '/' + soundPath;
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
});
|
||||||
|
map.unshift({});
|
||||||
|
map = Object.assign.apply(null, map);
|
||||||
|
|
||||||
|
async.series([
|
||||||
function (next) {
|
function (next) {
|
||||||
fs.readdir(path.join(__dirname, '../../build/public/sounds'), next);
|
rimraf(soundsPath, next);
|
||||||
},
|
},
|
||||||
function (sounds, next) {
|
function (next) {
|
||||||
fs.readdir(path.join(nconf.get('upload_path'), 'sounds'), function (err, uploaded) {
|
mkdirp(soundsPath, next);
|
||||||
if (err) {
|
},
|
||||||
if (err.code === 'ENOENT') {
|
function (cb) {
|
||||||
return next(null, sounds);
|
async.parallel([
|
||||||
}
|
function (next) {
|
||||||
return next(err);
|
fs.writeFile(path.join(soundsPath, 'fileMap.json'), JSON.stringify(map), next);
|
||||||
}
|
},
|
||||||
next(null, sounds.concat(uploaded));
|
function (next) {
|
||||||
});
|
async.each(plugins.soundpacks, function (pack, next) {
|
||||||
}
|
file.linkDirs(pack.dir, path.join(soundsPath, pack.id), next);
|
||||||
], function (err, files) {
|
}, next);
|
||||||
if (err) {
|
},
|
||||||
winston.error('Could not get local sound files:' + err.message);
|
], cb);
|
||||||
console.log(err.stack);
|
},
|
||||||
return callback(null, []);
|
], callback);
|
||||||
}
|
|
||||||
|
|
||||||
var localList = {};
|
|
||||||
|
|
||||||
// Filter out hidden files
|
|
||||||
files = files.filter(function (filename) {
|
|
||||||
return !filename.startsWith('.');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return proper paths
|
|
||||||
files.forEach(function (filename) {
|
|
||||||
localList[filename] = nconf.get('relative_path') + '/assets/sounds/' + filename;
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, localList);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Meta.sounds.getMapping = function (uid, callback) {
|
var keys = ['chat-incoming', 'chat-outgoing', 'notification'];
|
||||||
var user = require('../user');
|
|
||||||
|
Meta.sounds.getUserSoundMap = function getUserSoundMap(uid, callback) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
defaultMapping: function (next) {
|
defaultMapping: function (next) {
|
||||||
db.getObject('settings:sounds', next);
|
db.getObject('settings:sounds', next);
|
||||||
@@ -78,82 +108,25 @@ module.exports = function (Meta) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userSettings = results.userSettings;
|
var userSettings = results.userSettings;
|
||||||
|
userSettings = {
|
||||||
|
notification: userSettings.notificationSound,
|
||||||
|
'chat-incoming': userSettings.incomingChatSound,
|
||||||
|
'chat-outgoing': userSettings.outgoingChatSound,
|
||||||
|
};
|
||||||
var defaultMapping = results.defaultMapping || {};
|
var defaultMapping = results.defaultMapping || {};
|
||||||
var soundMapping = {};
|
var soundMapping = {};
|
||||||
soundMapping.notification = (userSettings.notificationSound || userSettings.notificationSound === '') ?
|
|
||||||
userSettings.notificationSound : defaultMapping.notification || '';
|
|
||||||
|
|
||||||
soundMapping['chat-incoming'] = (userSettings.incomingChatSound || userSettings.incomingChatSound === '') ?
|
keys.forEach(function (key) {
|
||||||
userSettings.incomingChatSound : defaultMapping['chat-incoming'] || '';
|
if (userSettings[key] || userSettings[key] === '') {
|
||||||
|
soundMapping[key] = userSettings[key] || null;
|
||||||
soundMapping['chat-outgoing'] = (userSettings.outgoingChatSound || userSettings.outgoingChatSound === '') ?
|
} else {
|
||||||
userSettings.outgoingChatSound : defaultMapping['chat-outgoing'] || '';
|
soundMapping[key] = defaultMapping[key] || null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
callback(null, soundMapping);
|
callback(null, soundMapping);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function setupSounds(callback) {
|
|
||||||
var soundsPath = path.join(__dirname, '../../build/public/sounds');
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
fs.readdir(path.join(nconf.get('upload_path'), 'sounds'), function (err, files) {
|
|
||||||
if (err) {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
return next(null, []);
|
|
||||||
}
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
next(null, files);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (uploaded, next) {
|
|
||||||
uploaded = uploaded.filter(function (filename) {
|
|
||||||
return !filename.startsWith('.');
|
|
||||||
}).map(function (filename) {
|
|
||||||
return path.join(nconf.get('upload_path'), 'sounds', filename);
|
|
||||||
});
|
|
||||||
|
|
||||||
plugins.fireHook('filter:sounds.get', uploaded, function (err, filePaths) {
|
|
||||||
if (err) {
|
|
||||||
winston.error('Could not initialise sound files:' + err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the sounds directory
|
|
||||||
async.series([
|
|
||||||
function (next) {
|
|
||||||
rimraf(soundsPath, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
mkdirp(soundsPath, next);
|
|
||||||
}
|
|
||||||
], function (err) {
|
|
||||||
if (err) {
|
|
||||||
winston.error('Could not initialise sound files:' + err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link paths
|
|
||||||
async.each(filePaths, function (filePath, next) {
|
|
||||||
file.link(filePath, path.join(soundsPath, path.basename(filePath)), next);
|
|
||||||
}, function (err) {
|
|
||||||
if (!err) {
|
|
||||||
winston.verbose('[sounds] Sounds OK');
|
|
||||||
} else {
|
|
||||||
winston.error('[sounds] Could not initialise sounds: ' + err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof next === 'function') {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], callback);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@@ -32,6 +32,7 @@ var middleware;
|
|||||||
Plugins.libraryPaths = [];
|
Plugins.libraryPaths = [];
|
||||||
Plugins.versionWarning = [];
|
Plugins.versionWarning = [];
|
||||||
Plugins.languageCodes = [];
|
Plugins.languageCodes = [];
|
||||||
|
Plugins.soundpacks = [];
|
||||||
|
|
||||||
Plugins.initialized = false;
|
Plugins.initialized = false;
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ module.exports = function (Plugins) {
|
|||||||
Plugins.lessFiles.length = 0;
|
Plugins.lessFiles.length = 0;
|
||||||
Plugins.clientScripts.length = 0;
|
Plugins.clientScripts.length = 0;
|
||||||
Plugins.acpScripts.length = 0;
|
Plugins.acpScripts.length = 0;
|
||||||
|
Plugins.soundpacks.length = 0;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
async.apply(Plugins.getPluginPaths),
|
async.apply(Plugins.getPluginPaths),
|
||||||
@@ -55,6 +56,7 @@ module.exports = function (Plugins) {
|
|||||||
async.apply(mapClientSideScripts, pluginData),
|
async.apply(mapClientSideScripts, pluginData),
|
||||||
async.apply(mapClientModules, pluginData),
|
async.apply(mapClientModules, pluginData),
|
||||||
async.apply(mapStaticDirectories, pluginData, pluginData.path),
|
async.apply(mapStaticDirectories, pluginData, pluginData.path),
|
||||||
|
async.apply(mapSoundpack, pluginData),
|
||||||
], next);
|
], next);
|
||||||
}, next);
|
}, next);
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,10 @@ module.exports = function (Plugins) {
|
|||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
mapClientModules(pluginData, next);
|
mapClientModules(pluginData, next);
|
||||||
}
|
},
|
||||||
|
function (next) {
|
||||||
|
mapSoundpack(pluginData, next);
|
||||||
|
},
|
||||||
], function (err) {
|
], function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
winston.verbose('[plugins] Could not load plugin : ' + pluginData.id);
|
winston.verbose('[plugins] Could not load plugin : ' + pluginData.id);
|
||||||
@@ -249,6 +254,35 @@ module.exports = function (Plugins) {
|
|||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapSoundpack(pluginData, callback) {
|
||||||
|
var soundpack = pluginData.soundpack;
|
||||||
|
if (!soundpack || !soundpack.dir || !soundpack.sounds) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
soundpack.name = soundpack.name || pluginData.name;
|
||||||
|
soundpack.id = pluginData.id;
|
||||||
|
soundpack.dir = path.join(pluginData.path, soundpack.dir);
|
||||||
|
async.each(Object.keys(soundpack.sounds), function (key, next) {
|
||||||
|
file.exists(path.join(soundpack.dir, soundpack.sounds[key]), function (exists) {
|
||||||
|
if (!exists) {
|
||||||
|
delete soundpack.sounds[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(soundpack.sounds).length) {
|
||||||
|
Plugins.soundpacks.push(soundpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function resolveModulePath(fullPath, relPath) {
|
function resolveModulePath(fullPath, relPath) {
|
||||||
/**
|
/**
|
||||||
* With npm@3, dependencies can become flattened, and appear at the root level.
|
* With npm@3, dependencies can become flattened, and appear at the root level.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var nconf = require('nconf'),
|
var nconf = require('nconf');
|
||||||
util = require('util'),
|
var util = require('util');
|
||||||
winston = require('winston'),
|
var winston = require('winston');
|
||||||
EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
var channelName;
|
var channelName;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
|
||||||
var winston = require('winston');
|
|
||||||
|
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,28 @@ Sockets.init = function (server) {
|
|||||||
|
|
||||||
io.on('connection', onConnection);
|
io.on('connection', onConnection);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restrict socket.io listener to cookie domain. If none is set, infer based on url.
|
||||||
|
* Production only so you don't get accidentally locked out.
|
||||||
|
* Can be overridden via config (socket.io:origins)
|
||||||
|
*/
|
||||||
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
|
var domain = nconf.get('cookieDomain');
|
||||||
|
var parsedUrl = url.parse(nconf.get('url'));
|
||||||
|
var override = nconf.get('socket.io:origins');
|
||||||
|
if (!domain) {
|
||||||
|
domain = parsedUrl.hostname; // cookies don't provide isolation by port: http://stackoverflow.com/a/16328399/122353
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!override) {
|
||||||
|
io.set('origins', parsedUrl.protocol + '//' + domain + ':*');
|
||||||
|
winston.info('[socket.io] Restricting access to origin: ' + parsedUrl.protocol + '//' + domain + ':*');
|
||||||
|
} else {
|
||||||
|
io.set('origins', override);
|
||||||
|
winston.info('[socket.io] Restricting access to origin: ' + override);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
io.listen(server, {
|
io.listen(server, {
|
||||||
transports: nconf.get('socket.io:transports')
|
transports: nconf.get('socket.io:transports')
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -341,20 +341,8 @@ SocketModules.chats.getMessages = function (socket, data, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Sounds */
|
/* Sounds */
|
||||||
SocketModules.sounds.getSounds = function (socket, data, callback) {
|
SocketModules.sounds.getUserSoundMap = function getUserSoundMap(socket, data, callback) {
|
||||||
// Read sounds from local directory
|
meta.sounds.getUserSoundMap(socket.uid, callback);
|
||||||
meta.sounds.getFiles(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
SocketModules.sounds.getMapping = function (socket, data, callback) {
|
|
||||||
meta.sounds.getMapping(socket.uid, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
SocketModules.sounds.getData = function (socket, data, callback) {
|
|
||||||
async.parallel({
|
|
||||||
mapping: async.apply(meta.sounds.getMapping, socket.uid),
|
|
||||||
files: async.apply(meta.sounds.getFiles)
|
|
||||||
}, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = SocketModules;
|
module.exports = SocketModules;
|
||||||
|
|||||||
@@ -30,25 +30,26 @@ SocketPosts.reply = function (socket, data, callback) {
|
|||||||
data.req = websockets.reqFromSocket(socket);
|
data.req = websockets.reqFromSocket(socket);
|
||||||
data.timestamp = Date.now();
|
data.timestamp = Date.now();
|
||||||
|
|
||||||
topics.reply(data, function (err, postData) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
topics.reply(data, next);
|
||||||
}
|
},
|
||||||
|
function (postData, next) {
|
||||||
var result = {
|
var result = {
|
||||||
posts: [postData],
|
posts: [postData],
|
||||||
'reputation:disabled': parseInt(meta.config['reputation:disabled'], 10) === 1,
|
'reputation:disabled': parseInt(meta.config['reputation:disabled'], 10) === 1,
|
||||||
'downvote:disabled': parseInt(meta.config['downvote:disabled'], 10) === 1,
|
'downvote:disabled': parseInt(meta.config['downvote:disabled'], 10) === 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
callback(null, postData);
|
next(null, postData);
|
||||||
|
|
||||||
websockets.in('uid_' + socket.uid).emit('event:new_post', result);
|
websockets.in('uid_' + socket.uid).emit('event:new_post', result);
|
||||||
|
|
||||||
user.updateOnlineUsers(socket.uid);
|
user.updateOnlineUsers(socket.uid);
|
||||||
|
|
||||||
socketHelpers.notifyNew(socket.uid, 'newPost', result);
|
socketHelpers.notifyNew(socket.uid, 'newPost', result);
|
||||||
});
|
}
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketPosts.getRawPost = function (socket, pid, callback) {
|
SocketPosts.getRawPost = function (socket, pid, callback) {
|
||||||
@@ -119,7 +120,7 @@ SocketPosts.getPidIndex = function (socket, data, callback) {
|
|||||||
|
|
||||||
SocketPosts.getReplies = function (socket, pid, callback) {
|
SocketPosts.getReplies = function (socket, pid, callback) {
|
||||||
if (!utils.isNumber(pid)) {
|
if (!utils.isNumber(pid)) {
|
||||||
return callback(new Error('[[error:invalid-data]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
var postPrivileges;
|
var postPrivileges;
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<div class="post-cache">
|
<div class="row post-cache">
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/cache:post-cache]]</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/cache:post-cache]]</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="database">
|
<div class="row database">
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<!-- IF mongo -->
|
<!-- IF mongo -->
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="events">
|
<div class="row events">
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/events:events]]</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/events:events]]</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="logs">
|
<div class="row logs">
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-file-text-o"></i> [[admin/advanced/logs:logs]]</div>
|
<div class="panel-heading"><i class="fa fa-file-text-o"></i> [[admin/advanced/logs:logs]]</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="skins" class="row skins">
|
<div id="skins" class="skins">
|
||||||
<div class="directory row" id="bootstrap_themes">
|
<div class="directory row" id="bootstrap_themes">
|
||||||
<i class="fa fa-refresh fa-spin"></i> [[admin/appearance/skins:loading]]
|
<i class="fa fa-refresh fa-spin"></i> [[admin/appearance/skins:loading]]
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="logger">
|
<div class="row logger">
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">[[admin/development/logger:logger-settings]]</div>
|
<div class="panel-heading">[[admin/development/logger:logger-settings]]</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="rewards">
|
<div id="rewards" class="row">
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">[[admin/extend/rewards:rewards]]</div>
|
<div class="panel-heading">[[admin/extend/rewards:rewards]]</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="navigation">
|
<div class="row" id="navigation">
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
<ul id="active-navigation" class="nav navbar-nav">
|
<ul id="active-navigation" class="nav navbar-nav">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="social settings" class="row">
|
<div class="social settings">
|
||||||
<form role="form">
|
<form role="form">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-2 col-xs-12 settings-header">[[admin/general/social:post-sharing]]</div>
|
<div class="col-sm-2 col-xs-12 settings-header">[[admin/general/social:post-sharing]]</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="sounds settings" class="row">
|
<div class="sounds settings row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<form role="form">
|
<form role="form">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -9,9 +9,15 @@
|
|||||||
<div class="form-group col-xs-9">
|
<div class="form-group col-xs-9">
|
||||||
<select class="form-control" id="notification" name="notification">
|
<select class="form-control" id="notification" name="notification">
|
||||||
<option value="">[[user:no-sound]]</option>
|
<option value="">[[user:no-sound]]</option>
|
||||||
<!-- BEGIN sounds -->
|
<!-- BEGIN notification-sound -->
|
||||||
<option value="{sounds.name}">{sounds.name}</option>
|
<optgroup label="{notification-sound.name}">
|
||||||
<!-- END sounds -->
|
<!-- BEGIN notification-sound.sounds -->
|
||||||
|
<option value="{notification-sound.sounds.value}" <!-- IF notification-sound.sounds.selected -->selected<!-- ENDIF notification-sound.sounds.selected -->>
|
||||||
|
{notification-sound.sounds.name}
|
||||||
|
</option>
|
||||||
|
<!-- END notification-sound.sounds -->
|
||||||
|
</optgroup>
|
||||||
|
<!-- END notification-sound -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group col-xs-3">
|
<div class="btn-group col-xs-3">
|
||||||
@@ -29,9 +35,15 @@
|
|||||||
<div class="form-group col-xs-9">
|
<div class="form-group col-xs-9">
|
||||||
<select class="form-control" id="chat-incoming" name="chat-incoming">
|
<select class="form-control" id="chat-incoming" name="chat-incoming">
|
||||||
<option value="">[[user:no-sound]]</option>
|
<option value="">[[user:no-sound]]</option>
|
||||||
<!-- BEGIN sounds -->
|
<!-- BEGIN chat-incoming-sound -->
|
||||||
<option value="{sounds.name}">{sounds.name}</option>
|
<optgroup label="{chat-incoming-sound.name}">
|
||||||
<!-- END sounds -->
|
<!-- BEGIN chat-incoming-sound.sounds -->
|
||||||
|
<option value="{chat-incoming-sound.sounds.value}" <!-- IF chat-incoming-sound.sounds.selected -->selected<!-- ENDIF chat-incoming-sound.sounds.selected -->>
|
||||||
|
{chat-incoming-sound.sounds.name}
|
||||||
|
</option>
|
||||||
|
<!-- END chat-incoming-sound.sounds -->
|
||||||
|
</optgroup>
|
||||||
|
<!-- END chat-incoming-sound -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group col-xs-3">
|
<div class="btn-group col-xs-3">
|
||||||
@@ -44,9 +56,15 @@
|
|||||||
<div class="form-group col-xs-9">
|
<div class="form-group col-xs-9">
|
||||||
<select class="form-control" id="chat-outgoing" name="chat-outgoing">
|
<select class="form-control" id="chat-outgoing" name="chat-outgoing">
|
||||||
<option value="">[[user:no-sound]]</option>
|
<option value="">[[user:no-sound]]</option>
|
||||||
<!-- BEGIN sounds -->
|
<!-- BEGIN chat-outgoing-sound -->
|
||||||
<option value="{sounds.name}">{sounds.name}</option>
|
<optgroup label="{chat-outgoing-sound.name}">
|
||||||
<!-- END sounds -->
|
<!-- BEGIN chat-outgoing-sound.sounds -->
|
||||||
|
<option value="{chat-outgoing-sound.sounds.value}" <!-- IF chat-outgoing-sound.sounds.selected -->selected<!-- ENDIF chat-outgoing-sound.sounds.selected -->>
|
||||||
|
{chat-outgoing-sound.sounds.name}
|
||||||
|
</option>
|
||||||
|
<!-- END chat-outgoing-sound.sounds -->
|
||||||
|
</optgroup>
|
||||||
|
<!-- END chat-outgoing-sound -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group col-xs-3">
|
<div class="btn-group col-xs-3">
|
||||||
@@ -56,7 +74,15 @@
|
|||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<input data-action="upload" data-title="Upload Sound" data-route="{config.relative_path}/api/admin/upload/sound" type="button" class="btn btn-primary" value="[[admin/general/sounds:upload-new-sound]]"></input>
|
<input
|
||||||
|
data-action="upload"
|
||||||
|
data-title="Upload Sound"
|
||||||
|
data-route="{config.relative_path}/api/admin/upload/sound"
|
||||||
|
data-accept="audio/*"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
value="[[admin/general/sounds:upload-new-sound]]"
|
||||||
|
></input>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="flags">
|
<div class="row ip-blacklist">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
[[admin/manage/ip-blacklist:lead]]
|
[[admin/manage/ip-blacklist:lead]]
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
<div class="registration panel panel-primary">
|
<div class="registration panel panel-primary">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
[[admin/manage/registration:queue]]
|
[[admin/manage/registration:queue]]
|
||||||
@@ -9,6 +11,7 @@
|
|||||||
<!-- ENDIF !users.length -->
|
<!-- ENDIF !users.length -->
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped users-list">
|
<table class="table table-striped users-list">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>[[admin/manage/registration:list.name]]</th>
|
<th>[[admin/manage/registration:list.name]]</th>
|
||||||
<th>[[admin/manage/registration:list.email]]</th>
|
<th>[[admin/manage/registration:list.email]]</th>
|
||||||
@@ -19,6 +22,8 @@
|
|||||||
<!-- END customHeaders -->
|
<!-- END customHeaders -->
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<!-- BEGIN users -->
|
<!-- BEGIN users -->
|
||||||
<tr data-username="{users.username}">
|
<tr data-username="{users.username}">
|
||||||
<td>
|
<td>
|
||||||
@@ -70,6 +75,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- END users -->
|
<!-- END users -->
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -85,11 +91,14 @@
|
|||||||
</p>
|
</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped invites-list">
|
<table class="table table-striped invites-list">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>[[admin/manage/registration:invitations.inviter-username]]</th>
|
<th>[[admin/manage/registration:invitations.inviter-username]]</th>
|
||||||
<th>[[admin/manage/registration:invitations.invitee-email]]</th>
|
<th>[[admin/manage/registration:invitations.invitee-email]]</th>
|
||||||
<th>[[admin/manage/registration:invitations.invitee-username]]</th>
|
<th>[[admin/manage/registration:invitations.invitee-username]]</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<!-- BEGIN invites -->
|
<!-- BEGIN invites -->
|
||||||
<!-- BEGIN invites.invitations -->
|
<!-- BEGIN invites.invitations -->
|
||||||
<tr data-invitation-mail="{invites.invitations.email}"
|
<tr data-invitation-mail="{invites.invitations.email}"
|
||||||
@@ -104,6 +113,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<!-- END invites.invitations -->
|
<!-- END invites.invitations -->
|
||||||
<!-- END invites -->
|
<!-- END invites -->
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<div class="tags row">
|
<div class="tags row">
|
||||||
|
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default tag-management">
|
<div class="panel panel-default tag-management">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
@@ -76,5 +75,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped users-table">
|
<table class="table table-striped users-table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input component="user/select/all" type="checkbox"/></th>
|
<th><input component="user/select/all" type="checkbox"/></th>
|
||||||
<th>[[admin/manage/users:users.uid]]</th>
|
<th>[[admin/manage/users:users.uid]]</th>
|
||||||
@@ -82,6 +83,8 @@
|
|||||||
<th>[[admin/manage/users:users.last-online]]</th>
|
<th>[[admin/manage/users:users.last-online]]</th>
|
||||||
<th>[[admin/manage/users:users.banned]]</th>
|
<th>[[admin/manage/users:users.banned]]</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<!-- BEGIN users -->
|
<!-- BEGIN users -->
|
||||||
<tr class="user-row">
|
<tr class="user-row">
|
||||||
<th><input component="user/select/single" data-uid="{users.uid}" type="checkbox"/></th>
|
<th><input component="user/select/single" data-uid="{users.uid}" type="checkbox"/></th>
|
||||||
@@ -101,6 +104,7 @@
|
|||||||
<td class="text-center"><i class="ban fa fa-gavel text-danger<!-- IF !users.banned --> hidden<!-- ENDIF !users.banned -->"></i></td>
|
<td class="text-center"><i class="ban fa fa-gavel text-danger<!-- IF !users.banned --> hidden<!-- ENDIF !users.banned -->"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- END users -->
|
<!-- END users -->
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<table class="table table-striped privilege-table">
|
<table class="table table-striped privilege-table">
|
||||||
|
<thead>
|
||||||
<tr class="privilege-table-header">
|
<tr class="privilege-table-header">
|
||||||
<th colspan="2"></th>
|
<th colspan="2"></th>
|
||||||
<th class="arrowed" colspan="3">
|
<th class="arrowed" colspan="3">
|
||||||
@@ -17,6 +18,8 @@
|
|||||||
<th class="text-center">{privileges.labels.users.name}</th>
|
<th class="text-center">{privileges.labels.users.name}</th>
|
||||||
<!-- END privileges.labels.users -->
|
<!-- END privileges.labels.users -->
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<!-- IF privileges.users.length -->
|
<!-- IF privileges.users.length -->
|
||||||
<!-- BEGIN privileges.users -->
|
<!-- BEGIN privileges.users -->
|
||||||
<tr data-uid="{privileges.users.uid}">
|
<tr data-uid="{privileges.users.uid}">
|
||||||
@@ -48,9 +51,11 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- ENDIF privileges.users.length -->
|
<!-- ENDIF privileges.users.length -->
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<table class="table table-striped privilege-table">
|
<table class="table table-striped privilege-table">
|
||||||
|
<thead>
|
||||||
<tr class="privilege-table-header">
|
<tr class="privilege-table-header">
|
||||||
<th colspan="2"></th>
|
<th colspan="2"></th>
|
||||||
<th class="arrowed" colspan="3">
|
<th class="arrowed" colspan="3">
|
||||||
@@ -69,6 +74,8 @@
|
|||||||
<th class="text-center">{privileges.labels.groups.name}</th>
|
<th class="text-center">{privileges.labels.groups.name}</th>
|
||||||
<!-- END privileges.labels.groups -->
|
<!-- END privileges.labels.groups -->
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<!-- BEGIN privileges.groups -->
|
<!-- BEGIN privileges.groups -->
|
||||||
<tr data-group-name="{privileges.groups.name}" data-private="<!-- IF privileges.groups.isPrivate -->1<!-- ELSE -->0<!-- ENDIF privileges.groups.isPrivate -->">
|
<tr data-group-name="{privileges.groups.name}" data-private="<!-- IF privileges.groups.isPrivate -->1<!-- ELSE -->0<!-- ENDIF privileges.groups.isPrivate -->">
|
||||||
<td>
|
<td>
|
||||||
@@ -96,6 +103,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="help-block">
|
<div class="help-block">
|
||||||
[[admin/manage/categories:privileges.inherit]]
|
[[admin/manage/categories:privileges.inherit]]
|
||||||
|
|||||||
@@ -28,11 +28,10 @@
|
|||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-primary reset"><i class="fa fa-refresh"></i></button>
|
<button class="btn btn-primary reset"><i class="fa fa-refresh"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||||
<button class="btn btn-primary upload-btn">[[user:upload_picture]]</button>
|
<button class="btn btn-primary upload-btn <!-- IF !allowSkippingCrop -->hidden<!-- ENDIF !allowSkippingCrop -->">[[user:upload_picture]]</button>
|
||||||
<button class="btn btn-primary crop-btn">[[user:upload_cropped_picture]]</button>
|
<button class="btn btn-primary crop-btn">[[user:upload_cropped_picture]]</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -103,9 +103,9 @@ function initializeNodeBB(callback) {
|
|||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
async.series([
|
async.series([
|
||||||
async.apply(meta.sounds.init),
|
meta.sounds.addUploads,
|
||||||
async.apply(languages.init),
|
languages.init,
|
||||||
async.apply(meta.blacklist.load)
|
meta.blacklist.load,
|
||||||
], next);
|
], next);
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
|
|||||||
79
test/blacklist.js
Normal file
79
test/blacklist.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
'use strict';
|
||||||
|
/*global require, after, before*/
|
||||||
|
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var db = require('./mocks/databasemock');
|
||||||
|
var groups = require('../src/groups');
|
||||||
|
var user = require('../src/user');
|
||||||
|
var blacklist = require('../src/meta/blacklist');
|
||||||
|
|
||||||
|
describe('blacklist', function () {
|
||||||
|
|
||||||
|
var adminUid;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
groups.resetCache();
|
||||||
|
user.create({username: 'admin'}, function (err, uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
adminUid = uid;
|
||||||
|
groups.join('administrators', adminUid, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var socketBlacklist = require('../src/socket.io/blacklist');
|
||||||
|
var rules = '1.1.1.1\n2.2.2.2\n::ffff:0:2.2.2.2\n127.0.0.1\n192.168.100.0/22';
|
||||||
|
|
||||||
|
it('should validate blacklist', function (done) {
|
||||||
|
socketBlacklist.validate({uid: adminUid}, {
|
||||||
|
rules: rules
|
||||||
|
}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if not admin', function (done) {
|
||||||
|
socketBlacklist.save({uid: 0}, rules, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-privileges]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save blacklist', function (done) {
|
||||||
|
socketBlacklist.save({uid: adminUid}, rules, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass ip test against blacklist async', function (done) {
|
||||||
|
blacklist.test('3.3.3.3', function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass ip test against blacklist sync', function (done) {
|
||||||
|
assert(!blacklist.test('3.3.3.3'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail ip test against blacklist async', function (done) {
|
||||||
|
blacklist.test('1.1.1.1', function (err) {
|
||||||
|
assert.equal(err.message, '[[error:blacklisted-ip]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail ip test against blacklist sync', function (done) {
|
||||||
|
assert(blacklist.test('1.1.1.1'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function (done) {
|
||||||
|
db.emptydb(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -121,6 +121,28 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should notify offline users of message', function (done) {
|
||||||
|
Messaging.notificationSendDelay = 100;
|
||||||
|
|
||||||
|
db.sortedSetAdd('users:online', Date.now() - 350000, herpUid, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketModules.chats.send({uid: fooUid}, {roomId: roomId, message: 'second chat message'}, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
setTimeout(function () {
|
||||||
|
User.notifications.get(herpUid, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data.unread[0]);
|
||||||
|
var notification = data.unread[0];
|
||||||
|
assert.equal(notification.bodyShort, '[[notifications:new_message_from, foo]]');
|
||||||
|
assert.equal(notification.nid, 'chat_' + fooUid + '_' + roomId);
|
||||||
|
assert.equal(notification.path, '/chats/' + roomId);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should get messages from room', function (done) {
|
it('should get messages from room', function (done) {
|
||||||
socketModules.chats.getMessages({uid: fooUid}, {
|
socketModules.chats.getMessages({uid: fooUid}, {
|
||||||
uid: fooUid,
|
uid: fooUid,
|
||||||
|
|||||||
138
test/posts.js
138
test/posts.js
@@ -540,6 +540,144 @@ describe('Post\'s', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('socket methods', function () {
|
||||||
|
|
||||||
|
var pid;
|
||||||
|
before(function (done) {
|
||||||
|
topics.reply({
|
||||||
|
uid: voterUid,
|
||||||
|
tid: topicData.tid,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
content: 'raw content'
|
||||||
|
}, function (err, postData) {
|
||||||
|
assert.ifError(err);
|
||||||
|
pid = postData.pid;
|
||||||
|
privileges.categories.rescind(['read'], cid, 'guests', done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var socketPosts = require('../src/socket.io/posts');
|
||||||
|
it('should error with invalid data', function (done) {
|
||||||
|
socketPosts.reply({uid: 0}, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error with invalid tid', function (done) {
|
||||||
|
socketPosts.reply({uid: 0}, {tid: 0, content: 'derp'}, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to get raw post because of privilege', function (done) {
|
||||||
|
socketPosts.getRawPost({uid: 0}, pid, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-privileges]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to get raw post because post is deleted', function (done) {
|
||||||
|
posts.setPostField(pid, 'deleted', 1, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketPosts.getRawPost({uid: voterUid}, pid, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-post]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get raw post content', function (done) {
|
||||||
|
posts.setPostField(pid, 'deleted', 0, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketPosts.getRawPost({uid: voterUid}, pid, function (err, postContent) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(postContent, 'raw content');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get post', function (done) {
|
||||||
|
socketPosts.getPost({uid: voterUid}, pid, function (err, postData) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(postData);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shold error with invalid data', function (done) {
|
||||||
|
socketPosts.loadMoreBookmarks({uid: voterUid}, {uid: voterUid, after: null}, function (err, postData) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load more bookmarks', function (done) {
|
||||||
|
socketPosts.loadMoreBookmarks({uid: voterUid}, {uid: voterUid, after: 0}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load more user posts', function (done) {
|
||||||
|
socketPosts.loadMoreUserPosts({uid: voterUid}, {uid: voterUid, after: 0}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load more best posts', function (done) {
|
||||||
|
socketPosts.loadMoreBestPosts({uid: voterUid}, {uid: voterUid, after: 0}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load more up voted posts', function (done) {
|
||||||
|
socketPosts.loadMoreUpVotedPosts({uid: voterUid}, {uid: voterUid, after: 0}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load more down voted posts', function (done) {
|
||||||
|
socketPosts.loadMoreDownVotedPosts({uid: voterUid}, {uid: voterUid, after: 0}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get post category', function (done) {
|
||||||
|
socketPosts.getCategory({uid: voterUid}, pid, function (err, postCid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(cid, postCid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error with invalid data', function (done) {
|
||||||
|
socketPosts.getPidIndex({uid: voterUid}, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get pid index', function (done) {
|
||||||
|
socketPosts.getPidIndex({uid: voterUid}, {pid: pid, tid: topicData.tid, topicPostSort: 'oldest-to-newest'}, function (err, index) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(index, 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
after(function (done) {
|
after(function (done) {
|
||||||
db.emptydb(done);
|
db.emptydb(done);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -130,6 +130,13 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should error if pid is not a number', function (done) {
|
||||||
|
socketPosts.getReplies({uid: 0}, 'abc', function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should fail to create new reply with invalid user id', function (done) {
|
it('should fail to create new reply with invalid user id', function (done) {
|
||||||
topics.reply({uid: null, content: 'test post', tid: newTopic.tid}, function (err) {
|
topics.reply({uid: null, content: 'test post', tid: newTopic.tid}, function (err) {
|
||||||
assert.equal(err.message, '[[error:no-privileges]]');
|
assert.equal(err.message, '[[error:no-privileges]]');
|
||||||
|
|||||||
Reference in New Issue
Block a user