Baris Soner Usakli 11 years ago
commit 74b0da78f4

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

@ -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;
app.refreshTitle(url);
$(window).trigger('action:ajaxify.end', { url: url });
var widgetLocations = [];
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;

@ -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>&nbsp;<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;
});

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

@ -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">&nbsp;&nbsp;</button>
<button data-class="panel-primary" class="btn btn-xs btn-primary">&nbsp;&nbsp;</button>
<button data-class="panel-success" class="btn btn-xs btn-success">&nbsp;&nbsp;</button>
<button data-class="panel-info" class="btn btn-xs btn-info">&nbsp;&nbsp;</button>
<button data-class="panel-warning" class="btn btn-xs btn-warning">&nbsp;&nbsp;</button>
<button data-class="panel-danger" class="btn btn-xs btn-danger">&nbsp;&nbsp;</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">&nbsp;&nbsp;</button>
<button data-class="alert-info" class="btn btn-xs btn-info">&nbsp;&nbsp;</button>
<button data-class="alert-warning" class="btn btn-xs btn-warning">&nbsp;&nbsp;</button>
<button data-class="alert-danger" class="btn btn-xs btn-danger">&nbsp;&nbsp;</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -97,40 +97,10 @@
</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>

@ -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>
<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 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_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 -->

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

@ -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>&nbsp;[[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>&nbsp;[[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>&nbsp;[[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>&nbsp;[[global:motd.follow]]</span>\
</a>\
</div>\
', function(motd) {
motdString = motd;
assemble();
});
} else {
motdString = meta.config.motd;
assemble();
}
res.json(data);
});
});
});

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

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

@ -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, 20).getTime();
db.get('schemaDate', function(err, value) {
if (parseInt(value, 10) >= latestSchema) {
@ -685,6 +685,132 @@ 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, 20).getTime();
if (schemaDate < thisSchemaDate) {
updatesMade = true;
db.setAdd('plugins:active', 'nodebb-widget-essentials', function(err) {
winston.info('[2014/2/20] Activating NodeBB Essential Widgets');
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!!!

@ -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));
Loading…
Cancel
Save