mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 11:35:55 +01:00
Merge branch 'master' into search_hooks
This commit is contained in:
@@ -39,9 +39,10 @@
|
||||
"validator": "~3.2.1",
|
||||
"nodebb-plugin-mentions": "~0.4",
|
||||
"nodebb-plugin-markdown": "~0.3",
|
||||
"nodebb-theme-vanilla": "~0.0.13",
|
||||
"nodebb-theme-cerulean": "~0.0.12",
|
||||
"nodebb-theme-lavender": "~0.0",
|
||||
"nodebb-widget-essentials": "~0.0",
|
||||
"nodebb-theme-vanilla": "~0.0.14",
|
||||
"nodebb-theme-cerulean": "~0.0.13",
|
||||
"nodebb-theme-lavender": "~0.0.21",
|
||||
"cron": "~1.0.1",
|
||||
"semver": "~2.2.1",
|
||||
"string": "~1.7.0",
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"new_topic_button": "New Topic",
|
||||
"no_topics": "<strong>There are no topics in this category.</strong><br />Why don't you try posting one?",
|
||||
"sidebar.recent_replies": "Recent Replies",
|
||||
"sidebar.active_participants": "Active Participants",
|
||||
"sidebar.moderators": "Moderators",
|
||||
"posts": "posts",
|
||||
"views": "views",
|
||||
"posted": "posted",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"user.edit": "Editing \"%1\"",
|
||||
"user.following": "People %1 Follows",
|
||||
"user.followers": "People who Follow %1",
|
||||
"user.posts": "Posts made by %1",
|
||||
"user.favourites": "%1's Favourite Posts",
|
||||
"user.settings": "User Settings"
|
||||
}
|
||||
@@ -93,7 +93,7 @@ var ajaxify = {};
|
||||
|
||||
translator.load(tpl_url);
|
||||
|
||||
jQuery('#footer, #content').removeClass('hide').addClass('ajaxifying');
|
||||
$('#footer, #content').removeClass('hide').addClass('ajaxifying');
|
||||
|
||||
templates.flush();
|
||||
templates.load_template(function () {
|
||||
@@ -110,11 +110,31 @@ var ajaxify = {};
|
||||
|
||||
app.processPage();
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).removeClass('ajaxifying');
|
||||
ajaxify.initialLoad = false;
|
||||
var widgetLocations = [];
|
||||
|
||||
app.refreshTitle(url);
|
||||
$(window).trigger('action:ajaxify.end', { url: url });
|
||||
require(['vendor/async'], function(async) {
|
||||
$('#content [widget-area]').each(function() {
|
||||
widgetLocations.push(this.getAttribute('widget-area'));
|
||||
});
|
||||
|
||||
async.each(widgetLocations, function(location, next) {
|
||||
var area = $('#content [widget-area="' + location + '"]');
|
||||
|
||||
socket.emit('widgets.render', {template: tpl_url + '.tpl', url: url, location: location}, function(err, renderedWidgets) {
|
||||
area.html(templates.prepare(area.html()).parse({
|
||||
widgets: renderedWidgets
|
||||
})).removeClass('hidden');
|
||||
|
||||
next(err);
|
||||
});
|
||||
}, function(err) {
|
||||
$('#content, #footer').stop(true, true).removeClass('ajaxifying');
|
||||
ajaxify.initialLoad = false;
|
||||
|
||||
app.refreshTitle(url);
|
||||
$(window).trigger('action:ajaxify.end', { url: url });
|
||||
});
|
||||
});
|
||||
}, url);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -15,6 +15,9 @@ var socket,
|
||||
url: RELATIVE_PATH + '/api/config',
|
||||
success: function (data) {
|
||||
config = data;
|
||||
|
||||
exposeConfigToTemplates();
|
||||
|
||||
if(socket) {
|
||||
socket.disconnect();
|
||||
setTimeout(function() {
|
||||
@@ -591,13 +594,17 @@ var socket,
|
||||
});
|
||||
|
||||
createHeaderTooltips();
|
||||
|
||||
templates.setGlobal('relative_path', RELATIVE_PATH);
|
||||
templates.setGlobal('usePagination', config.usePagination);
|
||||
templates.setGlobal('topicsPerPage', config.topicsPerPage);
|
||||
templates.setGlobal('postsPerPage', config.postsPerPage);
|
||||
});
|
||||
|
||||
function exposeConfigToTemplates() {
|
||||
$(document).ready(function() {
|
||||
templates.setGlobal('relative_path', RELATIVE_PATH);
|
||||
for(var key in config) {
|
||||
templates.setGlobal('config.' + key, config[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createHeaderTooltips() {
|
||||
$('#header-menu li i[title]').each(function() {
|
||||
$(this).parents('a').tooltip({
|
||||
|
||||
@@ -102,6 +102,8 @@ define(['forum/admin/settings'], function(Settings) {
|
||||
tabIndent.config.tab = ' ';
|
||||
tabIndent.render(customCSSEl);
|
||||
|
||||
Themes.prepareWidgets();
|
||||
|
||||
Settings.prepare();
|
||||
}
|
||||
|
||||
@@ -132,5 +134,155 @@ define(['forum/admin/settings'], function(Settings) {
|
||||
themeContainer.appendChild(themeFrag);
|
||||
}
|
||||
|
||||
Themes.prepareWidgets = function() {
|
||||
$('#widgets .available-widgets .panel').draggable({
|
||||
helper: function(e) {
|
||||
return $(e.target).parents('.panel').clone().addClass('block').width($(e.target.parentNode).width());
|
||||
},
|
||||
connectToSortable: ".widget-area"
|
||||
});
|
||||
|
||||
$('#widgets .available-containers .containers > [data-container-html]').draggable({
|
||||
helper: function(e) {
|
||||
var target = $(e.target);
|
||||
target = target.attr('data-container-html') ? target : target.parents('[data-container-html]');
|
||||
|
||||
return target.clone().addClass('block').width(target.width()).css('opacity', '0.5');
|
||||
}
|
||||
});
|
||||
|
||||
function appendToggle(el) {
|
||||
if (!el.hasClass('block')) {
|
||||
el.addClass('block')
|
||||
.droppable({
|
||||
drop: function(event, ui) {
|
||||
var el = $(this);
|
||||
|
||||
el.find('.panel-body .container-html').val(ui.draggable.attr('data-container-html'));
|
||||
el.find('.panel-body').removeClass('hidden');
|
||||
},
|
||||
hoverClass: "panel-info"
|
||||
})
|
||||
.children('.panel-heading')
|
||||
.append('<div class="pull-right pointer"><span class="delete-widget"><i class="fa fa-times-circle"></i></span> <span class="toggle-widget"><i class="fa fa-chevron-down"></i></span></div>')
|
||||
.children('small').html('');
|
||||
}
|
||||
}
|
||||
|
||||
$('#widgets .widget-area').sortable({
|
||||
update: function (event, ui) {
|
||||
appendToggle(ui.item);
|
||||
},
|
||||
connectWith: "div"
|
||||
}).on('click', '.toggle-widget', function() {
|
||||
$(this).parents('.panel').children('.panel-body').toggleClass('hidden');
|
||||
}).on('click', '.delete-widget', function() {
|
||||
var panel = $(this).parents('.panel');
|
||||
|
||||
bootbox.confirm('Are you sure you wish to delete this widget?', function(confirm) {
|
||||
if (confirm) {
|
||||
panel.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#widgets .btn[data-template]').on('click', function() {
|
||||
var btn = $(this),
|
||||
template = btn.attr('data-template'),
|
||||
location = btn.attr('data-location'),
|
||||
area = btn.parents('.area').children('.widget-area'),
|
||||
widgets = [];
|
||||
|
||||
area.find('.panel[data-widget]').each(function() {
|
||||
var widgetData = {},
|
||||
data = $(this).find('form').serializeArray();
|
||||
|
||||
for (var d in data) {
|
||||
if (data.hasOwnProperty(d)) {
|
||||
if (data[d].name) {
|
||||
widgetData[data[d].name] = data[d].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
widgets.push({
|
||||
widget: this.getAttribute('data-widget'),
|
||||
data: widgetData
|
||||
});
|
||||
});
|
||||
|
||||
socket.emit('admin.widgets.set', {
|
||||
template: template,
|
||||
location: location,
|
||||
widgets: widgets
|
||||
}, function(err) {
|
||||
app.alert({
|
||||
alert_id: 'admin:widgets',
|
||||
type: err ? 'danger' : 'success',
|
||||
title: err ? 'Error' : 'Widgets Updated',
|
||||
message: err ? err : 'Successfully updated widgets',
|
||||
timeout: 2500
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function populateWidget(widget, data) {
|
||||
widget.find('input, textarea').each(function() {
|
||||
var input = $(this),
|
||||
value = data[input.attr('name')];
|
||||
|
||||
if (this.type === 'checkbox') {
|
||||
input.attr('checked', !!value);
|
||||
} else {
|
||||
input.val(value);
|
||||
}
|
||||
});
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
$.get(RELATIVE_PATH + '/api/admin/themes', function(data) {
|
||||
var areas = data.areas;
|
||||
|
||||
for (var a in areas) {
|
||||
if (areas.hasOwnProperty(a)) {
|
||||
var area = areas[a],
|
||||
widgetArea = $('#widgets .area [data-template="' + area.template + '"][data-location="' + area.location + '"]').parents('.area').find('.widget-area');
|
||||
|
||||
for (var i in area.data) {
|
||||
if (area.data.hasOwnProperty(i)) {
|
||||
var data = area.data[i],
|
||||
widgetEl = $('.available-widgets [data-widget="' + data.widget + '"]').clone();
|
||||
|
||||
widgetArea.append(populateWidget(widgetEl, data.data));
|
||||
appendToggle(widgetEl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('.color-selector').on('click', '.btn', function() {
|
||||
var btn = $(this),
|
||||
selector = btn.parents('.color-selector'),
|
||||
container = selector.parents('[data-container-html]'),
|
||||
classList = [];
|
||||
|
||||
selector.children().each(function() {
|
||||
classList.push($(this).attr('data-class'));
|
||||
});
|
||||
|
||||
container
|
||||
.removeClass(classList.join(' '))
|
||||
.addClass(btn.attr('data-class'));
|
||||
|
||||
container.attr('data-container-html', container.attr('data-container-html')
|
||||
.replace(/class="[a-zA-Z0-9-\s]+"/, 'class="' + container[0].className.replace(' pointer ui-draggable', '') + '"')
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return Themes;
|
||||
});
|
||||
@@ -23,14 +23,15 @@ define(function() {
|
||||
elements.each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var uid = getUID(banBtn);
|
||||
if (isUserAdmin(banBtn) || uid === yourid)
|
||||
if (isUserAdmin(banBtn) || uid === yourid) {
|
||||
banBtn.addClass('disabled');
|
||||
else if (isUserBanned(banBtn))
|
||||
} else if (isUserBanned(banBtn)) {
|
||||
banBtn.addClass('btn-warning');
|
||||
else if (!isUserAdmin(banBtn))
|
||||
} else if (!isUserAdmin(banBtn)) {
|
||||
banBtn.removeClass('disabled');
|
||||
else
|
||||
} else {
|
||||
banBtn.removeClass('btn-warning');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,87 +44,78 @@ define(function() {
|
||||
if (uid === yourid) {
|
||||
adminBtn.addClass('disabled');
|
||||
}
|
||||
}
|
||||
else if (isUserBanned(adminBtn))
|
||||
} else if (isUserBanned(adminBtn)) {
|
||||
adminBtn.addClass('disabled');
|
||||
else if (!isUserBanned(adminBtn))
|
||||
} else if (!isUserBanned(adminBtn)) {
|
||||
adminBtn.removeClass('disabled');
|
||||
else
|
||||
} else {
|
||||
adminBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initUsers() {
|
||||
updateUserBanButtons(jQuery('.ban-btn'));
|
||||
updateUserAdminButtons(jQuery('.admin-btn'));
|
||||
|
||||
$('#users-container').on('click', '.ban-btn', function() {
|
||||
var banBtn = $(this);
|
||||
var isAdmin = isUserAdmin(banBtn);
|
||||
var isBanned = isUserBanned(banBtn);
|
||||
var parent = banBtn.parents('.users-box');
|
||||
var uid = getUID(banBtn);
|
||||
|
||||
if (!isAdmin) {
|
||||
if (isBanned) {
|
||||
socket.emit('admin.user.unbanUser', uid);
|
||||
banBtn.removeClass('btn-warning');
|
||||
parent.attr('data-banned', 0);
|
||||
updateUserAdminButtons(jQuery('.admin-btn'));
|
||||
} else {
|
||||
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
updateUserAdminButtons(jQuery('.admin-btn'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#users-container').on('click', '.admin-btn', function() {
|
||||
var adminBtn = $(this);
|
||||
var isAdmin = isUserAdmin(adminBtn);
|
||||
var parent = adminBtn.parents('.users-box');
|
||||
var isBanned = isUserBanned(adminBtn);
|
||||
var uid = getUID(adminBtn);
|
||||
|
||||
if(uid === yourid){
|
||||
app.alert({
|
||||
title: 'Error',
|
||||
message: 'You can\'t remove yourself as Administrator!',
|
||||
type: 'danger',
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
else if (!isAdmin) {
|
||||
socket.emit('admin.user.makeAdmin', uid);
|
||||
adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
|
||||
parent.attr('data-admin', 1);
|
||||
updateUserBanButtons(jQuery('.ban-btn'));
|
||||
|
||||
} else if(uid !== yourid) {
|
||||
bootbox.confirm('Do you really want to remove this user as admin "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('admin.user.removeAdmin', uid);
|
||||
adminBtn.attr('value', 'Make Admin').html('Make Admin');
|
||||
parent.attr('data-admin', 0);
|
||||
updateUserBanButtons(jQuery('.ban-btn'));
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function updateButtons() {
|
||||
updateUserBanButtons($('.ban-btn'));
|
||||
updateUserAdminButtons($('.admin-btn'));
|
||||
}
|
||||
|
||||
$('#users-container').on('click', '.ban-btn', function() {
|
||||
var banBtn = $(this);
|
||||
var parent = banBtn.parents('.users-box');
|
||||
var uid = getUID(banBtn);
|
||||
|
||||
if (!isUserAdmin(banBtn)) {
|
||||
if (isUserBanned(banBtn)) {
|
||||
socket.emit('admin.user.unbanUser', uid);
|
||||
banBtn.removeClass('btn-warning');
|
||||
parent.attr('data-banned', 0);
|
||||
updateUserAdminButtons($('.admin-btn'));
|
||||
} else {
|
||||
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
updateUserAdminButtons($('.admin-btn'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#users-container').on('click', '.admin-btn', function() {
|
||||
var adminBtn = $(this);
|
||||
var parent = adminBtn.parents('.users-box');
|
||||
var uid = getUID(adminBtn);
|
||||
|
||||
if(uid === yourid) {
|
||||
app.alert({
|
||||
title: 'Error',
|
||||
message: 'You can\'t remove yourself as Administrator!',
|
||||
type: 'danger',
|
||||
timeout: 5000
|
||||
});
|
||||
} else if (!isUserAdmin(adminBtn)) {
|
||||
socket.emit('admin.user.makeAdmin', uid);
|
||||
adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
|
||||
parent.attr('data-admin', 1);
|
||||
updateUserBanButtons($('.ban-btn'));
|
||||
|
||||
} else if(uid !== yourid) {
|
||||
bootbox.confirm('Do you really want to remove this user as admin "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('admin.user.removeAdmin', uid);
|
||||
adminBtn.attr('value', 'Make Admin').html('Make Admin');
|
||||
parent.attr('data-admin', 0);
|
||||
updateUserBanButtons($('.ban-btn'));
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
function handleUserCreate() {
|
||||
var errorEl = $('#create-modal-error');
|
||||
$('#createUser').on('click', function() {
|
||||
@@ -217,12 +209,12 @@ define(function() {
|
||||
.removeClass('label-danger');
|
||||
}
|
||||
|
||||
initUsers();
|
||||
updateButtons();
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
|
||||
initUsers();
|
||||
updateButtons();
|
||||
|
||||
handleUserCreate();
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
|
||||
|
||||
socket.on('event:new_topic', Category.onNewTopic);
|
||||
|
||||
socket.emit('categories.getRecentReplies', cid, renderRecentReplies);
|
||||
|
||||
enableInfiniteLoading();
|
||||
};
|
||||
|
||||
@@ -84,9 +82,6 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
|
||||
}
|
||||
|
||||
topic.hide().fadeIn('slow');
|
||||
socket.emit('categories.getRecentReplies', templates.get('category_id'), renderRecentReplies);
|
||||
|
||||
addActiveUser(data);
|
||||
|
||||
socket.emit('categories.getPageCount', templates.get('category_id'), function(err, newPageCount) {
|
||||
pagination.recreatePaginationLinks(newPageCount);
|
||||
@@ -97,21 +92,6 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
|
||||
});
|
||||
}
|
||||
|
||||
function addActiveUser(data) {
|
||||
var activeUser = $('.category-sidebar .active-users').find('a[data-uid="' + data.uid + '"]');
|
||||
if(!activeUser.length) {
|
||||
var newUser = templates.prepare(templates['category'].blocks['active_users']).parse({
|
||||
active_users: [{
|
||||
uid: data.uid,
|
||||
username: data.username,
|
||||
userslug: data.userslug,
|
||||
picture: data.teaser_userpicture
|
||||
}]
|
||||
});
|
||||
$(newUser).appendTo($('.category-sidebar .active-users'));
|
||||
}
|
||||
}
|
||||
|
||||
Category.onTopicsLoaded = function(topics) {
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
@@ -159,32 +139,5 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
|
||||
});
|
||||
}
|
||||
|
||||
function renderRecentReplies(err, posts) {
|
||||
if (err || !posts || posts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recentReplies = $('#category_recent_replies');
|
||||
|
||||
templates.preload_template('recentreplies', function() {
|
||||
|
||||
templates['recentreplies'].parse({posts:[]});
|
||||
|
||||
var html = templates.prepare(templates['recentreplies'].blocks['posts']).parse({
|
||||
posts: posts
|
||||
});
|
||||
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
translatedHTML = $(translatedHTML);
|
||||
translatedHTML.find('img').addClass('img-responsive');
|
||||
|
||||
recentReplies.html(translatedHTML);
|
||||
|
||||
$('#category_recent_replies span.timeago').timeago();
|
||||
app.createUserTooltips();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return Category;
|
||||
});
|
||||
@@ -3,33 +3,6 @@ define(function() {
|
||||
|
||||
home.init = function() {
|
||||
|
||||
ajaxify.register_events([
|
||||
'user.count',
|
||||
'meta.getUsageStats',
|
||||
'user.getActiveUsers'
|
||||
]);
|
||||
|
||||
socket.emit('user.count', updateUserCount);
|
||||
socket.on('user.count', updateUserCount);
|
||||
|
||||
function updateUserCount(err, data) {
|
||||
$('#stats_users').html(utils.makeNumberHumanReadable(data.count)).attr('title', data.count);
|
||||
}
|
||||
|
||||
socket.emit('meta.getUsageStats', updateUsageStats);
|
||||
socket.on('meta.getUsageStats', updateUsageStats);
|
||||
|
||||
function updateUsageStats(err, data) {
|
||||
$('#stats_topics').html(utils.makeNumberHumanReadable(data.topics)).attr('title', data.topics);
|
||||
$('#stats_posts').html(utils.makeNumberHumanReadable(data.posts)).attr('title', data.posts);
|
||||
}
|
||||
|
||||
socket.emit('user.getActiveUsers', updateActiveUsers);
|
||||
socket.on('user.getActiveUsers', updateActiveUsers);
|
||||
|
||||
function updateActiveUsers(err, data) {
|
||||
$('#stats_online').html(data.users);
|
||||
}
|
||||
}
|
||||
|
||||
return home;
|
||||
|
||||
@@ -18,23 +18,23 @@ define(function() {
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
|
||||
if (!data.success) {
|
||||
$('#login-error-notify').show();
|
||||
$('#login').removeAttr('disabled').html('Login');
|
||||
} else {
|
||||
$('#login').html('Redirecting...');
|
||||
if(!app.previousUrl) {
|
||||
app.previousUrl = '/';
|
||||
}
|
||||
|
||||
if(app.previousUrl.indexOf('/reset/') != -1)
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
else
|
||||
window.location.replace(app.previousUrl + "?loggedin");
|
||||
|
||||
app.loadConfig();
|
||||
$('#login').html('Redirecting...');
|
||||
if(!app.previousUrl) {
|
||||
app.previousUrl = '/';
|
||||
}
|
||||
|
||||
if(app.previousUrl.indexOf('/reset/') !== -1) {
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
} else {
|
||||
var index = app.previousUrl.indexOf('#');
|
||||
if(index !== -1) {
|
||||
window.location.replace(app.previousUrl.slice(0, index) + '?loggedin' + app.previousUrl.slice(index));
|
||||
} else {
|
||||
window.location.replace(app.previousUrl + "?loggedin");
|
||||
}
|
||||
}
|
||||
|
||||
app.loadConfig();
|
||||
},
|
||||
error: function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').show();
|
||||
|
||||
958
public/src/modules/vendor/async.js
vendored
Normal file
958
public/src/modules/vendor/async.js
vendored
Normal file
@@ -0,0 +1,958 @@
|
||||
/*global setImmediate: false, setTimeout: false, console: false */
|
||||
(function () {
|
||||
|
||||
var async = {};
|
||||
|
||||
// global on the server, window in the browser
|
||||
var root, previous_async;
|
||||
|
||||
root = this;
|
||||
if (root != null) {
|
||||
previous_async = root.async;
|
||||
}
|
||||
|
||||
async.noConflict = function () {
|
||||
root.async = previous_async;
|
||||
return async;
|
||||
};
|
||||
|
||||
function only_once(fn) {
|
||||
var called = false;
|
||||
return function() {
|
||||
if (called) throw new Error("Callback was already called.");
|
||||
called = true;
|
||||
fn.apply(root, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
//// cross-browser compatiblity functions ////
|
||||
|
||||
var _each = function (arr, iterator) {
|
||||
if (arr.forEach) {
|
||||
return arr.forEach(iterator);
|
||||
}
|
||||
for (var i = 0; i < arr.length; i += 1) {
|
||||
iterator(arr[i], i, arr);
|
||||
}
|
||||
};
|
||||
|
||||
var _map = function (arr, iterator) {
|
||||
if (arr.map) {
|
||||
return arr.map(iterator);
|
||||
}
|
||||
var results = [];
|
||||
_each(arr, function (x, i, a) {
|
||||
results.push(iterator(x, i, a));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
var _reduce = function (arr, iterator, memo) {
|
||||
if (arr.reduce) {
|
||||
return arr.reduce(iterator, memo);
|
||||
}
|
||||
_each(arr, function (x, i, a) {
|
||||
memo = iterator(memo, x, i, a);
|
||||
});
|
||||
return memo;
|
||||
};
|
||||
|
||||
var _keys = function (obj) {
|
||||
if (Object.keys) {
|
||||
return Object.keys(obj);
|
||||
}
|
||||
var keys = [];
|
||||
for (var k in obj) {
|
||||
if (obj.hasOwnProperty(k)) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
//// exported async module functions ////
|
||||
|
||||
//// nextTick implementation with browser-compatible fallback ////
|
||||
if (typeof process === 'undefined' || !(process.nextTick)) {
|
||||
if (typeof setImmediate === 'function') {
|
||||
async.nextTick = function (fn) {
|
||||
// not a direct alias for IE10 compatibility
|
||||
setImmediate(fn);
|
||||
};
|
||||
async.setImmediate = async.nextTick;
|
||||
}
|
||||
else {
|
||||
async.nextTick = function (fn) {
|
||||
setTimeout(fn, 0);
|
||||
};
|
||||
async.setImmediate = async.nextTick;
|
||||
}
|
||||
}
|
||||
else {
|
||||
async.nextTick = process.nextTick;
|
||||
if (typeof setImmediate !== 'undefined') {
|
||||
async.setImmediate = function (fn) {
|
||||
// not a direct alias for IE10 compatibility
|
||||
setImmediate(fn);
|
||||
};
|
||||
}
|
||||
else {
|
||||
async.setImmediate = async.nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
async.each = function (arr, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
_each(arr, function (x) {
|
||||
iterator(x, only_once(function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed >= arr.length) {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.forEach = async.each;
|
||||
|
||||
async.eachSeries = function (arr, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
var iterate = function () {
|
||||
iterator(arr[completed], function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed >= arr.length) {
|
||||
callback(null);
|
||||
}
|
||||
else {
|
||||
iterate();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
iterate();
|
||||
};
|
||||
async.forEachSeries = async.eachSeries;
|
||||
|
||||
async.eachLimit = function (arr, limit, iterator, callback) {
|
||||
var fn = _eachLimit(limit);
|
||||
fn.apply(null, [arr, iterator, callback]);
|
||||
};
|
||||
async.forEachLimit = async.eachLimit;
|
||||
|
||||
var _eachLimit = function (limit) {
|
||||
|
||||
return function (arr, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length || limit <= 0) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
var started = 0;
|
||||
var running = 0;
|
||||
|
||||
(function replenish () {
|
||||
if (completed >= arr.length) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
while (running < limit && started < arr.length) {
|
||||
started += 1;
|
||||
running += 1;
|
||||
iterator(arr[started - 1], function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
running -= 1;
|
||||
if (completed >= arr.length) {
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
replenish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var doParallel = function (fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [async.each].concat(args));
|
||||
};
|
||||
};
|
||||
var doParallelLimit = function(limit, fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [_eachLimit(limit)].concat(args));
|
||||
};
|
||||
};
|
||||
var doSeries = function (fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [async.eachSeries].concat(args));
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var _asyncMap = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (err, v) {
|
||||
results[x.index] = v;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
};
|
||||
async.map = doParallel(_asyncMap);
|
||||
async.mapSeries = doSeries(_asyncMap);
|
||||
async.mapLimit = function (arr, limit, iterator, callback) {
|
||||
return _mapLimit(limit)(arr, iterator, callback);
|
||||
};
|
||||
|
||||
var _mapLimit = function(limit) {
|
||||
return doParallelLimit(limit, _asyncMap);
|
||||
};
|
||||
|
||||
// reduce only has a series version, as doing reduce in parallel won't
|
||||
// work in many situations.
|
||||
async.reduce = function (arr, memo, iterator, callback) {
|
||||
async.eachSeries(arr, function (x, callback) {
|
||||
iterator(memo, x, function (err, v) {
|
||||
memo = v;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, memo);
|
||||
});
|
||||
};
|
||||
// inject alias
|
||||
async.inject = async.reduce;
|
||||
// foldl alias
|
||||
async.foldl = async.reduce;
|
||||
|
||||
async.reduceRight = function (arr, memo, iterator, callback) {
|
||||
var reversed = _map(arr, function (x) {
|
||||
return x;
|
||||
}).reverse();
|
||||
async.reduce(reversed, memo, iterator, callback);
|
||||
};
|
||||
// foldr alias
|
||||
async.foldr = async.reduceRight;
|
||||
|
||||
var _filter = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (v) {
|
||||
if (v) {
|
||||
results.push(x);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(_map(results.sort(function (a, b) {
|
||||
return a.index - b.index;
|
||||
}), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.filter = doParallel(_filter);
|
||||
async.filterSeries = doSeries(_filter);
|
||||
// select alias
|
||||
async.select = async.filter;
|
||||
async.selectSeries = async.filterSeries;
|
||||
|
||||
var _reject = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (v) {
|
||||
if (!v) {
|
||||
results.push(x);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(_map(results.sort(function (a, b) {
|
||||
return a.index - b.index;
|
||||
}), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.reject = doParallel(_reject);
|
||||
async.rejectSeries = doSeries(_reject);
|
||||
|
||||
var _detect = function (eachfn, arr, iterator, main_callback) {
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x, function (result) {
|
||||
if (result) {
|
||||
main_callback(x);
|
||||
main_callback = function () {};
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback();
|
||||
});
|
||||
};
|
||||
async.detect = doParallel(_detect);
|
||||
async.detectSeries = doSeries(_detect);
|
||||
|
||||
async.some = function (arr, iterator, main_callback) {
|
||||
async.each(arr, function (x, callback) {
|
||||
iterator(x, function (v) {
|
||||
if (v) {
|
||||
main_callback(true);
|
||||
main_callback = function () {};
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback(false);
|
||||
});
|
||||
};
|
||||
// any alias
|
||||
async.any = async.some;
|
||||
|
||||
async.every = function (arr, iterator, main_callback) {
|
||||
async.each(arr, function (x, callback) {
|
||||
iterator(x, function (v) {
|
||||
if (!v) {
|
||||
main_callback(false);
|
||||
main_callback = function () {};
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback(true);
|
||||
});
|
||||
};
|
||||
// all alias
|
||||
async.all = async.every;
|
||||
|
||||
async.sortBy = function (arr, iterator, callback) {
|
||||
async.map(arr, function (x, callback) {
|
||||
iterator(x, function (err, criteria) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
else {
|
||||
callback(null, {value: x, criteria: criteria});
|
||||
}
|
||||
});
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
else {
|
||||
var fn = function (left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
};
|
||||
callback(null, _map(results.sort(fn), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.auto = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
var keys = _keys(tasks);
|
||||
if (!keys.length) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
var results = {};
|
||||
|
||||
var listeners = [];
|
||||
var addListener = function (fn) {
|
||||
listeners.unshift(fn);
|
||||
};
|
||||
var removeListener = function (fn) {
|
||||
for (var i = 0; i < listeners.length; i += 1) {
|
||||
if (listeners[i] === fn) {
|
||||
listeners.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
var taskComplete = function () {
|
||||
_each(listeners.slice(0), function (fn) {
|
||||
fn();
|
||||
});
|
||||
};
|
||||
|
||||
addListener(function () {
|
||||
if (_keys(results).length === keys.length) {
|
||||
callback(null, results);
|
||||
callback = function () {};
|
||||
}
|
||||
});
|
||||
|
||||
_each(keys, function (k) {
|
||||
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
|
||||
var taskCallback = function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
if (err) {
|
||||
var safeResults = {};
|
||||
_each(_keys(results), function(rkey) {
|
||||
safeResults[rkey] = results[rkey];
|
||||
});
|
||||
safeResults[k] = args;
|
||||
callback(err, safeResults);
|
||||
// stop subsequent errors hitting callback multiple times
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
results[k] = args;
|
||||
async.setImmediate(taskComplete);
|
||||
}
|
||||
};
|
||||
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
|
||||
var ready = function () {
|
||||
return _reduce(requires, function (a, x) {
|
||||
return (a && results.hasOwnProperty(x));
|
||||
}, true) && !results.hasOwnProperty(k);
|
||||
};
|
||||
if (ready()) {
|
||||
task[task.length - 1](taskCallback, results);
|
||||
}
|
||||
else {
|
||||
var listener = function () {
|
||||
if (ready()) {
|
||||
removeListener(listener);
|
||||
task[task.length - 1](taskCallback, results);
|
||||
}
|
||||
};
|
||||
addListener(listener);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.waterfall = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor !== Array) {
|
||||
var err = new Error('First argument to waterfall must be an array of functions');
|
||||
return callback(err);
|
||||
}
|
||||
if (!tasks.length) {
|
||||
return callback();
|
||||
}
|
||||
var wrapIterator = function (iterator) {
|
||||
return function (err) {
|
||||
if (err) {
|
||||
callback.apply(null, arguments);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var next = iterator.next();
|
||||
if (next) {
|
||||
args.push(wrapIterator(next));
|
||||
}
|
||||
else {
|
||||
args.push(callback);
|
||||
}
|
||||
async.setImmediate(function () {
|
||||
iterator.apply(null, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
wrapIterator(async.iterator(tasks))();
|
||||
};
|
||||
|
||||
var _parallel = function(eachfn, tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor === Array) {
|
||||
eachfn.map(tasks, function (fn, callback) {
|
||||
if (fn) {
|
||||
fn(function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
callback.call(null, err, args);
|
||||
});
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
else {
|
||||
var results = {};
|
||||
eachfn.each(_keys(tasks), function (k, callback) {
|
||||
tasks[k](function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async.parallel = function (tasks, callback) {
|
||||
_parallel({ map: async.map, each: async.each }, tasks, callback);
|
||||
};
|
||||
|
||||
async.parallelLimit = function(tasks, limit, callback) {
|
||||
_parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
|
||||
};
|
||||
|
||||
async.series = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor === Array) {
|
||||
async.mapSeries(tasks, function (fn, callback) {
|
||||
if (fn) {
|
||||
fn(function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
callback.call(null, err, args);
|
||||
});
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
else {
|
||||
var results = {};
|
||||
async.eachSeries(_keys(tasks), function (k, callback) {
|
||||
tasks[k](function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async.iterator = function (tasks) {
|
||||
var makeCallback = function (index) {
|
||||
var fn = function () {
|
||||
if (tasks.length) {
|
||||
tasks[index].apply(null, arguments);
|
||||
}
|
||||
return fn.next();
|
||||
};
|
||||
fn.next = function () {
|
||||
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
|
||||
};
|
||||
return fn;
|
||||
};
|
||||
return makeCallback(0);
|
||||
};
|
||||
|
||||
async.apply = function (fn) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
return function () {
|
||||
return fn.apply(
|
||||
null, args.concat(Array.prototype.slice.call(arguments))
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
var _concat = function (eachfn, arr, fn, callback) {
|
||||
var r = [];
|
||||
eachfn(arr, function (x, cb) {
|
||||
fn(x, function (err, y) {
|
||||
r = r.concat(y || []);
|
||||
cb(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, r);
|
||||
});
|
||||
};
|
||||
async.concat = doParallel(_concat);
|
||||
async.concatSeries = doSeries(_concat);
|
||||
|
||||
async.whilst = function (test, iterator, callback) {
|
||||
if (test()) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.whilst(test, iterator, callback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
async.doWhilst = function (iterator, test, callback) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (test()) {
|
||||
async.doWhilst(iterator, test, callback);
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.until = function (test, iterator, callback) {
|
||||
if (!test()) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.until(test, iterator, callback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
async.doUntil = function (iterator, test, callback) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (!test()) {
|
||||
async.doUntil(iterator, test, callback);
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.queue = function (worker, concurrency) {
|
||||
if (concurrency === undefined) {
|
||||
concurrency = 1;
|
||||
}
|
||||
function _insert(q, data, pos, callback) {
|
||||
if(data.constructor !== Array) {
|
||||
data = [data];
|
||||
}
|
||||
_each(data, function(task) {
|
||||
var item = {
|
||||
data: task,
|
||||
callback: typeof callback === 'function' ? callback : null
|
||||
};
|
||||
|
||||
if (pos) {
|
||||
q.tasks.unshift(item);
|
||||
} else {
|
||||
q.tasks.push(item);
|
||||
}
|
||||
|
||||
if (q.saturated && q.tasks.length === concurrency) {
|
||||
q.saturated();
|
||||
}
|
||||
async.setImmediate(q.process);
|
||||
});
|
||||
}
|
||||
|
||||
var workers = 0;
|
||||
var q = {
|
||||
tasks: [],
|
||||
concurrency: concurrency,
|
||||
saturated: null,
|
||||
empty: null,
|
||||
drain: null,
|
||||
push: function (data, callback) {
|
||||
_insert(q, data, false, callback);
|
||||
},
|
||||
unshift: function (data, callback) {
|
||||
_insert(q, data, true, callback);
|
||||
},
|
||||
process: function () {
|
||||
if (workers < q.concurrency && q.tasks.length) {
|
||||
var task = q.tasks.shift();
|
||||
if (q.empty && q.tasks.length === 0) {
|
||||
q.empty();
|
||||
}
|
||||
workers += 1;
|
||||
var next = function () {
|
||||
workers -= 1;
|
||||
if (task.callback) {
|
||||
task.callback.apply(task, arguments);
|
||||
}
|
||||
if (q.drain && q.tasks.length + workers === 0) {
|
||||
q.drain();
|
||||
}
|
||||
q.process();
|
||||
};
|
||||
var cb = only_once(next);
|
||||
worker(task.data, cb);
|
||||
}
|
||||
},
|
||||
length: function () {
|
||||
return q.tasks.length;
|
||||
},
|
||||
running: function () {
|
||||
return workers;
|
||||
}
|
||||
};
|
||||
return q;
|
||||
};
|
||||
|
||||
async.cargo = function (worker, payload) {
|
||||
var working = false,
|
||||
tasks = [];
|
||||
|
||||
var cargo = {
|
||||
tasks: tasks,
|
||||
payload: payload,
|
||||
saturated: null,
|
||||
empty: null,
|
||||
drain: null,
|
||||
push: function (data, callback) {
|
||||
if(data.constructor !== Array) {
|
||||
data = [data];
|
||||
}
|
||||
_each(data, function(task) {
|
||||
tasks.push({
|
||||
data: task,
|
||||
callback: typeof callback === 'function' ? callback : null
|
||||
});
|
||||
if (cargo.saturated && tasks.length === payload) {
|
||||
cargo.saturated();
|
||||
}
|
||||
});
|
||||
async.setImmediate(cargo.process);
|
||||
},
|
||||
process: function process() {
|
||||
if (working) return;
|
||||
if (tasks.length === 0) {
|
||||
if(cargo.drain) cargo.drain();
|
||||
return;
|
||||
}
|
||||
|
||||
var ts = typeof payload === 'number'
|
||||
? tasks.splice(0, payload)
|
||||
: tasks.splice(0);
|
||||
|
||||
var ds = _map(ts, function (task) {
|
||||
return task.data;
|
||||
});
|
||||
|
||||
if(cargo.empty) cargo.empty();
|
||||
working = true;
|
||||
worker(ds, function () {
|
||||
working = false;
|
||||
|
||||
var args = arguments;
|
||||
_each(ts, function (data) {
|
||||
if (data.callback) {
|
||||
data.callback.apply(null, args);
|
||||
}
|
||||
});
|
||||
|
||||
process();
|
||||
});
|
||||
},
|
||||
length: function () {
|
||||
return tasks.length;
|
||||
},
|
||||
running: function () {
|
||||
return working;
|
||||
}
|
||||
};
|
||||
return cargo;
|
||||
};
|
||||
|
||||
var _console_fn = function (name) {
|
||||
return function (fn) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
fn.apply(null, args.concat([function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (typeof console !== 'undefined') {
|
||||
if (err) {
|
||||
if (console.error) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
else if (console[name]) {
|
||||
_each(args, function (x) {
|
||||
console[name](x);
|
||||
});
|
||||
}
|
||||
}
|
||||
}]));
|
||||
};
|
||||
};
|
||||
async.log = _console_fn('log');
|
||||
async.dir = _console_fn('dir');
|
||||
/*async.info = _console_fn('info');
|
||||
async.warn = _console_fn('warn');
|
||||
async.error = _console_fn('error');*/
|
||||
|
||||
async.memoize = function (fn, hasher) {
|
||||
var memo = {};
|
||||
var queues = {};
|
||||
hasher = hasher || function (x) {
|
||||
return x;
|
||||
};
|
||||
var memoized = function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var callback = args.pop();
|
||||
var key = hasher.apply(null, args);
|
||||
if (key in memo) {
|
||||
callback.apply(null, memo[key]);
|
||||
}
|
||||
else if (key in queues) {
|
||||
queues[key].push(callback);
|
||||
}
|
||||
else {
|
||||
queues[key] = [callback];
|
||||
fn.apply(null, args.concat([function () {
|
||||
memo[key] = arguments;
|
||||
var q = queues[key];
|
||||
delete queues[key];
|
||||
for (var i = 0, l = q.length; i < l; i++) {
|
||||
q[i].apply(null, arguments);
|
||||
}
|
||||
}]));
|
||||
}
|
||||
};
|
||||
memoized.memo = memo;
|
||||
memoized.unmemoized = fn;
|
||||
return memoized;
|
||||
};
|
||||
|
||||
async.unmemoize = function (fn) {
|
||||
return function () {
|
||||
return (fn.unmemoized || fn).apply(null, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
async.times = function (count, iterator, callback) {
|
||||
var counter = [];
|
||||
for (var i = 0; i < count; i++) {
|
||||
counter.push(i);
|
||||
}
|
||||
return async.map(counter, iterator, callback);
|
||||
};
|
||||
|
||||
async.timesSeries = function (count, iterator, callback) {
|
||||
var counter = [];
|
||||
for (var i = 0; i < count; i++) {
|
||||
counter.push(i);
|
||||
}
|
||||
return async.mapSeries(counter, iterator, callback);
|
||||
};
|
||||
|
||||
async.compose = function (/* functions... */) {
|
||||
var fns = Array.prototype.reverse.call(arguments);
|
||||
return function () {
|
||||
var that = this;
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var callback = args.pop();
|
||||
async.reduce(fns, args, function (newargs, fn, cb) {
|
||||
fn.apply(that, newargs.concat([function () {
|
||||
var err = arguments[0];
|
||||
var nextargs = Array.prototype.slice.call(arguments, 1);
|
||||
cb(err, nextargs);
|
||||
}]))
|
||||
},
|
||||
function (err, results) {
|
||||
callback.apply(that, [err].concat(results));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var _applyEach = function (eachfn, fns /*args...*/) {
|
||||
var go = function () {
|
||||
var that = this;
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var callback = args.pop();
|
||||
return eachfn(fns, function (fn, cb) {
|
||||
fn.apply(that, args.concat([cb]));
|
||||
},
|
||||
callback);
|
||||
};
|
||||
if (arguments.length > 2) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
return go.apply(this, args);
|
||||
}
|
||||
else {
|
||||
return go;
|
||||
}
|
||||
};
|
||||
async.applyEach = doParallel(_applyEach);
|
||||
async.applyEachSeries = doSeries(_applyEach);
|
||||
|
||||
async.forever = function (fn, callback) {
|
||||
function next(err) {
|
||||
if (err) {
|
||||
if (callback) {
|
||||
return callback(err);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
fn(next);
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
// AMD / RequireJS
|
||||
if (typeof define !== 'undefined' && define.amd) {
|
||||
define([], function () {
|
||||
return async;
|
||||
});
|
||||
}
|
||||
// Node.js
|
||||
else if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = async;
|
||||
}
|
||||
// included directly via <script> tag
|
||||
else {
|
||||
root.async = async;
|
||||
}
|
||||
|
||||
}());
|
||||
@@ -7,7 +7,7 @@
|
||||
var RELATIVE_PATH = "{relative_path}";
|
||||
</script>
|
||||
<link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css">
|
||||
<script src="//code.jquery.com/jquery.js"></script>
|
||||
<script src="{relative_path}/vendor/jquery/js/jquery.js"></script>
|
||||
<script src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/vendor/colorpicker/colorpicker.css">
|
||||
<script src="{relative_path}/socket.io/socket.io.js"></script>
|
||||
@@ -34,7 +34,6 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
|
||||
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
|
||||
<script src="{relative_path}/src/utils.js"></script>
|
||||
|
||||
@@ -106,7 +105,6 @@
|
||||
<li><a href="{relative_path}/admin/languages"><i class="fa fa-fw fa-comments-o"></i> Languages</a></li>
|
||||
<li><a href="{relative_path}/admin/settings"><i class="fa fa-fw fa-cogs"></i> Settings</a></li>
|
||||
<li><a href="{relative_path}/admin/database"><i class="fa fa-fw fa-hdd-o"></i> Database</a></li>
|
||||
<li><a href="{relative_path}/admin/motd"><i class="fa fa-fw fa-comment"></i> MOTD</a></li>
|
||||
<li><a href="{relative_path}/admin/events"><i class="fa fa-fw fa-calendar-o"></i> Events</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<h1><i class="fa fa-comment"></i> MOTD</h1>
|
||||
<hr />
|
||||
<div class="alert alert-warning motd">
|
||||
<p>
|
||||
The <strong>Message of the Day</strong> (MOTD) is typically a message shown to users when they first log into a forum or chat room.
|
||||
In NodeBB, the MOTD is present at the top of the forum homepage, and can be customized much like a header.
|
||||
</p>
|
||||
<p>
|
||||
You can enter full HTML/Javascript.
|
||||
</p>
|
||||
<br />
|
||||
<textarea class="form-control" placeholder="Welcome to NodeBB!" data-field="motd" rows="10"></textarea>
|
||||
<br />
|
||||
<form class="form-inline">
|
||||
<label>MOTD Class</label>
|
||||
<input class="form-control" type="text" placeholder="CSS class to add to MOTD" data-field="motd_class" />
|
||||
</form>
|
||||
<form class="form-inline">
|
||||
<div class="checkbox">
|
||||
<label for="show_motd">
|
||||
<input type="checkbox" id="show_motd" data-field="show_motd" /> Show the Message of the Day
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" id="save" checked>Save</button>
|
||||
|
||||
<script>
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -5,6 +5,7 @@
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#" data-target="#themes" data-toggle="tab">Themes</a></li>
|
||||
<li><a href="#" data-target="#customise" data-toggle="tab">Customise</a></li>
|
||||
<li><a href="#" data-target="#widgets" data-toggle="tab">Widgets</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
@@ -49,6 +50,82 @@
|
||||
|
||||
<button class="btn btn-primary" id="save">Save</button>
|
||||
</div>
|
||||
<div class="tab-pane" id="widgets">
|
||||
<h3>Widgets</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6 pull-right">
|
||||
<!-- BEGIN areas -->
|
||||
<div class="area">
|
||||
<h4>{areas.name} <small>{areas.template} / {areas.location}</small> <button data-template="{areas.template}" data-location="{areas.location}" class="btn btn-success btn-xs pull-right">Save</button></h4>
|
||||
<div class="well widget-area">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- END areas -->
|
||||
</div>
|
||||
<div class="col-xs-6 pull-left">
|
||||
<div class="available-widgets">
|
||||
<h4>Available Widgets <small>Drag and drop widgets into templates</small></h4>
|
||||
<div>
|
||||
<!-- BEGIN widgets -->
|
||||
<div data-widget="{widgets.widget}" class="panel panel-default pointer">
|
||||
<div class="panel-heading">
|
||||
<strong>{widgets.name}</strong> <small>{widgets.description}</small>
|
||||
</div>
|
||||
<div class="panel-body hidden">
|
||||
<form>
|
||||
{widgets.content}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END widgets -->
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="available-containers">
|
||||
<h4>Available Containers <small>Drag and drop on top of any widget</small></h4>
|
||||
<div class="containers">
|
||||
<div class="pointer" style="padding: 20px; border: 1px dotted #dedede; margin-bottom: 20px;" data-container-html=" ">
|
||||
None
|
||||
</div>
|
||||
<div class="well pointer" data-container-html='<div class="well">{body}</div>'>
|
||||
Well
|
||||
</div>
|
||||
<div class="jumbotron pointer" data-container-html='<div class="jumbotron">{body}</div>'>
|
||||
Jumbotron
|
||||
</div>
|
||||
<div class="panel panel-default pointer" data-container-html='<div class="panel panel-default"><div class="panel-heading">{title}</div><div class="panel-body">{body}</div></div>'>
|
||||
<div class="panel-heading">
|
||||
Panel Header
|
||||
<div class="pull-right color-selector">
|
||||
<button data-class="panel-default" class="btn btn-xs"> </button>
|
||||
<button data-class="panel-primary" class="btn btn-xs btn-primary"> </button>
|
||||
<button data-class="panel-success" class="btn btn-xs btn-success"> </button>
|
||||
<button data-class="panel-info" class="btn btn-xs btn-info"> </button>
|
||||
<button data-class="panel-warning" class="btn btn-xs btn-warning"> </button>
|
||||
<button data-class="panel-danger" class="btn btn-xs btn-danger"> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
Panel Body
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info pointer" data-container-html='<div class="alert alert-info">{body}</div>'>
|
||||
Alert
|
||||
<div class="pull-right color-selector">
|
||||
<button data-class="alert-success" class="btn btn-xs btn-success"> </button>
|
||||
<button data-class="alert-info" class="btn btn-xs btn-info"> </button>
|
||||
<button data-class="alert-warning" class="btn btn-xs btn-warning"> </button>
|
||||
<button data-class="alert-danger" class="btn btn-xs btn-danger"> </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -86,51 +86,21 @@
|
||||
</li>
|
||||
<!-- END topics -->
|
||||
</ul>
|
||||
<!-- IF usePagination -->
|
||||
<!-- IF config.usePagination -->
|
||||
<div class="text-center">
|
||||
<ul class="pagination">
|
||||
<li class="previous pull-left"><a href="#"><i class="fa fa-chevron-left"></i> [[global:previouspage]]</a></li>
|
||||
<li class="next pull-right"><a href="#">[[global:nextpage]] <i class="fa fa-chevron-right"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- ENDIF usePagination -->
|
||||
<!-- ENDIF config.usePagination -->
|
||||
</div>
|
||||
|
||||
<!-- IF topics.length -->
|
||||
<div class="col-md-3 col-xs-12 category-sidebar">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">[[category:sidebar.recent_replies]]</div>
|
||||
<div class="panel-body recent-replies">
|
||||
<ul id="category_recent_replies"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">[[category:sidebar.active_participants]]</div>
|
||||
<div class="panel-body active-users">
|
||||
<!-- BEGIN active_users -->
|
||||
<a data-uid="{active_users.uid}" href="../../user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded user-img" /></a>
|
||||
<!-- END active_users -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IF moderators.length -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">[[category:sidebar.moderators]]</div>
|
||||
<div class="panel-body moderators">
|
||||
<!-- BEGIN moderators -->
|
||||
<a data-uid="{moderators.uid}" href="../../user/{moderators.userslug}"><img title="{moderators.username}" src="{moderators.picture}" class="img-rounded user-img" /></a>
|
||||
<!-- END moderators -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- ENDIF moderators.length -->
|
||||
|
||||
<!-- BEGIN sidebars -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel panel-default {sidebars.block_class}">{sidebars.header}</div>
|
||||
<div class="panel-body">{sidebars.content}</div>
|
||||
</div>
|
||||
<!-- END sidebars -->
|
||||
<div widget-area="sidebar" class="col-md-3 col-xs-12 category-sidebar">
|
||||
<!-- BEGIN widgets -->
|
||||
{widgets.html}
|
||||
<!-- END widgets -->
|
||||
</div>
|
||||
<!-- ENDIF topics.length -->
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
<!-- BEGIN pluginCSS -->
|
||||
<link rel="stylesheet" href="{pluginCSS.path}?{cache-buster}">
|
||||
<!-- END pluginCSS -->
|
||||
<!-- IF useCustomCSS -->
|
||||
<style type="text/css">{customCSS}</style>
|
||||
<!-- ENDIF useCustomCSS -->
|
||||
|
||||
<script>
|
||||
var RELATIVE_PATH = "{relative_path}";
|
||||
</script>
|
||||
@@ -31,12 +35,6 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- TODO : this has to be refactored, maybe configured from ACP? -baris -->
|
||||
<link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
|
||||
<!-- IF useCustomCSS -->
|
||||
<style type="text/css">{customCSS}</style>
|
||||
<!-- ENDIF useCustomCSS -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<div class="motd {motd_class}">
|
||||
{motd}
|
||||
<div widget-area="motd" class="hidden">
|
||||
<!-- BEGIN widgets -->
|
||||
<div class="motd">
|
||||
{widgets.html}
|
||||
</div>
|
||||
<!-- END widgets -->
|
||||
</div>
|
||||
|
||||
<div class="row home" itemscope itemtype="http://www.schema.org/ItemList">
|
||||
@@ -45,25 +49,10 @@
|
||||
<!-- END categories -->
|
||||
</div>
|
||||
|
||||
<div class="row footer-stats">
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_online"></span><br /><small>[[footer:stats.online]]</small></h2>
|
||||
</div>
|
||||
<div widget-area="footer" class="hidden">
|
||||
<!-- BEGIN widgets -->
|
||||
<div class="footer">
|
||||
{widgets.html}
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_users"></span><br /><small>[[footer:stats.users]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_topics"></span><br /><small>[[footer:stats.topics]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_posts"></span><br /><small>[[footer:stats.posts]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END widgets -->
|
||||
</div>
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
<!-- BEGIN posts -->
|
||||
<li data-pid="{posts.pid}" class="clearfix">
|
||||
<a href="{relative_path}/user/{posts.userslug}">
|
||||
<img title="{posts.username}" class="img-rounded user-img" src="{posts.picture}" />
|
||||
</a>
|
||||
<strong><span>{posts.username}</span></strong>
|
||||
<p>{posts.content}</p>
|
||||
<span class="pull-right">
|
||||
<a href="{relative_path}/topic/{posts.topicSlug}#{posts.pid}">[[category:posted]]</a>
|
||||
<span class="timeago" title="{posts.relativeTime}"></span>
|
||||
</span>
|
||||
</li>
|
||||
<!-- END posts -->
|
||||
@@ -214,14 +214,14 @@
|
||||
<div style="clear:both;"></div>
|
||||
</div>
|
||||
|
||||
<!-- IF usePagination -->
|
||||
<!-- IF config.usePagination -->
|
||||
<div class="text-center">
|
||||
<ul class="pagination">
|
||||
<li class="previous pull-left"><a href="#"><i class="fa fa-chevron-left"></i> [[global:previouspage]]</a></li>
|
||||
<li class="next pull-right"><a href="#">[[global:nextpage]] <i class="fa fa-chevron-right"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- ENDIF usePagination -->
|
||||
<!-- ENDIF config.usePagination -->
|
||||
|
||||
<div id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Move Topic" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
|
||||
@@ -58,25 +58,6 @@ var db = require('./database'),
|
||||
Categories.getCategoryTopics(category_id, start, end, current_user, next);
|
||||
}
|
||||
|
||||
function getActiveUsers(next) {
|
||||
Categories.getActiveUsers(category_id, function(err, uids) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], next);
|
||||
});
|
||||
}
|
||||
|
||||
function getModerators(next) {
|
||||
Categories.getModerators(category_id, next);
|
||||
}
|
||||
|
||||
function getSidebars(next) {
|
||||
plugins.fireHook('filter:category.build_sidebars', [], function(err, sidebars) {
|
||||
next(err, sidebars);
|
||||
});
|
||||
}
|
||||
|
||||
function getPageCount(next) {
|
||||
Categories.getPageCount(category_id, current_user, next);
|
||||
}
|
||||
@@ -84,9 +65,6 @@ var db = require('./database'),
|
||||
async.parallel({
|
||||
'category': getCategoryData,
|
||||
'topics': getTopics,
|
||||
'active_users': getActiveUsers,
|
||||
'moderators': getModerators,
|
||||
'sidebars': getSidebars,
|
||||
'pageCount': getPageCount
|
||||
}, function(err, results) {
|
||||
if(err) {
|
||||
@@ -100,13 +78,10 @@ var db = require('./database'),
|
||||
'disabled': results.category.disabled,
|
||||
'topic_row_size': 'col-md-9',
|
||||
'category_id': category_id,
|
||||
'active_users': results.active_users,
|
||||
'moderators': results.moderators,
|
||||
'topics': results.topics.topics,
|
||||
'nextStart': results.topics.nextStart,
|
||||
'pageCount': results.pageCount,
|
||||
'disableSocialButtons': meta.config.disableSocialButtons !== undefined ? parseInt(meta.config.disableSocialButtons, 10) !== 0 : false,
|
||||
'sidebars': results.sidebars
|
||||
};
|
||||
|
||||
callback(null, category);
|
||||
|
||||
@@ -171,8 +171,6 @@ var fs = require('fs'),
|
||||
var user = require('./user');
|
||||
|
||||
Meta.title.parseFragment(decodeURIComponent(urlFragment), function(err, title) {
|
||||
var title;
|
||||
|
||||
if (err) {
|
||||
title = Meta.config.browserTitle || 'NodeBB';
|
||||
} else {
|
||||
|
||||
@@ -375,8 +375,10 @@ var fs = require('fs'),
|
||||
dirs = dirs.map(function(file) {
|
||||
return path.join(npmPluginPath, file);
|
||||
}).filter(function(file) {
|
||||
var stats = fs.statSync(file);
|
||||
if (stats.isDirectory() && file.substr(npmPluginPath.length + 1, 14) === 'nodebb-plugin-') return true;
|
||||
var stats = fs.statSync(file),
|
||||
isPlugin = file.substr(npmPluginPath.length + 1, 14) === 'nodebb-plugin-' || file.substr(npmPluginPath.length + 1, 14) === 'nodebb-widget-';
|
||||
|
||||
if (stats.isDirectory() && isPlugin) return true;
|
||||
else return false;
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ var nconf = require('nconf'),
|
||||
categories = require('./../categories'),
|
||||
meta = require('../meta'),
|
||||
plugins = require('../plugins'),
|
||||
widgets = require('../widgets'),
|
||||
image = require('./../image'),
|
||||
file = require('./../file'),
|
||||
Languages = require('../languages'),
|
||||
@@ -414,7 +415,32 @@ var nconf = require('nconf'),
|
||||
});
|
||||
|
||||
app.get('/themes', function (req, res) {
|
||||
res.json(200, {});
|
||||
async.parallel({
|
||||
areas: function(next) {
|
||||
plugins.fireHook('filter:widgets.getAreas', [], next);
|
||||
},
|
||||
widgets: function(next) {
|
||||
plugins.fireHook('filter:widgets.getWidgets', [], next);
|
||||
}
|
||||
}, function(err, data) {
|
||||
async.each(data.areas, function(area, next) {
|
||||
widgets.getArea(area.template, area.location, function(err, areaData) {
|
||||
area.data = areaData;
|
||||
next(err);
|
||||
});
|
||||
}, function(err) {
|
||||
for (var w in data.widgets) {
|
||||
if (data.widgets.hasOwnProperty(w)) {
|
||||
data.widgets[w].content += "<br /><label>Title:</label><input type=\"text\" class=\"form-control\" name=\"title\" placeholder=\"Title (only shown on some containers)\" /><br /><label>Container:</label><textarea rows=\"4\" class=\"form-control container-html\" name=\"container\" placeholder=\"Drag and drop a container or enter HTML here.\"></textarea>";
|
||||
}
|
||||
}
|
||||
|
||||
res.json(200, {
|
||||
areas: data.areas,
|
||||
widgets: data.widgets
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/testing/categories', function (req, res) {
|
||||
|
||||
@@ -108,45 +108,7 @@ var path = require('path'),
|
||||
data.categories = visibleCategories;
|
||||
|
||||
async.each(data.categories, getRecentReplies, function (err) {
|
||||
|
||||
var motdString,
|
||||
assemble = function() {
|
||||
data.motd_class = (parseInt(meta.config.show_motd, 10) === 1 || meta.config.show_motd === undefined) ? '' : ' none';
|
||||
data.motd_class += (meta.config.motd && meta.config.motd.length > 0) ? '' : ' default';
|
||||
data.motd_class += meta.config.motd_class ? ' ' + meta.config.motd_class : '';
|
||||
|
||||
data.motd = motdString;
|
||||
res.json(data);
|
||||
};
|
||||
|
||||
if (!meta.config.motd) {
|
||||
translator.translate('\n\n<h1>NodeBB</h1> <small><span>v' + pkg.version + '</span></small>\n\n<h5>[[global:motd.welcome]]</h5>\
|
||||
<div class="btn-group">\
|
||||
<a target="_blank" href="https://www.nodebb.org" class="btn btn-link btn-md">\
|
||||
<i class="fa fa-comment"></i>\
|
||||
<span> [[global:motd.get]]</span>\
|
||||
</a>\
|
||||
<a target="_blank" href="https://github.com/designcreateplay/NodeBB" class="btn btn-link btn-md">\
|
||||
<i class="fa fa-github"></i>\
|
||||
<span> [[global:motd.fork]]</span>\
|
||||
</a>\
|
||||
<a target="_blank" href="https://facebook.com/NodeBB" class="btn btn-link btn-md">\
|
||||
<i class="fa fa-facebook"></i>\
|
||||
<span> [[global:motd.like]]</span>\
|
||||
</a>\
|
||||
<a target="_blank" href="https://twitter.com/NodeBB" class="btn btn-link btn-md">\
|
||||
<i class="fa fa-twitter"></i>\
|
||||
<span> [[global:motd.follow]]</span>\
|
||||
</a>\
|
||||
</div>\
|
||||
', function(motd) {
|
||||
motdString = motd;
|
||||
assemble();
|
||||
});
|
||||
} else {
|
||||
motdString = meta.config.motd;
|
||||
assemble();
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,16 +13,6 @@
|
||||
|
||||
login_strategies = [];
|
||||
|
||||
passport.use(new passportLocal(function(user, password, next) {
|
||||
Auth.login(user, password, function(err, login) {
|
||||
if (!err) {
|
||||
next(null, login.user);
|
||||
} else {
|
||||
next(null, false, err);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
plugins.ready(function() {
|
||||
plugins.fireHook('filter:auth.init', login_strategies, function(err) {
|
||||
if (err) {
|
||||
@@ -33,16 +23,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user.uid);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function(uid, done) {
|
||||
done(null, {
|
||||
uid: uid
|
||||
});
|
||||
});
|
||||
|
||||
Auth.initialize = function(app) {
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
@@ -107,11 +87,9 @@
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return res.send({
|
||||
success: false,
|
||||
message: info.message
|
||||
});
|
||||
return res.json(403, info);
|
||||
}
|
||||
|
||||
// Alter user cookie depending on passed-in option
|
||||
@@ -127,10 +105,7 @@
|
||||
req.login({
|
||||
uid: user.uid
|
||||
}, function() {
|
||||
res.send({
|
||||
success: true,
|
||||
message: 'authentication succeeded'
|
||||
});
|
||||
res.json(info);
|
||||
});
|
||||
})(req, res, next);
|
||||
});
|
||||
@@ -163,50 +138,60 @@
|
||||
|
||||
Auth.login = function(username, password, next) {
|
||||
if (!username || !password) {
|
||||
return next({
|
||||
status: 'error',
|
||||
message: 'invalid-user'
|
||||
});
|
||||
} else {
|
||||
return next(new Error('invalid-user'));
|
||||
}
|
||||
|
||||
var userslug = utils.slugify(username);
|
||||
var userslug = utils.slugify(username);
|
||||
|
||||
user.getUidByUserslug(userslug, function(err, uid) {
|
||||
user.getUidByUserslug(userslug, function(err, uid) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if(!uid) {
|
||||
return next(null, false, 'user doesn\'t exist');
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['password', 'banned'], function(err, userData) {
|
||||
if (err) {
|
||||
return next(new Error('redis-error'));
|
||||
} else if (uid == null) {
|
||||
return next(new Error('invalid-user'));
|
||||
return next(err);
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['password', 'banned'], function(err, userData) {
|
||||
if (err) return next(err);
|
||||
if (!userData || !userData.password) {
|
||||
return next(new Error('invalid userdata or password'));
|
||||
}
|
||||
|
||||
if (userData.banned && parseInt(userData.banned, 10) === 1) {
|
||||
return next({
|
||||
status: "error",
|
||||
message: "user-banned"
|
||||
});
|
||||
if (userData.banned && parseInt(userData.banned, 10) === 1) {
|
||||
return next(null, false, 'User banned');
|
||||
}
|
||||
|
||||
bcrypt.compare(password, userData.password, function(err, res) {
|
||||
if (err) {
|
||||
winston.err(err.message);
|
||||
return next(new Error('bcrypt compare error'));
|
||||
}
|
||||
|
||||
bcrypt.compare(password, userData.password, function(err, res) {
|
||||
if (err) {
|
||||
winston.err(err.message);
|
||||
next(new Error('bcrypt compare error'));
|
||||
return;
|
||||
}
|
||||
if (!res) {
|
||||
next(null, false, 'invalid-password');
|
||||
}
|
||||
|
||||
if (res) {
|
||||
next(null, {
|
||||
user: {
|
||||
uid: uid
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next(new Error('invalid-password'));
|
||||
}
|
||||
});
|
||||
next(null, {
|
||||
uid: uid
|
||||
}, 'Authentication successful');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
passport.use(new passportLocal(Auth.login));
|
||||
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user.uid);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function(uid, done) {
|
||||
done(null, {
|
||||
uid: uid
|
||||
});
|
||||
});
|
||||
}(exports));
|
||||
@@ -45,18 +45,17 @@ var fs = require('fs'),
|
||||
|
||||
app.namespace('/user', function () {
|
||||
|
||||
function createRoute(routeName, path, templateName) {
|
||||
app.get(routeName, function(req, res, next) {
|
||||
if (!req.params.userslug) {
|
||||
return next();
|
||||
}
|
||||
function createRoute(routeName, path, templateName, access) {
|
||||
|
||||
if (!req.user && (path === '/favourites' || !!parseInt(meta.config.privateUserInfo, 10))) {
|
||||
function isAllowed(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
if (!callerUID && !!parseInt(meta.config.privateUserInfo, 10)) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
user.getUidByUserslug(req.params.userslug, function (err, uid) {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
@@ -64,17 +63,41 @@ var fs = require('fs'),
|
||||
return res.redirect('/404');
|
||||
}
|
||||
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
res.send(header + app.create_route('user/' + req.params.userslug + path, templateName) + templates['footer']);
|
||||
});
|
||||
if (parseInt(uid, 10) === callerUID) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.path.indexOf('/edit') !== -1) {
|
||||
user.isAdministrator(callerUID, function(err, isAdmin) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if(!isAdmin) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
} else if (req.path.indexOf('/settings') !== -1 || req.path.indexOf('/favourites') !== -1) {
|
||||
res.redirect('/403')
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
app.get(routeName, isAllowed, function(req, res, next) {
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
res.send(header + app.create_route('user/' + req.params.userslug + path, templateName) + templates['footer']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createRoute('/:userslug', '', 'account');
|
||||
@@ -82,66 +105,8 @@ var fs = require('fs'),
|
||||
createRoute('/:userslug/followers', '/followers', 'followers');
|
||||
createRoute('/:userslug/favourites', '/favourites', 'favourites');
|
||||
createRoute('/:userslug/posts', '/posts', 'accountposts');
|
||||
|
||||
app.get('/:userslug/edit', function (req, res, next) {
|
||||
|
||||
if (!req.user) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
|
||||
function done() {
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send(header + app.create_route('user/' + req.params.userslug + '/edit', 'accountedit') + templates['footer']);
|
||||
});
|
||||
}
|
||||
|
||||
if(err || !userslug) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (userslug === req.params.userslug) {
|
||||
return done();
|
||||
}
|
||||
|
||||
user.isAdministrator(req.user.uid, function(err, isAdmin) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if(!isAdmin) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/:userslug/settings', function (req, res) {
|
||||
|
||||
if (!req.user) {
|
||||
return res.redirect('/403');
|
||||
}
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
|
||||
if (req.params.userslug && userslug === req.params.userslug) {
|
||||
app.build_header({
|
||||
req: req,
|
||||
res: res
|
||||
}, function (err, header) {
|
||||
res.send(header + app.create_route('user/' + req.params.userslug + '/settings', 'accountsettings') + templates['footer']);
|
||||
})
|
||||
} else {
|
||||
return res.redirect('/404');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
createRoute('/:userslug/edit', '/edit', 'accountedit');
|
||||
createRoute('/:userslug/settings', '/settings', 'accountsettings');
|
||||
|
||||
app.post('/uploadpicture', function (req, res) {
|
||||
if (!req.user) {
|
||||
@@ -257,164 +222,77 @@ var fs = require('fs'),
|
||||
next();
|
||||
}
|
||||
|
||||
app.get('/api/user/:userslug/following', isAllowed, function (req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
app.get('/api/user/:userslug/following', isAllowed, getUserFollowing);
|
||||
app.get('/api/user/:userslug/followers', isAllowed, getUserFollowers);
|
||||
app.get('/api/user/:userslug/edit', isAllowed, getUserEdit);
|
||||
app.get('/api/user/:userslug/settings', isAllowed, getUserSettings);
|
||||
app.get('/api/user/:userslug/favourites', isAllowed, getUserFavourites);
|
||||
app.get('/api/user/:userslug/posts', isAllowed, getUserPosts);
|
||||
app.get('/api/user/uid/:uid', isAllowed, getUserData);
|
||||
app.get('/api/user/:userslug', isAllowed, getUserProfile);
|
||||
app.get('/api/users', isAllowed, getOnlineUsers);
|
||||
app.get('/api/users/sort-posts', isAllowed, getUsersSortedByPosts);
|
||||
app.get('/api/users/sort-reputation', isAllowed, getUsersSortedByReputation);
|
||||
app.get('/api/users/latest', isAllowed, getUsersSortedByJoinDate);
|
||||
app.get('/api/users/online', isAllowed, getOnlineUsers);
|
||||
app.get('/api/users/search', isAllowed, getUsersForSearch);
|
||||
|
||||
|
||||
function getUserProfile(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (userData) {
|
||||
user.getFollowing(userData.uid, function (err, followingData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
userData.following = followingData;
|
||||
userData.followingCount = followingData.length;
|
||||
res.json(userData);
|
||||
});
|
||||
|
||||
} else {
|
||||
res.json(404, {
|
||||
if(!userData) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/user/:userslug/followers', isAllowed, function (req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
user.isFollowing(callerUID, userData.theirid, function (isFollowing) {
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
posts.getPostsByUid(callerUID, userData.theirid, 0, 9, function (err, userPosts) {
|
||||
|
||||
if (userData) {
|
||||
user.getFollowers(userData.uid, function (err, followersData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
userData.followers = followersData;
|
||||
userData.followersCount = followersData.length;
|
||||
res.json(userData);
|
||||
});
|
||||
} else {
|
||||
res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/user/:userslug/edit', function (req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
userData.posts = userPosts.posts.filter(function (p) {
|
||||
return p && parseInt(p.deleted, 10) !== 1;
|
||||
});
|
||||
|
||||
if(!parseInt(callerUID, 10)) {
|
||||
return res.json(403, {
|
||||
error: 'Not allowed!'
|
||||
userData.isFollowing = isFollowing;
|
||||
|
||||
if (!userData.profileviews) {
|
||||
userData.profileviews = 1;
|
||||
}
|
||||
|
||||
if (callerUID !== parseInt(userData.uid, 10) && callerUID) {
|
||||
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
|
||||
}
|
||||
|
||||
postTools.parse(userData.signature, function (err, signature) {
|
||||
userData.signature = signature;
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
function getUserData(req, res, next) {
|
||||
var uid = req.params.uid ? req.params.uid : 0;
|
||||
|
||||
user.getUserData(uid, function(err, userData) {
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/api/user/:userslug/settings', function(req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
|
||||
user.getUidByUserslug(req.params.userslug, function(err, uid) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!uid) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
if (uid != callerUID || callerUID == '0') {
|
||||
return res.json(403, {
|
||||
error: 'Not allowed!'
|
||||
});
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:user.settings', [], function(err, settings) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if(!userData) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
userData.yourid = req.user.uid;
|
||||
userData.theirid = uid;
|
||||
userData.settings = settings;
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/user/:userslug/favourites', isAllowed, function (req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
|
||||
user.getUidByUserslug(req.params.userslug, function (err, uid) {
|
||||
if (!uid) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
if (uid != callerUID || callerUID == '0') {
|
||||
return res.json(403, {
|
||||
error: 'Not allowed!'
|
||||
});
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['username', 'userslug'], function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!userData) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
posts.getFavourites(uid, 0, 9, function (err, favourites) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
userData.theirid = uid;
|
||||
userData.yourid = callerUID;
|
||||
userData.posts = favourites.posts;
|
||||
userData.nextStart = favourites.nextStart;
|
||||
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/user/:userslug/posts', isAllowed, function (req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
function getUserPosts(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
user.getUidByUserslug(req.params.userslug, function (err, uid) {
|
||||
if (!uid) {
|
||||
@@ -448,70 +326,157 @@ var fs = require('fs'),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getUserFavourites(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
app.get('/api/user/uid/:uid', isAllowed, function(req, res, next) {
|
||||
var uid = req.params.uid ? req.params.uid : 0;
|
||||
user.getUidByUserslug(req.params.userslug, function (err, uid) {
|
||||
if (!uid) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
user.getUserData(uid, function(err, userData) {
|
||||
if (parseInt(uid, 10) !== callerUID) {
|
||||
return res.json(403, {
|
||||
error: 'Not allowed!'
|
||||
});
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['username', 'userslug'], function (err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!userData) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
posts.getFavourites(uid, 0, 9, function (err, favourites) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
userData.theirid = uid;
|
||||
userData.yourid = callerUID;
|
||||
userData.posts = favourites.posts;
|
||||
userData.nextStart = favourites.nextStart;
|
||||
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getUserSettings(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
user.getUidByUserslug(req.params.userslug, function(err, uid) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (!uid) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
if (parseInt(uid, 10) !== callerUID) {
|
||||
return res.json(403, {
|
||||
error: 'Not allowed!'
|
||||
});
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:user.settings', [], function(err, settings) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if(!userData) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
userData.yourid = req.user.uid;
|
||||
userData.theirid = uid;
|
||||
userData.settings = settings;
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function getUserEdit(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/api/user/:userslug', isAllowed, function (req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : '0';
|
||||
function getUserFollowers(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if(!userData) {
|
||||
return res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
|
||||
user.isFollowing(callerUID, userData.theirid, function (isFollowing) {
|
||||
|
||||
posts.getPostsByUid(callerUID, userData.theirid, 0, 9, function (err, userPosts) {
|
||||
|
||||
if (userData) {
|
||||
user.getFollowers(userData.uid, function (err, followersData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
userData.posts = userPosts.posts.filter(function (p) {
|
||||
return p && parseInt(p.deleted, 10) !== 1;
|
||||
});
|
||||
|
||||
userData.isFollowing = isFollowing;
|
||||
|
||||
if (!userData.profileviews) {
|
||||
userData.profileviews = 1;
|
||||
}
|
||||
|
||||
if (parseInt(callerUID, 10) !== parseInt(userData.uid, 10) && parseInt(callerUID, 0)) {
|
||||
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
|
||||
}
|
||||
|
||||
postTools.parse(userData.signature, function (err, signature) {
|
||||
userData.signature = signature;
|
||||
res.json(userData);
|
||||
});
|
||||
userData.followers = followersData;
|
||||
userData.followersCount = followersData.length;
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/api/users', isAllowed, getOnlineUsers);
|
||||
app.get('/api/users/sort-posts', isAllowed, getUsersSortedByPosts);
|
||||
app.get('/api/users/sort-reputation', isAllowed, getUsersSortedByReputation);
|
||||
app.get('/api/users/latest', isAllowed, getUsersSortedByJoinDate);
|
||||
app.get('/api/users/online', isAllowed, getOnlineUsers);
|
||||
app.get('/api/users/search', isAllowed, getUsersForSearch);
|
||||
function getUserFollowing(req, res, next) {
|
||||
var callerUID = req.user ? parseInt(req.user.uid, 10) : 0;
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (userData) {
|
||||
user.getFollowing(userData.uid, function (err, followingData) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
userData.following = followingData;
|
||||
userData.followingCount = followingData.length;
|
||||
res.json(userData);
|
||||
});
|
||||
|
||||
} else {
|
||||
res.json(404, {
|
||||
error: 'User not found!'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getUsersSortedByJoinDate(req, res) {
|
||||
user.getUsers('users:joindate', 0, 49, function (err, data) {
|
||||
@@ -607,78 +572,76 @@ var fs = require('fs'),
|
||||
}
|
||||
|
||||
function getUserDataByUserSlug(userslug, callerUID, callback) {
|
||||
var userData;
|
||||
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
user.getUidByUserslug(userslug, next);
|
||||
},
|
||||
function(uid, next) {
|
||||
if (!uid) {
|
||||
return next(new Error('invalid-user'));
|
||||
user.getUidByUserslug(userslug, function(err, uid) {
|
||||
if(err || !uid) {
|
||||
return callback(err || new Error('invalid-user'));
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
userData : function(next) {
|
||||
user.getUserData(uid, next);
|
||||
},
|
||||
userSettings : function(next) {
|
||||
user.getSettings(uid, next);
|
||||
},
|
||||
isAdmin : function(next) {
|
||||
user.isAdministrator(callerUID, next);
|
||||
},
|
||||
followStats: function(next) {
|
||||
user.getFollowStats(uid, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if(err || !results.userData) {
|
||||
return callback(err || new Error('invalid-user'));
|
||||
}
|
||||
|
||||
user.getUserData(uid, next);
|
||||
},
|
||||
function(data, next) {
|
||||
userData = data;
|
||||
if (!userData) {
|
||||
return callback(new Error('invalid-user'));
|
||||
var userData = results.userData;
|
||||
var userSettings = results.userSettings;
|
||||
var isAdmin = results.isAdmin;
|
||||
|
||||
userData.joindate = utils.toISOString(userData.joindate);
|
||||
if(userData.lastonline) {
|
||||
userData.lastonline = utils.toISOString(userData.lastonline);
|
||||
} else {
|
||||
userData.lastonline = userData.joindate;
|
||||
}
|
||||
|
||||
user.isAdministrator(callerUID, next);
|
||||
}
|
||||
], function(err, isAdmin) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
userData.joindate = utils.toISOString(userData.joindate);
|
||||
if(userData.lastonline) {
|
||||
userData.lastonline = utils.toISOString(userData.lastonline);
|
||||
} else {
|
||||
userData.lastonline = userData.joindate;
|
||||
}
|
||||
|
||||
if (!userData.birthday) {
|
||||
userData.age = '';
|
||||
} else {
|
||||
userData.age = Math.floor((new Date().getTime() - new Date(userData.birthday).getTime()) / 31536000000);
|
||||
}
|
||||
|
||||
function canSeeEmail() {
|
||||
return isAdmin || callerUID == userData.uid || (userData.email && (userData.showemail && parseInt(userData.showemail, 10) === 1));
|
||||
}
|
||||
|
||||
if (!canSeeEmail()) {
|
||||
userData.email = "";
|
||||
}
|
||||
|
||||
if (callerUID == userData.uid && (!userData.showemail || parseInt(userData.showemail, 10) === 0)) {
|
||||
userData.emailClass = "";
|
||||
} else {
|
||||
userData.emailClass = "hide";
|
||||
}
|
||||
|
||||
userData.websiteName = userData.website.replace('http://', '').replace('https://', '');
|
||||
userData.banned = parseInt(userData.banned, 10) === 1;
|
||||
userData.uid = userData.uid;
|
||||
userData.yourid = callerUID;
|
||||
userData.theirid = userData.uid;
|
||||
|
||||
userData.disableSignatures = meta.config.disableSignatures !== undefined && parseInt(meta.config.disableSignatures, 10) === 1;
|
||||
|
||||
user.getFollowStats(userData.uid, function (err, followStats) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
if (!userData.birthday) {
|
||||
userData.age = '';
|
||||
} else {
|
||||
userData.age = Math.floor((new Date().getTime() - new Date(userData.birthday).getTime()) / 31536000000);
|
||||
}
|
||||
userData.followingCount = followStats.followingCount;
|
||||
userData.followerCount = followStats.followerCount;
|
||||
|
||||
function canSeeEmail() {
|
||||
return isAdmin || parseInt(callerUID, 10) === parseInt(userData.uid, 10) || (userData.email && userSettings.showemail);
|
||||
}
|
||||
|
||||
if (!canSeeEmail()) {
|
||||
userData.email = "";
|
||||
}
|
||||
|
||||
if (parseInt(callerUID, 10) === parseInt(userData.uid, 10) && !userSettings.showemail) {
|
||||
userData.emailClass = "";
|
||||
} else {
|
||||
userData.emailClass = "hide";
|
||||
}
|
||||
|
||||
userData.websiteName = userData.website.replace('http://', '').replace('https://', '');
|
||||
userData.banned = parseInt(userData.banned, 10) === 1;
|
||||
userData.uid = userData.uid;
|
||||
userData.yourid = callerUID;
|
||||
userData.theirid = userData.uid;
|
||||
|
||||
userData.disableSignatures = meta.config.disableSignatures !== undefined && parseInt(meta.config.disableSignatures, 10) === 1;
|
||||
|
||||
userData.followingCount = results.followStats.followingCount;
|
||||
userData.followerCount = results.followStats.followerCount;
|
||||
|
||||
callback(null, userData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}(exports));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
var groups = require('../groups'),
|
||||
meta = require('../meta'),
|
||||
plugins = require('../plugins'),
|
||||
widgets = require('../widgets'),
|
||||
user = require('../user'),
|
||||
topics = require('../topics'),
|
||||
categories = require('../categories'),
|
||||
@@ -255,10 +256,11 @@ SocketAdmin.categories.groupsList = function(socket, cid, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/* Themes & Plugins */
|
||||
/* Themes, Widgets, and Plugins */
|
||||
|
||||
SocketAdmin.themes = {};
|
||||
SocketAdmin.plugins = {};
|
||||
SocketAdmin.widgets = {};
|
||||
|
||||
SocketAdmin.themes.getInstalled = function(socket, data, callback) {
|
||||
meta.themes.get(callback);
|
||||
@@ -269,7 +271,7 @@ SocketAdmin.themes.set = function(socket, data, callback) {
|
||||
return callback(new Error('invalid data'));
|
||||
}
|
||||
meta.themes.set(data, callback);
|
||||
}
|
||||
};
|
||||
|
||||
SocketAdmin.plugins.toggle = function(socket, plugin_id) {
|
||||
plugins.toggleActive(plugin_id, function(status) {
|
||||
@@ -277,6 +279,14 @@ SocketAdmin.plugins.toggle = function(socket, plugin_id) {
|
||||
});
|
||||
};
|
||||
|
||||
SocketAdmin.widgets.set = function(socket, data, callback) {
|
||||
if(!data) {
|
||||
return callback(new Error('invalid data'));
|
||||
}
|
||||
|
||||
widgets.setArea(data, callback);
|
||||
};
|
||||
|
||||
/* Configs */
|
||||
|
||||
SocketAdmin.config = {};
|
||||
|
||||
11
src/socket.io/widgets.js
Normal file
11
src/socket.io/widgets.js
Normal file
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
var widgets = require('../widgets'),
|
||||
|
||||
SocketWidgets = {};
|
||||
|
||||
SocketWidgets.render = function(socket, data, callback) {
|
||||
widgets.render(socket.uid, data, callback);
|
||||
};
|
||||
|
||||
module.exports = SocketWidgets;
|
||||
130
src/upgrade.js
130
src/upgrade.js
@@ -19,7 +19,7 @@ var db = require('./database'),
|
||||
|
||||
Upgrade.check = function(callback) {
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
|
||||
var latestSchema = new Date(2014, 1, 14, 21, 50).getTime();
|
||||
var latestSchema = new Date(2014, 1, 20, 20, 25).getTime();
|
||||
|
||||
db.get('schemaDate', function(err, value) {
|
||||
if (parseInt(value, 10) >= latestSchema) {
|
||||
@@ -685,6 +685,134 @@ Upgrade.upgrade = function(callback) {
|
||||
winston.info('[2014/2/14] Added posts to sorted set - skipped');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2014, 1, 19, 18, 15).getTime();
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
|
||||
db.setObjectField('widgets:home.tpl', 'motd', JSON.stringify([
|
||||
{
|
||||
"widget": "html",
|
||||
"data": {
|
||||
"html": Meta.config['motd'] || "Welcome to NodeBB, if you are an administrator of this forum visit the <a target='_blank' href='/admin/themes'>Themes</a> ACP to modify and add widgets."
|
||||
}
|
||||
}
|
||||
]), function(err) {
|
||||
Meta.configs.remove('motd');
|
||||
Meta.configs.remove('motd_class');
|
||||
Meta.configs.remove('show_motd');
|
||||
|
||||
winston.info('[2014/2/19] Updated MOTD to use the HTML widget.');
|
||||
next(err);
|
||||
});
|
||||
} else {
|
||||
winston.info('[2014/2/19] Updating MOTD to use the HTML widget - skipped');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2014, 1, 20, 15, 30).getTime();
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
|
||||
var container = '<div class="panel panel-default"><div class="panel-heading">{title}</div><div class="panel-body">{body}</div></div>';
|
||||
|
||||
db.setObjectField('widgets:category.tpl', 'sidebar', JSON.stringify([
|
||||
{
|
||||
"widget": "recentreplies",
|
||||
"data": {
|
||||
"title": "Recent Replies",
|
||||
"container": container
|
||||
}
|
||||
},
|
||||
{
|
||||
"widget": "activeusers",
|
||||
"data": {
|
||||
"title": "Active Users",
|
||||
"container": container
|
||||
}
|
||||
},
|
||||
{
|
||||
"widget": "moderators",
|
||||
"data": {
|
||||
"title": "Moderators",
|
||||
"container": container
|
||||
}
|
||||
}
|
||||
]), function(err) {
|
||||
winston.info('[2014/2/20] Adding Recent Replies, Active Users, and Moderator widgets to category sidebar.');
|
||||
next(err);
|
||||
});
|
||||
} else {
|
||||
winston.info('[2014/2/20] Adding Recent Replies, Active Users, and Moderator widgets to category sidebar - skipped');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2014, 1, 20, 16, 15).getTime();
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
|
||||
db.setObjectField('widgets:home.tpl', 'footer', JSON.stringify([
|
||||
{
|
||||
"widget": "forumstats",
|
||||
"data": {}
|
||||
}
|
||||
]), function(err) {
|
||||
winston.info('[2014/2/20] Adding Forum Stats Widget to the Homepage Footer.');
|
||||
next(err);
|
||||
});
|
||||
} else {
|
||||
winston.info('[2014/2/20] Adding Forum Stats Widget to the Homepage Footer - skipped');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2014, 1, 20, 19, 45).getTime();
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
|
||||
var container = '<div class="panel panel-default"><div class="panel-heading">{title}</div><div class="panel-body">{body}</div></div>';
|
||||
|
||||
db.setObjectField('widgets:home.tpl', 'sidebar', JSON.stringify([
|
||||
{
|
||||
"widget": "html",
|
||||
"data": {
|
||||
"html": Meta.config['motd'] || "Welcome to NodeBB, if you are an administrator of this forum visit the <a target='_blank' href='/admin/themes'>Themes</a> ACP to modify and add widgets.",
|
||||
"container": container,
|
||||
"title": "MOTD"
|
||||
}
|
||||
}
|
||||
]), function(err) {
|
||||
winston.info('[2014/2/20] Updating Lavender MOTD');
|
||||
next(err);
|
||||
});
|
||||
} else {
|
||||
winston.info('[2014/2/20] Updating Lavender MOTD - skipped');
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
thisSchemaDate = new Date(2014, 1, 20, 20, 25).getTime();
|
||||
|
||||
if (schemaDate < thisSchemaDate) {
|
||||
updatesMade = true;
|
||||
|
||||
db.setAdd('plugins:active', 'nodebb-widget-essentials', function(err) {
|
||||
winston.info('[2014/2/20] Activating NodeBB Essential Widgets');
|
||||
Plugins.reload(function() {
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
winston.info('[2014/2/20] Activating NodeBB Essential Widgets - skipped');
|
||||
next();
|
||||
}
|
||||
}
|
||||
// Add new schema updates here
|
||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 17!!!
|
||||
|
||||
63
src/user.js
63
src/user.js
@@ -104,43 +104,46 @@ var bcrypt = require('bcryptjs'),
|
||||
'postcount': 0,
|
||||
'lastposttime': 0,
|
||||
'banned': 0,
|
||||
'status': 'online',
|
||||
'showemail': 0
|
||||
'status': 'online'
|
||||
};
|
||||
|
||||
db.setObject('user:' + uid, userData);
|
||||
db.setObject('user:' + uid, userData, function(err) {
|
||||
|
||||
db.setObjectField('username:uid', userData.username, uid);
|
||||
db.setObjectField('userslug:uid', userData.userslug, uid);
|
||||
|
||||
if (userData.email !== undefined) {
|
||||
db.setObjectField('email:uid', userData.email, uid);
|
||||
if (parseInt(uid, 10) !== 1) {
|
||||
User.email.verify(uid, userData.email);
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
db.setObjectField('username:uid', userData.username, uid);
|
||||
db.setObjectField('userslug:uid', userData.userslug, uid);
|
||||
|
||||
plugins.fireHook('action:user.create', userData);
|
||||
db.incrObjectField('global', 'userCount');
|
||||
|
||||
db.sortedSetAdd('users:joindate', timestamp, uid);
|
||||
db.sortedSetAdd('users:postcount', 0, uid);
|
||||
db.sortedSetAdd('users:reputation', 0, uid);
|
||||
|
||||
groups.joinByGroupName('registered-users', uid);
|
||||
|
||||
if (password) {
|
||||
User.hashPassword(password, function(err, hash) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
if (userData.email !== undefined) {
|
||||
db.setObjectField('email:uid', userData.email, uid);
|
||||
if (parseInt(uid, 10) !== 1) {
|
||||
User.email.verify(uid, userData.email);
|
||||
}
|
||||
}
|
||||
|
||||
User.setUserField(uid, 'password', hash);
|
||||
plugins.fireHook('action:user.create', userData);
|
||||
db.incrObjectField('global', 'userCount');
|
||||
|
||||
db.sortedSetAdd('users:joindate', timestamp, uid);
|
||||
db.sortedSetAdd('users:postcount', 0, uid);
|
||||
db.sortedSetAdd('users:reputation', 0, uid);
|
||||
|
||||
groups.joinByGroupName('registered-users', uid);
|
||||
|
||||
if (password) {
|
||||
User.hashPassword(password, function(err, hash) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
User.setUserField(uid, 'password', hash);
|
||||
callback(null, uid);
|
||||
});
|
||||
} else {
|
||||
callback(null, uid);
|
||||
});
|
||||
} else {
|
||||
callback(null, uid);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -209,7 +212,7 @@ var bcrypt = require('bcryptjs'),
|
||||
settings = {}
|
||||
}
|
||||
|
||||
settings.showemail = settings.showemail ? parseInt(settings.showemail, 10) !== 0 : parseInt(meta.config.usePagination, 10) !== 0;
|
||||
settings.showemail = settings.showemail ? parseInt(settings.showemail, 10) !== 0 : false;
|
||||
settings.usePagination = settings.usePagination ? parseInt(settings.usePagination, 10) !== 0 : parseInt(meta.config.usePagination, 10) !== 0;
|
||||
settings.topicsPerPage = settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : parseInt(meta.config.topicsPerPage, 10) || 20;
|
||||
settings.postsPerPage = settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : parseInt(meta.config.postsPerPage, 10) || 10;
|
||||
|
||||
@@ -88,10 +88,10 @@ module.exports.server = server;
|
||||
property: 'keywords',
|
||||
content: meta.config.keywords || ''
|
||||
}],
|
||||
defaultLinkTags = [/*{
|
||||
defaultLinkTags = [{
|
||||
rel: 'apple-touch-icon',
|
||||
href: meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'
|
||||
}*/],
|
||||
href: '/apple-touch-icon'
|
||||
}],
|
||||
templateValues = {
|
||||
bootswatchCSS: meta.config['theme:src'],
|
||||
pluginCSS: plugins.cssFiles.map(function(file) { return { path: nconf.get('relative_path') + file.replace(/\\/g, '/') }; }),
|
||||
@@ -203,7 +203,17 @@ module.exports.server = server;
|
||||
|
||||
logger.init(app);
|
||||
|
||||
// favicon & apple-touch-icon middleware
|
||||
app.use(express.favicon(path.join(__dirname, '../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico')));
|
||||
app.use('/apple-touch-icon', function(req, res) {
|
||||
if (meta.config['brand:logo'] && validator.isURL(meta.config['brand:logo'])) {
|
||||
return res.redirect(meta.config['brand:logo']);
|
||||
} else {
|
||||
return res.sendfile(path.join(__dirname, '../public', meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'), {
|
||||
maxAge: app.enabled('cache') ? 5184000000 : 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.use(require('less-middleware')({
|
||||
src: path.join(__dirname, '../', 'public'),
|
||||
|
||||
66
src/widgets.js
Normal file
66
src/widgets.js
Normal file
@@ -0,0 +1,66 @@
|
||||
var async = require('async'),
|
||||
winston = require('winston'),
|
||||
plugins = require('./plugins'),
|
||||
db = require('./database'),
|
||||
templates = require('./../public/src/templates');
|
||||
|
||||
|
||||
(function(Widgets) {
|
||||
|
||||
Widgets.render = function(uid, area, callback) {
|
||||
if (!area.location || !area.template) {
|
||||
callback({
|
||||
error: 'Missing location and template data'
|
||||
});
|
||||
}
|
||||
|
||||
var rendered = [];
|
||||
|
||||
Widgets.getArea(area.template, area.location, function(err, widgets) {
|
||||
async.eachSeries(widgets, function(widget, next) {
|
||||
plugins.fireHook('filter:widget.render:' + widget.widget, {
|
||||
uid: uid,
|
||||
area: area,
|
||||
data: widget.data
|
||||
}, function(err, html){
|
||||
if (widget.data.container && widget.data.container.match('{body}')) {
|
||||
html = templates.prepare(widget.data.container).parse({
|
||||
title: widget.data.title,
|
||||
body: html
|
||||
});
|
||||
}
|
||||
|
||||
rendered.push({
|
||||
html: html
|
||||
});
|
||||
|
||||
next(err);
|
||||
});
|
||||
}, function(err) {
|
||||
callback(err, rendered);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Widgets.getArea = function(template, location, callback) {
|
||||
db.getObjectField('widgets:' + template, location, function(err, widgets) {
|
||||
if (!widgets) {
|
||||
return callback(err, []);
|
||||
}
|
||||
callback(err, JSON.parse(widgets));
|
||||
})
|
||||
};
|
||||
|
||||
Widgets.setArea = function(area, callback) {
|
||||
if (!area.location || !area.template) {
|
||||
callback({
|
||||
error: 'Missing location and template data'
|
||||
});
|
||||
}
|
||||
|
||||
db.setObjectField('widgets:' + area.template, area.location, JSON.stringify(area.widgets), function(err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
}(exports));
|
||||
Reference in New Issue
Block a user