Baris Usakli 12 years ago
commit f5619a9b29

@ -3,13 +3,14 @@
**NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8. **NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8.
* [NodeBB Homepage](http://www.nodebb.org/ "NodeBB") * [NodeBB Homepage](http://www.nodebb.org/ "NodeBB")
* [Demo & Meta Discussion](http://try.nodebb.org)
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") * [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
![NodeBB Main Category Listing](http://i.imgur.com/ZBrHqLr.png) ![NodeBB Main Category Listing](http://i.imgur.com/zffCFoh.png)
![NodeBB Topic Page](http://i.imgur.com/YSBA6Vr.png) ![NodeBB Topic Page](http://i.imgur.com/tcHW08M.png)
## How can I follow along/contribute? ## How can I follow along/contribute?

@ -108,6 +108,9 @@
}); });
} else if (nconf.get('upgrade')) { } else if (nconf.get('upgrade')) {
nconf.file({
file: __dirname + '/config.json'
});
meta = require('./src/meta.js'); meta = require('./src/meta.js');
meta.configs.init(function () { meta.configs.init(function () {

@ -2,7 +2,7 @@
"name": "nodebb", "name": "nodebb",
"license": "GPLv3 or later", "license": "GPLv3 or later",
"description": "NodeBB Forum", "description": "NodeBB Forum",
"version": "0.0.6", "version": "0.0.7",
"homepage": "http://www.nodebb.org", "homepage": "http://www.nodebb.org",
"repository": { "repository": {
"type": "git", "type": "git",
@ -34,8 +34,8 @@
"request": "~2.25.0", "request": "~2.25.0",
"reds": "~0.2.4", "reds": "~0.2.4",
"winston": "~0.7.2", "winston": "~0.7.2",
"nodebb-plugin-mentions": "~0.1.0", "nodebb-plugin-mentions": "~0.1.9",
"nodebb-plugin-markdown": "~0.1.0", "nodebb-plugin-markdown": "~0.1.2",
"rss": "~0.2.0", "rss": "~0.2.0",
"prompt": "~0.2.11", "prompt": "~0.2.11",
"uglify-js": "~2.4.0", "uglify-js": "~2.4.0",
@ -53,15 +53,22 @@
"contributors": [ "contributors": [
{ {
"name": "Andrew Rodrigues", "name": "Andrew Rodrigues",
"email": "andrew@designcreateplay.com" "email": "andrew@designcreateplay.com",
"url": "https://github.com/psychobunny"
}, },
{ {
"name": "Julian Lam", "name": "Julian Lam",
"email": "julian@designcreateplay.com" "email": "julian@designcreateplay.com",
"url": "https://github.com/julianlam"
}, },
{ {
"name": "Barış Soner Uşaklı", "name": "Barış Soner Uşaklı",
"email": "baris@designcreateplay.com" "email": "baris@designcreateplay.com",
"url": "https://github.com/barisusakli"
},
{
"name": "Andrew Darqui",
"url": "https://github.com/adarqui"
}, },
{ {
"name": "Damian Bushong", "name": "Damian Bushong",
@ -70,6 +77,10 @@
{ {
"name": "Matt Smith", "name": "Matt Smith",
"url": "https://github.com/soimafreak" "url": "https://github.com/soimafreak"
},
{
"name": "Quinton Marchi",
"url": "https://github.com/iamcardinal"
} }
] ]
} }

@ -76,6 +76,9 @@ var ajaxify = {};
templates.flush(); templates.flush();
templates.load_template(function () { templates.load_template(function () {
exec_body_scripts(content); exec_body_scripts(content);
require(['forum/' + tpl_url], function(script) {
if (script && script.init) script.init();
});
if (callback) { if (callback) {
callback(); callback();
@ -127,10 +130,13 @@ var ajaxify = {};
} }
} else if (window.location.pathname !== '/outgoing') { } else if (window.location.pathname !== '/outgoing') {
// External Link // External Link
if (config.useOutgoingLinksPage == true) {
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href)); ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
e.preventDefault(); e.preventDefault();
} }
} }
}
}); });
}); });

@ -17,7 +17,7 @@ var socket,
socket.socket.connect(); socket.socket.connect();
}, 200); }, 200);
} else { } else {
socket = io.connect(config.socket.address); socket = io.connect(RELATIVE_PATH);
var reconnecting = false, var reconnecting = false,
reconnectEl, reconnectTimer; reconnectEl, reconnectTimer;

@ -1,4 +1,9 @@
(function() { define(['forum/accountheader'], function(header) {
var Account = {};
Account.init = function() {
header.init();
var yourid = templates.get('yourid'), var yourid = templates.get('yourid'),
theirid = templates.get('theirid'), theirid = templates.get('theirid'),
isFollowing = templates.get('isFollowing'); isFollowing = templates.get('isFollowing');
@ -59,9 +64,21 @@
ajaxify.go($(this).attr('topic-url')); ajaxify.go($(this).attr('topic-url'));
}); });
socket.on('api:user.isOnline', Account.handleUserOnline);
socket.emit('api:user.isOnline', theirid, Account.handleUserOnline);
socket.on('event:new_post', function(data) {
var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
$('.user-recent-posts').prepend(html);
});
});
};
Account.handleUserOnline = function(data) {
var onlineStatus = $('.account-online-status'); var onlineStatus = $('.account-online-status');
function handleUserOnline(data) {
if (data.online) { if (data.online) {
onlineStatus.find('span span').text('online'); onlineStatus.find('span span').text('online');
onlineStatus.find('i').attr('class', 'icon-circle'); onlineStatus.find('i').attr('class', 'icon-circle');
@ -69,17 +86,7 @@
onlineStatus.find('span span').text('offline'); onlineStatus.find('span span').text('offline');
onlineStatus.find('i').attr('class', 'icon-circle-blank'); onlineStatus.find('i').attr('class', 'icon-circle-blank');
} }
} };
socket.on('api:user.isOnline', handleUserOnline);
socket.emit('api:user.isOnline', theirid, handleUserOnline);
socket.on('event:new_post', function(data) {
var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
$('.user-recent-posts').prepend(html);
});
return Account;
}); });
}());

@ -1,19 +1,21 @@
var gravatarPicture = templates.get('gravatarpicture'); define(['forum/accountheader'], function(header) {
var uploadedPicture = templates.get('uploadedpicture'); var AccountEdit = {};
$(document).ready(function() {
AccountEdit.init = function() {
header.init();
var gravatarPicture = templates.get('gravatarpicture');
var uploadedPicture = templates.get('uploadedpicture');
$('#uploadForm').submit(function() { $('#uploadForm').submit(function() {
status('uploading the file ...'); AccountEdit.status('uploading the file ...');
$('#upload-progress-bar').css('width', '0%'); $('#upload-progress-bar').css('width', '0%');
$('#upload-progress-box').show(); $('#upload-progress-box').show();
$('#upload-progress-box').removeClass('hide'); $('#upload-progress-box').removeClass('hide');
if (!$('#userPhotoInput').val()) { if (!$('#userPhotoInput').val()) {
error('select an image to upload!'); AccountEdit.error('select an image to upload!');
return false; return false;
} }
@ -23,7 +25,7 @@ $(document).ready(function() {
$(this).ajaxSubmit({ $(this).ajaxSubmit({
error: function(xhr) { error: function(xhr) {
error('Error: ' + xhr.status); AccountEdit.error('Error: ' + xhr.status);
}, },
uploadProgress: function(event, position, total, percent) { uploadProgress: function(event, position, total, percent) {
@ -34,7 +36,7 @@ $(document).ready(function() {
success: function(response) { success: function(response) {
if (response.error) { if (response.error) {
error(response.error); AccountEdit.error(response.error);
return; return;
} }
@ -46,54 +48,20 @@ $(document).ready(function() {
uploadedPicture = imageUrlOnServer; uploadedPicture = imageUrlOnServer;
setTimeout(function() { setTimeout(function() {
hideAlerts(); AccountEdit.hideAlerts();
$('#upload-picture-modal').modal('hide'); $('#upload-picture-modal').modal('hide');
}, 750); }, 750);
socket.emit('api:updateHeader', { socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug'] fields: ['username', 'picture', 'userslug']
}); });
success('File uploaded successfully!'); AccountEdit.success('File uploaded successfully!');
} }
}); });
return false; return false;
}); });
function hideAlerts() {
$('#alert-status').addClass('hide');
$('#alert-success').addClass('hide');
$('#alert-error').addClass('hide');
$('#upload-progress-box').addClass('hide');
}
function status(message) {
hideAlerts();
$('#alert-status').text(message).removeClass('hide');
}
function success(message) {
hideAlerts();
$('#alert-success').text(message).removeClass('hide');
}
function error(message) {
hideAlerts();
$('#alert-error').text(message).removeClass('hide');
}
function changeUserPicture(type) {
var userData = {
type: type
};
socket.emit('api:user.changePicture', userData, function(success) {
if (!success) {
app.alertError('There was an error changing picture!');
}
});
}
var selectedImageType = ''; var selectedImageType = '';
$('#submitBtn').on('click', function() { $('#submitBtn').on('click', function() {
@ -126,37 +94,9 @@ $(document).ready(function() {
return false; return false;
}); });
function updateImages() {
var currentPicture = $('#user-current-picture').attr('src');
if (gravatarPicture) {
$('#user-gravatar-picture').attr('src', gravatarPicture);
$('#gravatar-box').show();
} else
$('#gravatar-box').hide();
if (uploadedPicture) {
$('#user-uploaded-picture').attr('src', uploadedPicture);
$('#uploaded-box').show();
} else
$('#uploaded-box').hide();
if (currentPicture == gravatarPicture)
$('#gravatar-box .icon-ok').show();
else
$('#gravatar-box .icon-ok').hide();
if (currentPicture == uploadedPicture)
$('#uploaded-box .icon-ok').show();
else
$('#uploaded-box .icon-ok').hide();
}
$('#changePictureBtn').on('click', function() { $('#changePictureBtn').on('click', function() {
selectedImageType = ''; selectedImageType = '';
updateImages(); AccountEdit.updateImages();
$('#change-picture-modal').modal('show'); $('#change-picture-modal').modal('show');
$('#change-picture-modal').removeClass('hide'); $('#change-picture-modal').removeClass('hide');
@ -180,7 +120,7 @@ $(document).ready(function() {
$('#change-picture-modal').modal('hide'); $('#change-picture-modal').modal('hide');
if (selectedImageType) { if (selectedImageType) {
changeUserPicture(selectedImageType); AccountEdit.changeUserPicture(selectedImageType);
if (selectedImageType == 'gravatar') if (selectedImageType == 'gravatar')
$('#user-current-picture').attr('src', gravatarPicture); $('#user-current-picture').attr('src', gravatarPicture);
@ -200,7 +140,7 @@ $(document).ready(function() {
$('#upload-picture-modal').modal('show'); $('#upload-picture-modal').modal('show');
$('#upload-picture-modal').removeClass('hide'); $('#upload-picture-modal').removeClass('hide');
hideAlerts(); AccountEdit.hideAlerts();
return false; return false;
}); });
@ -288,4 +228,70 @@ $(document).ready(function() {
}); });
}()); }());
};
AccountEdit.hideAlerts = function() {
$('#alert-status').addClass('hide');
$('#alert-success').addClass('hide');
$('#alert-error').addClass('hide');
$('#upload-progress-box').addClass('hide');
}
AccountEdit.status = function(message) {
AccountEdit.hideAlerts();
$('#alert-status').text(message).removeClass('hide');
}
AccountEdit.success = function(message) {
AccountEdit.hideAlerts();
$('#alert-success').text(message).removeClass('hide');
}
AccountEdit.error = function(message) {
AccountEdit.hideAlerts();
$('#alert-error').text(message).removeClass('hide');
}
AccountEdit.changeUserPicture = function(type) {
var userData = {
type: type
};
socket.emit('api:user.changePicture', userData, function(success) {
if (!success) {
app.alertError('There was an error changing picture!');
}
});
}
AccountEdit.updateImages = function() {
var currentPicture = $('#user-current-picture').attr('src');
var gravatarPicture = templates.get('gravatarpicture');
var uploadedPicture = templates.get('uploadedpicture');
if (gravatarPicture) {
$('#user-gravatar-picture').attr('src', gravatarPicture);
$('#gravatar-box').show();
} else
$('#gravatar-box').hide();
if (uploadedPicture) {
$('#user-uploaded-picture').attr('src', uploadedPicture);
$('#uploaded-box').show();
} else
$('#uploaded-box').hide();
if (currentPicture == gravatarPicture)
$('#gravatar-box .icon-ok').show();
else
$('#gravatar-box .icon-ok').hide();
if (currentPicture == uploadedPicture)
$('#uploaded-box .icon-ok').show();
else
$('#uploaded-box .icon-ok').hide();
}
return AccountEdit;
}); });

@ -1,24 +1,11 @@
(function() { define(function() {
var AccountHeader = {};
AccountHeader.init = function() {
var yourid = templates.get('yourid'), var yourid = templates.get('yourid'),
theirid = templates.get('theirid'); theirid = templates.get('theirid');
AccountHeader.createMenu();
function createMenu() {
var userslug = $('.account-username-box').attr('data-userslug');
var links = $('<div class="account-sub-links inline-block pull-right">\
<span id="settingsLink" class="pull-right"><a href="/user/' + userslug + '/settings">settings</a></span>\
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
</div>');
$('.account-username-box').append(links);
}
$(document).ready(function() {
createMenu();
var editLink = $('#editLink'); var editLink = $('#editLink');
var settingsLink = $('#settingsLink'); var settingsLink = $('#settingsLink');
@ -37,6 +24,20 @@
return false; return false;
} }
}); });
}); }
}()); AccountHeader.createMenu = function() {
var userslug = $('.account-username-box').attr('data-userslug');
var links = $('<div class="account-sub-links inline-block pull-right">\
<span id="settingsLink" class="pull-right"><a href="/user/' + userslug + '/settings">settings</a></span>\
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
</div>');
$('.account-username-box').append(links);
}
return AccountHeader;
});

@ -1,4 +1,8 @@
$(document).ready(function() { define(['forum/accountheader'], function(header) {
var AccountSettings = {};
AccountSettings.init = function() {
header.init();
$('#submitBtn').on('click', function() { $('#submitBtn').on('click', function() {
@ -15,5 +19,7 @@ $(document).ready(function() {
}); });
return false; return false;
}); });
};
return AccountSettings;
}); });

@ -1,3 +1,7 @@
define(function() {
var Categories = {};
Categories.init = function() {
var modified_categories = {}; var modified_categories = {};
function modified(el) { function modified(el) {
@ -49,7 +53,6 @@ jQuery('.blockclass').each(function() {
//DRY Failure. this needs to go into an ajaxify onready style fn. Currently is copy pasted into every single function so after ACP is off the ground fix asap //DRY Failure. this needs to go into an ajaxify onready style fn. Currently is copy pasted into every single function so after ACP is off the ground fix asap
(function() {
function showCreateCategoryModal() { function showCreateCategoryModal() {
$('#new-category-modal').modal(); $('#new-category-modal').modal();
} }
@ -135,5 +138,7 @@ jQuery('.blockclass').each(function() {
}); });
}); });
};
}()); return Categories;
});

@ -1,78 +1,3 @@
var nodebb_admin = (function(nodebb_admin) {
nodebb_admin.config = undefined;
nodebb_admin.prepare = function() {
// Come back in 500ms if the config isn't ready yet
if (nodebb_admin.config === undefined) {
setTimeout(function() {
nodebb_admin.prepare();
}, 500);
return;
}
// Populate the fields on the page from the config
var fields = document.querySelectorAll('#content [data-field]'),
numFields = fields.length,
saveBtn = document.getElementById('save'),
x, key, inputType;
for (x = 0; x < numFields; x++) {
key = fields[x].getAttribute('data-field');
inputType = fields[x].getAttribute('type');
if (fields[x].nodeName === 'INPUT') {
if (nodebb_admin.config[key]) {
switch (inputType) {
case 'text':
case 'textarea':
case 'number':
fields[x].value = nodebb_admin.config[key];
break;
case 'checkbox':
fields[x].checked = nodebb_admin.config[key] === '1' ? true : false;
break;
}
}
} else if (fields[x].nodeName === 'TEXTAREA') {
if (nodebb_admin.config[key]) fields[x].value = nodebb_admin.config[key];
}
}
saveBtn.addEventListener('click', function(e) {
var key, value;
e.preventDefault();
for (x = 0; x < numFields; x++) {
key = fields[x].getAttribute('data-field');
if (fields[x].nodeName === 'INPUT') {
inputType = fields[x].getAttribute('type');
switch (inputType) {
case 'text':
case 'number':
value = fields[x].value;
break;
case 'checkbox':
value = fields[x].checked ? '1' : '0';
break;
}
} else if (fields[x].nodeName === 'TEXTAREA') {
value = fields[x].value;
}
socket.emit('api:config.set', {
key: key,
value: value
});
}
});
}
nodebb_admin.remove = function(key) {
socket.emit('api:config.remove', key);
}
jQuery('document').ready(function() { jQuery('document').ready(function() {
// On menu click, change "active" state // On menu click, change "active" state
var menuEl = document.querySelector('.sidebar-nav'), var menuEl = document.querySelector('.sidebar-nav'),
@ -91,7 +16,9 @@ var nodebb_admin = (function(nodebb_admin) {
}); });
socket.once('api:config.get', function(config) { socket.once('api:config.get', function(config) {
nodebb_admin.config = config; require(['forum/admin/settings'], function(Settings) {
Settings.config = config;
});
}); });
socket.emit('api:config.get'); socket.emit('api:config.get');
@ -115,7 +42,3 @@ var nodebb_admin = (function(nodebb_admin) {
}); });
} }
}); });
return nodebb_admin;
}(nodebb_admin || {}));

@ -1,4 +1,7 @@
$(document).ready(function() { define(function() {
var Groups = {};
Groups.init = function() {
var createEl = document.getElementById('create'), var createEl = document.getElementById('create'),
createModal = $('#create-modal'), createModal = $('#create-modal'),
createSubmitBtn = document.getElementById('create-modal-go'), createSubmitBtn = document.getElementById('create-modal-go'),
@ -191,4 +194,7 @@ $(document).ready(function() {
} }
}); });
}); });
};
return Groups;
}); });

@ -1,6 +1,7 @@
define(function() {
var Admin = {};
(function() { Admin.init = function() {
ajaxify.register_events(['api:get_all_rooms']); ajaxify.register_events(['api:get_all_rooms']);
socket.on('api:get_all_rooms', function(data) { socket.on('api:get_all_rooms', function(data) {
@ -21,5 +22,7 @@
app.enter_room('admin'); app.enter_room('admin');
socket.emit('api:get_all_rooms'); socket.emit('api:get_all_rooms');
};
}()); return Admin;
});

@ -1,7 +1,5 @@
var nodebb_admin = nodebb_admin || {}; define(function() {
var Plugins = {
(function() {
var plugins = {
init: function() { init: function() {
var pluginsList = $('.plugins'), var pluginsList = $('.plugins'),
numPlugins = pluginsList[0].querySelectorAll('li').length, numPlugins = pluginsList[0].querySelectorAll('li').length,
@ -31,8 +29,5 @@ var nodebb_admin = nodebb_admin || {};
} }
}; };
jQuery(document).ready(function() { return Plugins;
nodebb_admin.plugins = plugins;
nodebb_admin.plugins.init();
}); });
})();

@ -0,0 +1,81 @@
define(function() {
var Settings = {};
Settings.config = {};
Settings.init = function() {
Settings.prepare();
};
Settings.prepare = function() {
// Come back in 500ms if the config isn't ready yet
if (Settings.config === undefined) {
setTimeout(function() {
Settings.prepare();
}, 500);
return;
}
// Populate the fields on the page from the config
var fields = document.querySelectorAll('#content [data-field]'),
numFields = fields.length,
saveBtn = document.getElementById('save'),
x, key, inputType;
for (x = 0; x < numFields; x++) {
key = fields[x].getAttribute('data-field');
inputType = fields[x].getAttribute('type');
if (fields[x].nodeName === 'INPUT') {
if (Settings.config[key]) {
switch (inputType) {
case 'text':
case 'textarea':
case 'number':
fields[x].value = Settings.config[key];
break;
case 'checkbox':
fields[x].checked = Settings.config[key] === '1' ? true : false;
break;
}
}
} else if (fields[x].nodeName === 'TEXTAREA') {
if (Settings.config[key]) fields[x].value = Settings.config[key];
}
}
saveBtn.addEventListener('click', function(e) {
var key, value;
e.preventDefault();
for (x = 0; x < numFields; x++) {
key = fields[x].getAttribute('data-field');
if (fields[x].nodeName === 'INPUT') {
inputType = fields[x].getAttribute('type');
switch (inputType) {
case 'text':
case 'number':
value = fields[x].value;
break;
case 'checkbox':
value = fields[x].checked ? '1' : '0';
break;
}
} else if (fields[x].nodeName === 'TEXTAREA') {
value = fields[x].value;
}
socket.emit('api:config.set', {
key: key,
value: value
});
}
});
};
Settings.remove = function(key) {
socket.emit('api:config.remove', key);
};
return Settings;
});

@ -1,43 +1,9 @@
var nodebb_admin = (function(nodebb_admin) { define(function() {
var Themes = {};
var themes = {}; Themes.init = function() {
themes.render = function(bootswatch) {
var themeFrag = document.createDocumentFragment(),
themeEl = document.createElement('li'),
themeContainer = document.querySelector('#bootstrap_themes'),
numThemes = bootswatch.themes.length;
for (var x = 0; x < numThemes; x++) {
var theme = bootswatch.themes[x];
themeEl.setAttribute('data-css', theme.cssMin);
themeEl.setAttribute('data-theme', theme.name);
themeEl.innerHTML = '<img src="' + theme.thumbnail + '" />' +
'<div>' +
'<div class="pull-right">' +
'<button class="btn btn-primary" data-action="use">Use</button> ' +
'<button class="btn btn-default" data-action="preview">Preview</button>' +
'</div>' +
'<h4>' + theme.name + '</h4>' +
'<p>' + theme.description + '</p>' +
'</div>' +
'<div class="clear">';
themeFrag.appendChild(themeEl.cloneNode(true));
}
themeContainer.innerHTML = '';
themeContainer.appendChild(themeFrag);
}
nodebb_admin.themes = themes;
return nodebb_admin;
}(nodebb_admin || {}));
(function() {
var scriptEl = document.createElement('script'); var scriptEl = document.createElement('script');
scriptEl.src = 'http://api.bootswatch.com/3/?callback=nodebb_admin.themes.render'; scriptEl.src = 'http://api.bootswatch.com/3/?callback=bootswatchListener';
document.body.appendChild(scriptEl); document.body.appendChild(scriptEl);
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'), var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
@ -74,8 +40,10 @@ var nodebb_admin = (function(nodebb_admin) {
revertEl.addEventListener('click', function() { revertEl.addEventListener('click', function() {
bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) { bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) {
if (confirm) { if (confirm) {
nodebb_admin.remove('theme:id'); require(['forum/admin/settings'], function(Settings) {
nodebb_admin.remove('theme:src'); Settings.remove('theme:id');
Settings.remove('theme:src');
});
} }
}); });
}, false); }, false);
@ -115,4 +83,33 @@ var nodebb_admin = (function(nodebb_admin) {
instListEl.innerHTML = ''; instListEl.innerHTML = '';
instListEl.appendChild(themeFrag); instListEl.appendChild(themeFrag);
}); });
})(); }
Themes.render = function(bootswatch) {
var themeFrag = document.createDocumentFragment(),
themeEl = document.createElement('li'),
themeContainer = document.querySelector('#bootstrap_themes'),
numThemes = bootswatch.themes.length;
for (var x = 0; x < numThemes; x++) {
var theme = bootswatch.themes[x];
themeEl.setAttribute('data-css', theme.cssMin);
themeEl.setAttribute('data-theme', theme.name);
themeEl.innerHTML = '<img src="' + theme.thumbnail + '" />' +
'<div>' +
'<div class="pull-right">' +
'<button class="btn btn-primary" data-action="use">Use</button> ' +
'<button class="btn btn-default" data-action="preview">Preview</button>' +
'</div>' +
'<h4>' + theme.name + '</h4>' +
'<p>' + theme.description + '</p>' +
'</div>' +
'<div class="clear">';
themeFrag.appendChild(themeEl.cloneNode(true));
}
themeContainer.innerHTML = '';
themeContainer.appendChild(themeFrag);
}
return Themes;
});

@ -1,4 +1,7 @@
$(document).ready(function() { define(function() {
var Topics = {};
Topics.init = function() {
var topicsListEl = document.querySelector('.topics'), var topicsListEl = document.querySelector('.topics'),
loadMoreEl = document.getElementById('topics_loadmore'); loadMoreEl = document.getElementById('topics_loadmore');
@ -76,7 +79,6 @@ $(document).ready(function() {
topicEls[x].removeAttribute('data-locked'); topicEls[x].removeAttribute('data-locked');
topicEls[x].removeAttribute('data-deleted'); topicEls[x].removeAttribute('data-deleted');
} }
});
socket.on('api:topic.pin', function(response) { socket.on('api:topic.pin', function(response) {
if (response.status === 'ok') { if (response.status === 'ok') {
@ -125,3 +127,7 @@ socket.on('api:topic.restore', function(response) {
$(btnEl).removeClass('active'); $(btnEl).removeClass('active');
} }
}); });
};
return Topics;
});

@ -1,5 +1,7 @@
(function() { define(function() {
var Users = {};
Users.init = function() {
var yourid = templates.get('yourid'); var yourid = templates.get('yourid');
function isUserAdmin(element) { function isUserAdmin(element) {
@ -166,5 +168,7 @@
}); });
}); });
};
}()); return Users;
});

@ -1,4 +1,7 @@
(function () { define(function () {
var Category = {};
Category.init = function() {
var cid = templates.get('category_id'), var cid = templates.get('category_id'),
room = 'category_' + cid, room = 'category_' + cid,
twitterEl = document.getElementById('twitter-intent'), twitterEl = document.getElementById('twitter-intent'),
@ -35,36 +38,7 @@
'event:new_topic' 'event:new_topic'
]); ]);
function onNewTopic(data) { socket.on('event:new_topic', Category.onNewTopic);
var html = templates.prepare(templates['category'].blocks['topics']).parse({
topics: [data]
}),
topic = $(html),
container = $('#topics-container'),
topics = $('#topics-container').children(),
numTopics = topics.length;
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove();
if (numTopics > 0) {
for (var x = 0; x < numTopics; x++) {
if ($(topics[x]).find('.icon-pushpin').length)
continue;
topic.insertBefore(topics[x]);
topic.hide().fadeIn('slow');
break;
}
} else {
container.append(topic);
topic.hide().fadeIn('slow');
}
socket.emit('api:categories.getRecentReplies', cid);
$('#topics-container span.timeago').timeago();
}
socket.on('event:new_topic', onNewTopic);
socket.emit('api:categories.getRecentReplies', cid); socket.emit('api:categories.getRecentReplies', cid);
socket.on('api:categories.getRecentReplies', function (posts) { socket.on('api:categories.getRecentReplies', function (posts) {
@ -97,7 +71,45 @@
$('#category_recent_replies span.timeago').timeago(); $('#category_recent_replies span.timeago').timeago();
}); });
function onTopicsLoaded(topics) { $(window).off('scroll').on('scroll', function (ev) {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
Category.loadMoreTopics(cid);
}
});
};
Category.onNewTopic = function(data) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({
topics: [data]
}),
topic = $(html),
container = $('#topics-container'),
topics = $('#topics-container').children(),
numTopics = topics.length;
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove();
if (numTopics > 0) {
for (var x = 0; x < numTopics; x++) {
if ($(topics[x]).find('.icon-pushpin').length)
continue;
topic.insertBefore(topics[x]);
topic.hide().fadeIn('slow');
break;
}
} else {
container.append(topic);
topic.hide().fadeIn('slow');
}
socket.emit('api:categories.getRecentReplies', cid);
$('#topics-container span.timeago').timeago();
}
Category.onTopicsLoaded = function(topics) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({ var html = templates.prepare(templates['category'].blocks['topics']).parse({
topics: topics topics: topics
@ -113,26 +125,18 @@
} }
function loadMoreTopics(cid) { Category.loadMoreTopics = function(cid) {
loadingMoreTopics = true; loadingMoreTopics = true;
socket.emit('api:category.loadMore', { socket.emit('api:category.loadMore', {
cid: cid, cid: cid,
after: $('#topics-container').children().length after: $('#topics-container').children().length
}, function (data) { }, function (data) {
if (data.topics.length) { if (data.topics.length) {
onTopicsLoaded(data.topics); Category.onTopicsLoaded(data.topics);
} }
loadingMoreTopics = false; loadingMoreTopics = false;
}); });
} }
$(window).off('scroll').on('scroll', function (ev) { return Category;
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics(cid);
}
}); });
})();

@ -1,7 +1,13 @@
(function() { define(['forum/accountheader'], function(header) {
$(document).ready(function() { var AccountHeader = {};
AccountHeader.init = function() {
header.init();
$('.user-favourite-posts .topic-row').on('click', function() { $('.user-favourite-posts .topic-row').on('click', function() {
ajaxify.go($(this).attr('topic-url')); ajaxify.go($(this).attr('topic-url'));
}); });
};
return AccountHeader;
}); });
}());

@ -1,18 +1,20 @@
(function() { define(['forum/accountheader'], function(header) {
var Followers = {};
Followers.init = function() {
header.init();
var yourid = templates.get('yourid'), var yourid = templates.get('yourid'),
theirid = templates.get('theirid'), theirid = templates.get('theirid'),
followersCount = templates.get('followersCount'); followersCount = templates.get('followersCount');
$(document).ready(function() {
if (parseInt(followersCount, 10) === 0) { if (parseInt(followersCount, 10) === 0) {
$('#no-followers-notice').removeClass('hide'); $('#no-followers-notice').removeClass('hide');
} }
app.addCommasToNumbers(); app.addCommasToNumbers();
};
return Followers;
}); });
}());

@ -1,11 +1,13 @@
(function() { define(['forum/accountheader'], function(header) {
var Following = {};
Following.init = function() {
header.init();
var yourid = templates.get('yourid'), var yourid = templates.get('yourid'),
theirid = templates.get('theirid'), theirid = templates.get('theirid'),
followingCount = templates.get('followingCount'); followingCount = templates.get('followingCount');
$(document).ready(function() {
if (parseInt(followingCount, 10) === 0) { if (parseInt(followingCount, 10) === 0) {
$('#no-following-notice').removeClass('hide'); $('#no-following-notice').removeClass('hide');
} }
@ -34,7 +36,7 @@
} }
app.addCommasToNumbers(); app.addCommasToNumbers();
}); };
return Following;
}()); });

@ -1,4 +1,7 @@
(function() { define(function() {
var Login = {};
Login.init = function() {
// Alternate Logins // Alternate Logins
var altLoginEl = document.querySelector('.alt-logins'); var altLoginEl = document.querySelector('.alt-logins');
altLoginEl.addEventListener('click', function(e) { altLoginEl.addEventListener('click', function(e) {
@ -57,4 +60,7 @@
}); });
document.querySelector('#content input').focus(); document.querySelector('#content input').focus();
}()); };
return Login;
});

@ -1,6 +1,11 @@
(function() { define(function() {
var loadingMoreTopics = false; var Recent = {};
Recent.newTopicCount = 0;
Recent.newPostCount = 0;
Recent.loadingMoreTopics = false;
Recent.init = function() {
app.enter_room('recent_posts'); app.enter_room('recent_posts');
ajaxify.register_events([ ajaxify.register_events([
@ -8,33 +13,44 @@
'event:new_post' 'event:new_post'
]); ]);
var newTopicCount = 0,
newPostCount = 0;
$('#new-topics-alert').on('click', function() { $('#new-topics-alert').on('click', function() {
$(this).hide(); $(this).hide();
}); });
socket.on('event:new_topic', function(data) { socket.on('event:new_topic', function(data) {
++newTopicCount; ++Recent.newTopicCount;
updateAlertText(); Recent.updateAlertText();
});
socket.on('event:new_post', function(data) {
++Recent.newPostCount;
Recent.updateAlertText();
});
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !Recent.loadingMoreTopics) {
Recent.loadMoreTopics();
}
}); });
};
function updateAlertText() { Recent.updateAlertText = function() {
var text = ''; var text = '';
if (newTopicCount > 1) if (Recent.newTopicCount > 1)
text = 'There are ' + newTopicCount + ' new topics'; text = 'There are ' + Recent.newTopicCount + ' new topics';
else if (newTopicCount === 1) else if (Recent.newTopicCount === 1)
text = 'There is 1 new topic'; text = 'There is 1 new topic';
else else
text = 'There are no new topics'; text = 'There are no new topics';
if (newPostCount > 1) if (Recent.newPostCount > 1)
text += ' and ' + newPostCount + ' new posts.'; text += ' and ' + Recent.newPostCount + ' new posts.';
else if (newPostCount === 1) else if (Recent.newPostCount === 1)
text += ' and 1 new post.'; text += ' and 1 new post.';
else else
text += ' and no new posts.'; text += ' and no new posts.';
@ -44,12 +60,7 @@
$('#new-topics-alert').html(text).fadeIn('slow'); $('#new-topics-alert').html(text).fadeIn('slow');
} }
socket.on('event:new_post', function(data) { Recent.onTopicsLoaded = function(topics) {
++newPostCount;
updateAlertText();
});
function onTopicsLoaded(topics) {
var html = templates.prepare(templates['recent'].blocks['topics']).parse({ var html = templates.prepare(templates['recent'].blocks['topics']).parse({
topics: topics topics: topics
@ -61,25 +72,17 @@
container.append(html); container.append(html);
} }
function loadMoreTopics() { Recent.loadMoreTopics = function() {
loadingMoreTopics = true; Recent.loadingMoreTopics = true;
socket.emit('api:topics.loadMoreRecentTopics', { socket.emit('api:topics.loadMoreRecentTopics', {
after: $('#topics-container').children().length after: $('#topics-container').children().length
}, function(data) { }, function(data) {
if (data.topics && data.topics.length) { if (data.topics && data.topics.length) {
onTopicsLoaded(data.topics); Recent.onTopicsLoaded(data.topics);
} }
loadingMoreTopics = false; Recent.loadingMoreTopics = false;
}); });
} }
$(window).off('scroll').on('scroll', function() { return Recent;
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics();
}
}); });
})();

@ -1,4 +1,7 @@
(function() { define(function() {
var Register = {};
Register.init = function() {
var username = $('#username'), var username = $('#username'),
password = $('#password'), password = $('#password'),
password_confirm = $('#password-confirm'), password_confirm = $('#password-confirm'),
@ -150,5 +153,7 @@
register.on('click', function(e) { register.on('click', function(e) {
if (validateForm()) e.preventDefault(); if (validateForm()) e.preventDefault();
}); });
};
}()); return Register;
});

@ -1,4 +1,7 @@
(function() { define(function() {
var ResetPassword = {};
ResetPassword.init = function() {
var inputEl = document.getElementById('email'), var inputEl = document.getElementById('email'),
errorEl = document.getElementById('error'), errorEl = document.getElementById('error'),
errorTextEl = errorEl.querySelector('p'); errorTextEl = errorEl.querySelector('p');
@ -38,4 +41,7 @@
} }
} }
}); });
}()); };
return ResetPassword;
});

@ -1,4 +1,7 @@
(function() { define(function() {
var ResetCode = {};
ResetCode.init = function() {
var reset_code = templates.get('reset_code'); var reset_code = templates.get('reset_code');
var resetEl = document.getElementById('reset'), var resetEl = document.getElementById('reset'),
@ -49,4 +52,7 @@
$('#success').show(); $('#success').show();
} }
}); });
}()); };
return ResetCode;
});

@ -1,6 +1,7 @@
(function() { define(function() {
var Search = {};
$(document).ready(function() { Search.init = function() {
var searchQuery = $('#topics-container').attr('data-search-query'); var searchQuery = $('#topics-container').attr('data-search-query');
$('.search-result-text').each(function() { $('.search-result-text').each(function() {
@ -21,4 +22,5 @@
}); });
}); });
})(); return Search;
});

@ -1,4 +1,7 @@
(function() { define(function() {
var Topic = {};
Topic.init = function() {
var expose_tools = templates.get('expose_tools'), var expose_tools = templates.get('expose_tools'),
tid = templates.get('topic_id'), tid = templates.get('topic_id'),
postListEl = document.getElementById('post-container'), postListEl = document.getElementById('post-container'),
@ -713,4 +716,7 @@
window.onscroll = updateHeader; window.onscroll = updateHeader;
window.onload = updateHeader; window.onload = updateHeader;
})(); };
return Topic;
});

@ -1,4 +1,7 @@
(function() { define(function() {
var Unread = {};
Unread.init = function() {
var loadingMoreTopics = false; var loadingMoreTopics = false;
app.enter_room('recent_posts'); app.enter_room('recent_posts');
@ -110,5 +113,7 @@
$('#load-more-btn').on('click', function() { $('#load-more-btn').on('click', function() {
loadMoreTopics(); loadMoreTopics();
}); });
};
})(); return Unread;
});

@ -1,6 +1,7 @@
(function() { define(function() {
var Users = {};
$(document).ready(function() { Users.init = function() {
var timeoutId = 0; var timeoutId = 0;
var loadingMoreUsers = false; var loadingMoreUsers = false;
@ -131,6 +132,7 @@
loadMoreUsers(); loadMoreUsers();
} }
}); });
}); };
}()); return Users;
});

@ -98,6 +98,3 @@
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />
<input type="hidden" template-variable="theirid" value="{theirid}" /> <input type="hidden" template-variable="theirid" value="{theirid}" />
<input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" /> <input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" />
<script type="text/javascript" src="{relative_path}/src/forum/account.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>

@ -174,9 +174,3 @@
</div> </div>
</div> </div>
<input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" />
<input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" />
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountedit.js"></script>

@ -26,6 +26,3 @@
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a> <a id="submitBtn" href="#" class="btn btn-primary">Save changes</a>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountsettings.js"></script>

@ -101,5 +101,3 @@
<div class="col-md-3"><i class="icon-adn"></i></div><div class="col-md-3"><i class="icon-android"></i></div><div class="col-md-3"><i class="icon-apple"></i></div><div class="col-md-3"><i class="icon-bitbucket"></i></div><div class="col-md-3"><i class="icon-bitbucket-sign"></i></div><div class="col-md-3"><i class="icon-bitcoin"></i></div><div class="col-md-3"><i class="icon-btc"></i></div><div class="col-md-3"><i class="icon-css3"></i></div><div class="col-md-3"><i class="icon-dribbble"></i></div><div class="col-md-3"><i class="icon-dropbox"></i></div><div class="col-md-3"><i class="icon-facebook"></i></div><div class="col-md-3"><i class="icon-facebook-sign"></i></div><div class="col-md-3"><i class="icon-flickr"></i></div><div class="col-md-3"><i class="icon-foursquare"></i></div><div class="col-md-3"><i class="icon-github"></i></div><div class="col-md-3"><i class="icon-github-alt"></i></div><div class="col-md-3"><i class="icon-github-sign"></i></div><div class="col-md-3"><i class="icon-gittip"></i></div><div class="col-md-3"><i class="icon-google-plus"></i></div><div class="col-md-3"><i class="icon-google-plus-sign"></i></div><div class="col-md-3"><i class="icon-html5"></i></div><div class="col-md-3"><i class="icon-instagram"></i></div><div class="col-md-3"><i class="icon-linkedin"></i></div><div class="col-md-3"><i class="icon-linkedin-sign"></i></div><div class="col-md-3"><i class="icon-linux"></i></div><div class="col-md-3"><i class="icon-maxcdn"></i></div><div class="col-md-3"><i class="icon-pinterest"></i></div><div class="col-md-3"><i class="icon-pinterest-sign"></i></div><div class="col-md-3"><i class="icon-renren"></i></div><div class="col-md-3"><i class="icon-skype"></i></div><div class="col-md-3"><i class="icon-stackexchange"></i></div><div class="col-md-3"><i class="icon-trello"></i></div><div class="col-md-3"><i class="icon-tumblr"></i></div><div class="col-md-3"><i class="icon-tumblr-sign"></i></div><div class="col-md-3"><i class="icon-twitter"></i></div><div class="col-md-3"><i class="icon-twitter-sign"></i></div><div class="col-md-3"><i class="icon-vk"></i></div><div class="col-md-3"><i class="icon-weibo"></i></div><div class="col-md-3"><i class="icon-windows"></i></div><div class="col-md-3"><i class="icon-xing"></i></div><div class="col-md-3"><i class="icon-xing-sign"></i></div><div class="col-md-3"><i class="icon-youtube"></i></div><div class="col-md-3"><i class="icon-youtube-play"></i></div><div class="col-md-3"><i class="icon-youtube-sign"></i></div> <div class="col-md-3"><i class="icon-adn"></i></div><div class="col-md-3"><i class="icon-android"></i></div><div class="col-md-3"><i class="icon-apple"></i></div><div class="col-md-3"><i class="icon-bitbucket"></i></div><div class="col-md-3"><i class="icon-bitbucket-sign"></i></div><div class="col-md-3"><i class="icon-bitcoin"></i></div><div class="col-md-3"><i class="icon-btc"></i></div><div class="col-md-3"><i class="icon-css3"></i></div><div class="col-md-3"><i class="icon-dribbble"></i></div><div class="col-md-3"><i class="icon-dropbox"></i></div><div class="col-md-3"><i class="icon-facebook"></i></div><div class="col-md-3"><i class="icon-facebook-sign"></i></div><div class="col-md-3"><i class="icon-flickr"></i></div><div class="col-md-3"><i class="icon-foursquare"></i></div><div class="col-md-3"><i class="icon-github"></i></div><div class="col-md-3"><i class="icon-github-alt"></i></div><div class="col-md-3"><i class="icon-github-sign"></i></div><div class="col-md-3"><i class="icon-gittip"></i></div><div class="col-md-3"><i class="icon-google-plus"></i></div><div class="col-md-3"><i class="icon-google-plus-sign"></i></div><div class="col-md-3"><i class="icon-html5"></i></div><div class="col-md-3"><i class="icon-instagram"></i></div><div class="col-md-3"><i class="icon-linkedin"></i></div><div class="col-md-3"><i class="icon-linkedin-sign"></i></div><div class="col-md-3"><i class="icon-linux"></i></div><div class="col-md-3"><i class="icon-maxcdn"></i></div><div class="col-md-3"><i class="icon-pinterest"></i></div><div class="col-md-3"><i class="icon-pinterest-sign"></i></div><div class="col-md-3"><i class="icon-renren"></i></div><div class="col-md-3"><i class="icon-skype"></i></div><div class="col-md-3"><i class="icon-stackexchange"></i></div><div class="col-md-3"><i class="icon-trello"></i></div><div class="col-md-3"><i class="icon-tumblr"></i></div><div class="col-md-3"><i class="icon-tumblr-sign"></i></div><div class="col-md-3"><i class="icon-twitter"></i></div><div class="col-md-3"><i class="icon-twitter-sign"></i></div><div class="col-md-3"><i class="icon-vk"></i></div><div class="col-md-3"><i class="icon-weibo"></i></div><div class="col-md-3"><i class="icon-windows"></i></div><div class="col-md-3"><i class="icon-xing"></i></div><div class="col-md-3"><i class="icon-xing-sign"></i></div><div class="col-md-3"><i class="icon-youtube"></i></div><div class="col-md-3"><i class="icon-youtube-play"></i></div><div class="col-md-3"><i class="icon-youtube-sign"></i></div>
<div class="col-md-3"><i class="icon-ambulance"></i></div><div class="col-md-3"><i class="icon-h-sign"></i></div><div class="col-md-3"><i class="icon-hospital"></i></div><div class="col-md-3"><i class="icon-medkit"></i></div><div class="col-md-3"><i class="icon-plus-sign-alt"></i></div><div class="col-md-3"><i class="icon-stethoscope"></i></div><div class="col-md-3"><i class="icon-user-md"></i></div> <div class="col-md-3"><i class="icon-ambulance"></i></div><div class="col-md-3"><i class="icon-h-sign"></i></div><div class="col-md-3"><i class="icon-hospital"></i></div><div class="col-md-3"><i class="icon-medkit"></i></div><div class="col-md-3"><i class="icon-plus-sign-alt"></i></div><div class="col-md-3"><i class="icon-stethoscope"></i></div><div class="col-md-3"><i class="icon-user-md"></i></div>
</div></div></div> </div></div></div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/categories.js"></script>

@ -17,10 +17,7 @@
<button class="btn btn-lg btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() { require(['forum/admin/settings'], function(Settings) {
if (nodebb_admin) { Settings.prepare();
nodebb_admin.prepare(); });
clearInterval(loadDelay);
}
}, 500);
</script> </script>

@ -9,7 +9,7 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
$.getScript(RELATIVE_PATH + '/src/forum/admin/footer.js'); require(['forum/admin/footer']);
</script> </script>
</body> </body>

@ -17,10 +17,7 @@
<button class="btn btn-lg btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() { require(['forum/admin/settings'], function(Settings) {
if (nodebb_admin) { Settings.prepare();
nodebb_admin.prepare(); });
clearInterval(loadDelay);
}
}, 500);
</script> </script>

@ -96,5 +96,3 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/groups.js"></script>

@ -7,7 +7,6 @@
var RELATIVE_PATH = "{relative_path}"; var RELATIVE_PATH = "{relative_path}";
</script> </script>
<link id="base-theme" href="{relative_path}/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen"> <link id="base-theme" href="{relative_path}/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="{relative_path}/vendor/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen">
<link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css"> <link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css">
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script> <script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript" src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
@ -25,7 +24,10 @@
<script> <script>
require.config({ require.config({
baseUrl: "{relative_path}/src/modules", baseUrl: "{relative_path}/src/modules",
waitSeconds: 3 waitSeconds: 3,
paths: {
"forum": '../forum'
}
}); });
</script> </script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css"> <link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">

@ -18,5 +18,3 @@
</p> </p>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/index.js"></script>

@ -24,10 +24,7 @@
<button class="btn btn-lg btn-primary" id="save" checked>Save</button> <button class="btn btn-lg btn-primary" id="save" checked>Save</button>
<script> <script>
var loadDelay = setInterval(function() { require(['forum/admin/settings'], function(Settings) {
if (nodebb_admin) { Settings.prepare();
nodebb_admin.prepare(); });
clearInterval(loadDelay);
}
}, 500);
</script> </script>

@ -21,5 +21,3 @@
Full documentation regarding plugin authoring can be found in the <a target="_blank" href="https://github.com/designcreateplay/NodeBB/wiki/Writing-Plugins-for-NodeBB">NodeBB Wiki</a>. Full documentation regarding plugin authoring can be found in the <a target="_blank" href="https://github.com/designcreateplay/NodeBB/wiki/Writing-Plugins-for-NodeBB">NodeBB Wiki</a>.
</p> </p>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/plugins.js"></script>

@ -8,6 +8,8 @@
<input class="form-control" type="text" placeholder="Your Community Name" data-field="title" /><br /> <input class="form-control" type="text" placeholder="Your Community Name" data-field="title" /><br />
<label>Site Description</label> <label>Site Description</label>
<input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br /> <input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br />
<label>Site Keywords</label>
<input type="text" class="form-control" placeholder="Keywords describing your community, comma-seperated" data-field="keywords" /><br />
<label>Imgur Client ID</label> <label>Imgur Client ID</label>
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br /> <input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
<label>Maximum User Image Size</label> <label>Maximum User Image Size</label>
@ -70,17 +72,18 @@
<strong>Post Delay</strong><br /> <input type="text" class="form-control" value="10000" data-field="postDelay"><br /> <strong>Post Delay</strong><br /> <input type="text" class="form-control" value="10000" data-field="postDelay"><br />
<strong>Minimum Title Length</strong><br /> <input type="text" class="form-control" value="3" data-field="minimumTitleLength"><br /> <strong>Minimum Title Length</strong><br /> <input type="text" class="form-control" value="3" data-field="minimumTitleLength"><br />
<strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br /> <strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br />
<div class="checkbox">
<label>
<input type="checkbox" data-field="useOutgoingLinksPage"> <strong>Use Outgoing Links Warning Page</strong>
</label>
</div>
</div> </div>
</form> </form>
<button class="btn btn-lg btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() { require(['forum/admin/settings'], function(Settings) {
if (nodebb_admin) { Settings.prepare();
nodebb_admin.prepare(); });
clearInterval(loadDelay);
}
}, 500);
</script> </script>

@ -20,7 +20,7 @@ jQuery(document).ready(function () {
slug = 'category/' + category.slug; slug = 'category/' + category.slug;
asyncTest( "Loading Category '" + category.name + "' located at " + slug, function() { asyncTest( "Loading Category '" + category.name + "' located at " + slug, function() {
jQuery.get(config.api_url + slug, function(data) { jQuery.get(RELATIVE_PATH + '/api/' + slug, function(data) {
ok( data.category_name, JSON.stringify(data) ); //todo: check this against data.categories ok( data.category_name, JSON.stringify(data) ); //todo: check this against data.categories
start(); start();
}); });

@ -23,4 +23,10 @@
<button class="btn btn-warning" id="revert_theme">Revert</button> This will remove any custom theme applied to your NodeBB, and restore the base theme. <button class="btn btn-warning" id="revert_theme">Revert</button> This will remove any custom theme applied to your NodeBB, and restore the base theme.
</p> </p>
<script type="text/javascript" src="{relative_path}/src/forum/admin/themes.js"></script> <script>
var bootswatchListener = function(data) {
require(['forum/admin/themes'], function(t) {
t.render(data);
});
}
</script>

@ -22,5 +22,3 @@
<div class="text-center"> <div class="text-center">
<button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button> <button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/topics.js"></script>

@ -17,10 +17,7 @@
<button class="btn btn-lg btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() { require(['forum/admin/settings'], function(Settings) {
if (nodebb_admin) { Settings.prepare();
nodebb_admin.prepare(); });
clearInterval(loadDelay);
}
}, 500);
</script> </script>

@ -42,6 +42,3 @@
<button id="load-more-users-btn" class="btn btn-primary">Load More</button> <button id="load-more-users-btn" class="btn btn-primary">Load More</button>
</div> </div>
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />
<script type="text/javascript" src="{relative_path}/src/forum/admin/users.js"></script>

@ -97,5 +97,3 @@
<input type="hidden" template-variable="twitter-intent-url" value="{twitter-intent-url}" /> <input type="hidden" template-variable="twitter-intent-url" value="{twitter-intent-url}" />
<input type="hidden" template-variable="facebook-share-url" value="{facebook-share-url}" /> <input type="hidden" template-variable="facebook-share-url" value="{facebook-share-url}" />
<input type="hidden" template-variable="google-share-url" value="{google-share-url}" /> <input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
<script type="text/javascript" src="{relative_path}/src/forum/category.js"></script>

@ -22,6 +22,3 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/favourites.js"></script>

@ -36,6 +36,3 @@
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />
<input type="hidden" template-variable="theirid" value="{theirid}" /> <input type="hidden" template-variable="theirid" value="{theirid}" />
<input type="hidden" template-variable="followersCount" value="{followersCount}" /> <input type="hidden" template-variable="followersCount" value="{followersCount}" />
<script type="text/javascript" src="{relative_path}/src/forum/followers.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>

@ -38,6 +38,3 @@
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />
<input type="hidden" template-variable="theirid" value="{theirid}" /> <input type="hidden" template-variable="theirid" value="{theirid}" />
<input type="hidden" template-variable="followingCount" value="{followingCount}" /> <input type="hidden" template-variable="followingCount" value="{followingCount}" />
<script type="text/javascript" src="{relative_path}/src/forum/following.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>

@ -53,7 +53,7 @@
</footer> </footer>
<script> <script>
$.getScript(RELATIVE_PATH + '/src/forum/footer.js'); require(['forum/footer']);
</script> </script>
</body> </body>

@ -15,8 +15,14 @@
<script> <script>
require.config({ require.config({
baseUrl: "{relative_path}/src/modules", baseUrl: "{relative_path}/src/modules",
waitSeconds: 3 waitSeconds: 3,
paths: {
"forum": '../forum'
}
}); });
requirejs.onError = function(err) {
console.log(err);
}
</script> </script>
<link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" /> <link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
@ -50,9 +56,6 @@
<li class="visible-xs"> <li class="visible-xs">
<a href="/search">[[global:header.search]]</a> <a href="/search">[[global:header.search]]</a>
</li> </li>
<li class="visible-xs">
<a href="/search">Search</a>
</li>
<li> <li>
<a href="/"></a> <a href="/"></a>
</li> </li>

@ -60,5 +60,3 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/login.js"></script>

@ -49,5 +49,3 @@
</ul> </ul>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/recent.js"></script>

@ -80,5 +80,3 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/register.js"></script>

@ -26,5 +26,3 @@
<button class="btn btn-primary btn-block btn-lg" id="reset" type="submit">Reset Password</button> <button class="btn btn-primary btn-block btn-lg" id="reset" type="submit">Reset Password</button>
</form> </form>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/reset.js"></script>

@ -34,6 +34,3 @@
</form> </form>
</div> </div>
<input type="hidden" template-variable="reset_code" value="{reset_code}" /> <input type="hidden" template-variable="reset_code" value="{reset_code}" />
<script type="text/javascript" src="{relative_path}/src/forum/reset_code.js"></script>

@ -53,5 +53,3 @@
</ul> </ul>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/search.js"></script>

@ -199,7 +199,3 @@
<input type="hidden" template-variable="pinned" value="{pinned}" /> <input type="hidden" template-variable="pinned" value="{pinned}" />
<input type="hidden" template-variable="topic_name" value="{topic_name}" /> <input type="hidden" template-variable="topic_name" value="{topic_name}" />
<input type="hidden" template-variable="postcount" value="{postcount}" /> <input type="hidden" template-variable="postcount" value="{postcount}" />
<script type="text/javascript" src="{relative_path}/src/forum/topic.js"></script>

@ -54,5 +54,3 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/unread.js"></script>

@ -45,5 +45,3 @@
<button id="load-more-users-btn" class="btn btn-primary">[[users:load_more]]</button> <button id="load-more-users-btn" class="btn btn-primary">[[users:load_more]]</button>
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/users.js"></script>

@ -10,6 +10,9 @@
height: 100%; height: 100%;
background: rgba(64, 64, 64, 0.6); background: rgba(64, 64, 64, 0.6);
visibility: visible; visibility: visible;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
.btn-toolbar { .btn-toolbar {
&.formatting-bar { &.formatting-bar {

@ -42,6 +42,10 @@ var async = require('async'),
}, { }, {
name: 'redis:password', name: 'redis:password',
description: 'Password of your Redis database' description: 'Password of your Redis database'
}, {
name: "redis:database",
description: "Which database to use (0..n)",
'default': 0
}, { }, {
name: 'bind_address', name: 'bind_address',
description: 'IP or Hostname to bind to', description: 'IP or Hostname to bind to',
@ -64,11 +68,13 @@ var async = require('async'),
config.redis = { config.redis = {
host: config['redis:host'], host: config['redis:host'],
port: config['redis:port'], port: config['redis:port'],
password: config['redis:password'] password: config['redis:password'],
database: config['redis:database']
}; };
delete config['redis:host']; delete config['redis:host'];
delete config['redis:port']; delete config['redis:port'];
delete config['redis:password']; delete config['redis:password'];
delete config['redis:database'];
// Add hardcoded values // Add hardcoded values
config.bcrypt_rounds = 12; config.bcrypt_rounds = 12;
@ -81,10 +87,6 @@ var async = require('async'),
protocol = urlObject.protocol, protocol = urlObject.protocol,
server_conf = config, server_conf = config,
client_conf = { client_conf = {
socket: {
address: protocol + '//' + host + (config.use_port ? ':' + config.port : '')
},
api_url: protocol + '//' + host + (config.use_port ? ':' + config.port : '') + relative_path + '/api/',
relative_path: relative_path relative_path: relative_path
}; };

@ -15,28 +15,21 @@ var RDB = require('./redis.js'),
}); });
}); });
}, },
create: function(text, score, path, uniqueId, callback) { create: function(text, path, uniqueId, callback) {
/* /**
* Score guide:
* 0 Low priority messages (probably unused)
* 5 Normal messages
* 10 High priority messages
*
* uniqueId is used solely to override stale nids. * uniqueId is used solely to override stale nids.
* If a new nid is pushed to a user and an existing nid in the user's * If a new nid is pushed to a user and an existing nid in the user's
* (un)read list contains the same uniqueId, it will be removed, and * (un)read list contains the same uniqueId, it will be removed, and
* the new one put in its place. * the new one put in its place.
*/ */
RDB.incr('notifications:next_nid', function(err, nid) { RDB.incr('notifications:next_nid', function(err, nid) {
RDB.hmset( RDB.hmset('notifications:' + nid, {
'notifications:' + nid, text: text || '',
'text', text || '', path: path || null,
'score', score || 5, datetime: Date.now(),
'path', path || null, uniqueId: uniqueId || utils.generateUUID()
'datetime', Date.now(), }, function(err, status) {
'uniqueId', uniqueId || utils.generateUUID(), if (!err) callback(nid);
function(err, status) {
if (status === 'OK') callback(nid);
}); });
}); });
}, },
@ -51,7 +44,7 @@ var RDB = require('./redis.js'),
if (parseInt(uids[x]) > 0) { if (parseInt(uids[x]) > 0) {
(function(uid) { (function(uid) {
notifications.remove_by_uniqueId(notif_data.uniqueId, uid, function() { notifications.remove_by_uniqueId(notif_data.uniqueId, uid, function() {
RDB.zadd('uid:' + uid + ':notifications:unread', notif_data.score, nid); RDB.zadd('uid:' + uid + ':notifications:unread', notif_data.datetime, nid);
global.io.sockets.in('uid_' + uid).emit('event:new_notification'); global.io.sockets.in('uid_' + uid).emit('event:new_notification');
if (callback) callback(true); if (callback) callback(true);
}); });
@ -98,7 +91,7 @@ var RDB = require('./redis.js'),
if (parseInt(uid) > 0) { if (parseInt(uid) > 0) {
notifications.get(nid, function(notif_data) { notifications.get(nid, function(notif_data) {
RDB.zrem('uid:' + uid + ':notifications:unread', nid); RDB.zrem('uid:' + uid + ':notifications:unread', nid);
RDB.zadd('uid:' + uid + ':notifications:read', notif_data.score, nid); RDB.zadd('uid:' + uid + ':notifications:read', notif_data.datetime, nid);
if (callback) callback(); if (callback) callback();
}); });
} }

@ -185,7 +185,7 @@ var fs = require('fs'),
} else { } else {
// Otherwise, this hook contains no methods // Otherwise, this hook contains no methods
var returnVal = (Array.isArray(args) ? args[0] : args); var returnVal = (Array.isArray(args) ? args[0] : args);
if (callback) callback(err, returnVal); if (callback) callback(null, returnVal);
} }
}, },
isActive: function(id, callback) { isActive: function(id, callback) {

@ -5,6 +5,7 @@ var RDB = require('./redis.js'),
user = require('./user.js'), user = require('./user.js'),
async = require('async'), async = require('async'),
nconf = require('nconf'), nconf = require('nconf'),
validator = require('validator'),
utils = require('../public/src/utils'), utils = require('../public/src/utils'),
plugins = require('./plugins'), plugins = require('./plugins'),
@ -92,10 +93,9 @@ var RDB = require('./redis.js'),
], function(err, results) { ], function(err, results) {
io.sockets.in('topic_' + results[0].tid).emit('event:post_edited', { io.sockets.in('topic_' + results[0].tid).emit('event:post_edited', {
pid: pid, pid: pid,
title: title, title: validator.sanitize(title).escape(),
isMainPost: results[0].isMainPost, isMainPost: results[0].isMainPost,
content: results[1] content: results[1]
}); });
}); });
}; };

@ -17,6 +17,17 @@
RedisDB.exports.auth(nconf.get('redis:password')); RedisDB.exports.auth(nconf.get('redis:password'));
} }
if( (db = nconf.get('redis:database')) ){
RedisDB.exports.select(db, function(error){
if(error !== null){
winston.err(error);
if (global.env !== 'production') {
throw new Error(error);
}
}
});
}
RedisDB.exports.handle = function(error) { RedisDB.exports.handle = function(error) {
if (error !== null) { if (error !== null) {
winston.err(error); winston.err(error);

@ -28,6 +28,7 @@ var user = require('./../user.js'),
config.minimumUsernameLength = meta.config.minimumUsernameLength; config.minimumUsernameLength = meta.config.minimumUsernameLength;
config.maximumUsernameLength = meta.config.maximumUsernameLength; config.maximumUsernameLength = meta.config.maximumUsernameLength;
config.minimumPasswordLength = meta.config.minimumPasswordLength; config.minimumPasswordLength = meta.config.minimumPasswordLength;
config.useOutgoingLinksPage = meta.config.useOutgoingLinksPage;
res.json(200, config); res.json(200, config);
}); });

@ -1,75 +0,0 @@
(function(TestBed) {
TestBed.create_routes = function(app) {
app.get('/bench/forloop', function(req, res) {
var benchData = {};
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
function f(x) {
return x;
}
var runCount = req.query.runs ? req.query.runs : 1000000;
function withCaching() {
var time = process.hrtime();
for (var n = 0; n < runCount; ++n) {
for (var i = 0, len = myArray.length; i < len; ++i) {
f(myArray[i]);
}
}
var diff = process.hrtime(time);
diff = diff[0] + diff[1] / 1e9;
return diff;
}
function withoutCaching() {
var time = process.hrtime();
for (var n = 0; n < runCount; ++n) {
for (var i = 0; i < myArray.length; ++i) {
f(myArray[i]);
}
}
var diff = process.hrtime(time);
diff = diff[0] + diff[1] / 1e9;
return diff;
}
function withForeach() {
var time = process.hrtime();
for (var n = 0; n < runCount; ++n) {
myArray.forEach(function(index) {
});
}
var diff = process.hrtime(time);
diff = diff[0] + diff[1] / 1e9;
return diff;
}
benchData['runs'] = runCount;
benchData['withCaching'] = withCaching();
benchData['withoutCaching'] = withoutCaching();
benchData['withForeach'] = withForeach();
res.json(benchData);
});
};
}(exports));

@ -265,28 +265,28 @@ var RDB = require('./redis.js'),
ThreadTools.get_followers = function(tid, callback) { ThreadTools.get_followers = function(tid, callback) {
RDB.smembers('tid:' + tid + ':followers', function(err, followers) { RDB.smembers('tid:' + tid + ':followers', function(err, followers) {
callback(err, followers); callback(err, followers.map(function(follower) {
return parseInt(follower, 10);
}));
}); });
} }
ThreadTools.notify_followers = function(tid, exceptUid) { ThreadTools.notify_followers = function(tid, exceptUid) {
async.parallel([ async.parallel([
function(next) { function(next) {
topics.getTopicField(tid, 'title', function(err, title) { topics.getTopicField(tid, 'title', function(err, title) {
topics.getTeaser(tid, function(err, teaser) { topics.getTeaser(tid, function(err, teaser) {
if (!err) { if (!err) {
notifications.create('<strong>' + teaser.username + '</strong> has posted a reply to: "<strong>' + title + '</strong>"', null, nconf.get('relative_path') + '/topic/' + tid, 'topic:' + tid, function(nid) { notifications.create('<strong>' + teaser.username + '</strong> has posted a reply to: "<strong>' + title + '</strong>"', nconf.get('relative_path') + '/topic/' + tid, 'topic:' + tid, function(nid) {
next(null, nid); next(null, nid);
}); });
} else next(err); } else next(err);
}); });
}); });
}, },
function(next) { function(next) {
ThreadTools.get_followers(tid, function(err, followers) { ThreadTools.get_followers(tid, function(err, followers) {
exceptUid = parseInt(exceptUid, 10);
if (followers.indexOf(exceptUid) !== -1) followers.splice(followers.indexOf(exceptUid), 1); if (followers.indexOf(exceptUid) !== -1) followers.splice(followers.indexOf(exceptUid), 1);
next(null, followers); next(null, followers);
}); });

@ -15,15 +15,18 @@ schema = require('./schema.js'),
topicSearch = reds.createSearch('nodebbtopicsearch'), topicSearch = reds.createSearch('nodebbtopicsearch'),
validator = require('validator'); validator = require('validator');
(function(Topics) { (function(Topics) {
Topics.getTopicData = function(tid, callback) { Topics.getTopicData = function(tid, callback) {
RDB.hgetall('topic:' + tid, function(err, data) { RDB.hgetall('topic:' + tid, function(err, data) {
if (err === null) if (err === null) {
if(data)
data.title = validator.sanitize(data.title).escape();
callback(data); callback(data);
else } else {
console.log(err); console.log(err);
}
}); });
} }
@ -95,7 +98,7 @@ schema = require('./schema.js'),
var timestamp = Date.now(); var timestamp = Date.now();
var args = ['topics:recent', '+inf', timestamp - 86400000, 'WITHSCORES', 'LIMIT', start, end - start + 1]; var args = ['topics:recent', '+inf', timestamp - 86400000, 'LIMIT', start, end - start + 1];
RDB.zrevrangebyscore(args, function(err, tids) { RDB.zrevrangebyscore(args, function(err, tids) {
@ -658,7 +661,6 @@ schema = require('./schema.js'),
var slug = tid + '/' + utils.slugify(title); var slug = tid + '/' + utils.slugify(title);
var timestamp = Date.now(); var timestamp = Date.now();
title = validator.sanitize(title).escape();
RDB.hmset('topic:' + tid, { RDB.hmset('topic:' + tid, {
'tid': tid, 'tid': tid,
'uid': uid, 'uid': uid,

@ -1,193 +1,74 @@
var RDB = require('./redis.js'), var RDB = require('./redis.js'),
async = require('async'), async = require('async'),
winston = require('winston'), winston = require('winston'),
user = require('./user'), notifications = require('./notifications')
Groups = require('./groups'); Upgrade = {};
Upgrade.upgrade = function() {
winston.info('Beginning Redis database schema update');
function upgradeCategory(cid, callback) { async.series([
RDB.type('categories:' + cid + ':tid', function(err, type) {
if (type === 'set') {
RDB.smembers('categories:' + cid + ':tid', function(err, tids) {
function moveTopic(tid, callback) {
RDB.hget('topic:' + tid, 'timestamp', function(err, timestamp) {
if (err)
return callback(err);
RDB.zadd('temp_categories:' + cid + ':tid', timestamp, tid);
callback(null);
});
}
async.each(tids, moveTopic, function(err) {
if (!err) {
RDB.rename('temp_categories:' + cid + ':tid', 'categories:' + cid + ':tid');
callback(null);
} else
callback(err);
});
});
} else {
winston.info('category already upgraded ' + cid);
callback(null);
}
});
}
function upgradeUser(uid, callback) {
user.getUserFields(uid, ['joindate', 'postcount', 'reputation'], function(err, userData) {
if (err)
return callback(err);
async.parallel([
function(next) {
if (userData.joindate)
RDB.zadd('users:joindate', userData.joindate, uid, next);
else
next(null);
},
function(next) { function(next) {
if (userData.postcount) RDB.hget('notifications:1', 'score', function(err, score) {
RDB.zadd('users:postcount', userData.postcount, uid, next); if (score) {
else async.series([
next(null);
},
function(next) { function(next) {
if (userData.reputation) RDB.keys('uid:*:notifications:flag', function(err, keys) {
RDB.zadd('users:reputation', userData.reputation, uid, next); if (keys.length > 0) {
else winston.info('[2013/10/03] Removing deprecated Notification Flags');
next(null); async.each(keys, function(key, next) {
RDB.del(key, next);
}, next);
} else {
winston.info('[2013/10/03] No Notification Flags found. Good.');
next();
} }
], function(err, result) {
callback(err);
});
}); });
}
function upgradeUserHash(uid, callback) {
user.getUserFields(uid, ['username', 'userslug', 'email'], function(err, userData) {
if (err)
return callback(err);
async.parallel([
function(next) {
if (userData.username)
RDB.hset('username:uid', userData.username, uid, next);
else
next(null);
}, },
function(next) { function(next) {
if (userData.userslug) winston.info('[2013/10/03] Updating Notifications');
RDB.hset('userslug:uid', userData.userslug, uid, next); RDB.keys('uid:*:notifications:*', function(err, keys) {
else async.each(keys, function(key, next) {
next(null); RDB.zrange(key, 0, -1, function(err, nids) {
}, async.each(nids, function(nid, next) {
function(next) { notifications.get(nid, function(notif_data) {
if (userData.email) RDB.zadd(key, notif_data.datetime, nid, next);
RDB.hset('email:uid', userData.email, uid, next);
else
next(null);
}
], function(err, result) {
callback(err);
}); });
}, next);
});
}, next);
}); });
}
function upgradeAdmins(callback) {
Groups.getGidFromName('Administrators', function(err, gid) {
if (!err && !gid) {
winston.info('Upgrading Administrators');
async.parallel([
function(next) {
RDB.smembers("administrators", next);
}, },
function(next) { function(next) {
Groups.create('Administrators', 'Forum Administrators', next); RDB.keys('notifications:*', function(err, keys) {
} if (keys.length > 0) {
], function(err, results) { winston.info('[2013/10/03] Removing Notification Scores');
var gid = results[1].gid; async.each(keys, function(key, next) {
if (key === 'notifications:next_nid') return next();
async.each(results[0], function(uid, next) { RDB.hdel(key, 'score', next);
Groups.join(gid, uid, next); }, next);
}, callback);
});
} else { } else {
winston.info('Administrators group OK') winston.info('[2013/10/03] No Notification Scores found. Good.');
callback(); next();
} }
}); });
} }
], next);
exports.upgrade = function() {
winston.info('upgrading nodebb now');
var schema = [
function upgradeCategories(next) {
winston.info('upgrading categories');
RDB.lrange('categories:cid', 0, -1, function(err, cids) {
async.each(cids, upgradeCategory, function(err) {
if (!err) {
winston.info('upgraded categories');
next(null, null);
} else { } else {
next(err, null); winston.info('[2013/10/03] Updates to Notifications skipped.');
next();
} }
}); });
});
},
function upgradeUsers(next) {
winston.info('upgrading users');
RDB.lrange('userlist', 0, -1, function(err, uids) {
async.each(uids, upgradeUser, function(err) {
if (!err) {
winston.info('upgraded users');
next(null, null);
} else {
next(err, null);
} }
}); // Add new schema updates here
], function(err) {
});
},
function upgradeUserHashes(next) {
winston.info('upgrading user hashes');
RDB.zrange('users:joindate', 0, -1, function(err, uids) {
if (err)
return next(err);
async.each(uids, upgradeUserHash, function(err) {
if (!err) { if (!err) {
winston.info('upgraded user hashes'); winston.info('Redis schema update complete!');
next(null, null); process.exit();
} else { } else {
next(err, null); winston.error('Errors were encountered while updating the NodeBB schema: ' + err.message);
} }
}); });
}); };
},
upgradeAdmins
];
async.series(schema, function(err, results) {
if (!err)
winston.info('upgrade complete');
else
winston.err(err);
process.exit(); module.exports = Upgrade;
});
}

@ -578,7 +578,7 @@ var utils = require('./../public/src/utils.js'),
topics.getTopicField(tid, 'slug', function(err, slug) { topics.getTopicField(tid, 'slug', function(err, slug) {
var message = '<strong>' + username + '</strong> made a new post'; var message = '<strong>' + username + '</strong> made a new post';
notifications.create(message, 5, nconf.get('relative_path') + '/topic/' + slug + '#' + pid, 'topic:' + tid, function(nid) { notifications.create(message, nconf.get('relative_path') + '/topic/' + slug + '#' + pid, 'topic:' + tid, function(nid) {
notifications.push(nid, followers); notifications.push(nid, followers);
}); });
}); });
@ -888,7 +888,7 @@ var utils = require('./../public/src/utils.js'),
async.parallel({ async.parallel({
unread: function(next) { unread: function(next) {
RDB.zrevrangebyscore('uid:' + uid + ':notifications:unread', 10, 0, function(err, nids) { RDB.zrevrange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
// @todo handle err // @todo handle err
var unread = []; var unread = [];
@ -910,7 +910,7 @@ var utils = require('./../public/src/utils.js'),
}); });
}, },
read: function(next) { read: function(next) {
RDB.zrevrangebyscore('uid:' + uid + ':notifications:read', 10, 0, function(err, nids) { RDB.zrevrange('uid:' + uid + ':notifications:read', 0, 10, function(err, nids) {
// @todo handle err // @todo handle err
var read = []; var read = [];
@ -932,22 +932,6 @@ var utils = require('./../public/src/utils.js'),
}); });
} }
}, function(err, notifications) { }, function(err, notifications) {
// While maintaining score sorting, sort by time
var readCount = notifications.read.length,
unreadCount = notifications.unread.length;
notifications.read.sort(function(a, b) {
if (a.score === b.score) {
return (a.datetime - b.datetime) > 0 ? -1 : 1;
}
});
notifications.unread.sort(function(a, b) {
if (a.score === b.score) {
return (a.datetime - b.datetime) > 0 ? -1 : 1;
}
});
// Limit the number of notifications to `maxNotifs`, prioritising unread notifications // Limit the number of notifications to `maxNotifs`, prioritising unread notifications
if (notifications.read.length + notifications.unread.length > maxNotifs) { if (notifications.read.length + notifications.unread.length > maxNotifs) {
notifications.read.length = maxNotifs - notifications.unread.length; notifications.read.length = maxNotifs - notifications.unread.length;

@ -17,13 +17,13 @@ var express = require('express'),
admin = require('./routes/admin.js'), admin = require('./routes/admin.js'),
userRoute = require('./routes/user.js'), userRoute = require('./routes/user.js'),
apiRoute = require('./routes/api.js'), apiRoute = require('./routes/api.js'),
testBed = require('./routes/testbed.js'),
auth = require('./routes/authentication.js'), auth = require('./routes/authentication.js'),
meta = require('./meta.js'), meta = require('./meta.js'),
feed = require('./feed'), feed = require('./feed'),
plugins = require('./plugins'), plugins = require('./plugins'),
nconf = require('nconf'), nconf = require('nconf'),
winston = require('winston'); winston = require('winston'),
validator = require('validator');
(function (app) { (function (app) {
var templates = null, var templates = null,
@ -45,7 +45,7 @@ var express = require('express'),
app.build_header = function (options, callback) { app.build_header = function (options, callback) {
var defaultMetaTags = [{ var defaultMetaTags = [{
name: 'viewport', name: 'viewport',
content: 'width=device-width, initial-scale=1.0' content: 'width=device-width, initial-scale=1.0, user-scalable=no'
}, { }, {
name: 'content-type', name: 'content-type',
content: 'text/html; charset=UTF-8' content: 'text/html; charset=UTF-8'
@ -55,6 +55,9 @@ var express = require('express'),
}, { }, {
property: 'og:site_name', property: 'og:site_name',
content: meta.config.title || 'NodeBB' content: meta.config.title || 'NodeBB'
}, {
property: 'keywords',
content: meta.config['keywords'] || ''
}], }],
metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])), metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])),
templateValues = { templateValues = {
@ -131,22 +134,23 @@ var express = require('express'),
app.use(function (req, res, next) { app.use(function (req, res, next) {
res.status(404); res.status(404);
if (path.dirname(req.url) === '/src/forum') {
// Handle missing client-side scripts
res.type('text/javascript').send(200, '');
} else if (req.accepts('html')) {
// respond with html page // respond with html page
if (req.accepts('html')) { if (process.env.NODE_ENV === 'development') winston.warn('Route requested but not found: ' + req.url);
res.redirect(nconf.get('relative_path') + '/404'); res.redirect(nconf.get('relative_path') + '/404');
return; } else if (req.accepts('json')) {
}
// respond with json // respond with json
if (req.accepts('json')) { if (process.env.NODE_ENV === 'development') winston.warn('Route requested but not found: ' + req.url);
res.send({ res.json({
error: 'Not found' error: 'Not found'
}); });
return; } else {
}
// default to plain-text. send() // default to plain-text. send()
res.type('txt').send('Not found'); res.type('txt').send('Not found');
}
}); });
app.use(function (err, req, res, next) { app.use(function (err, req, res, next) {
@ -197,7 +201,6 @@ var express = require('express'),
auth.create_routes(app); auth.create_routes(app);
admin.create_routes(app); admin.create_routes(app);
userRoute.create_routes(app); userRoute.create_routes(app);
testBed.create_routes(app);
apiRoute.create_routes(app); apiRoute.create_routes(app);
@ -308,7 +311,8 @@ var express = require('express'),
}, },
function (topicData, next) { function (topicData, next) {
var lastMod = 0, var lastMod = 0,
timestamp; timestamp,
sanitize = validator.sanitize;
for (var x = 0, numPosts = topicData.posts.length; x < numPosts; x++) { for (var x = 0, numPosts = topicData.posts.length; x < numPosts; x++) {
timestamp = parseInt(topicData.posts[x].timestamp, 10); timestamp = parseInt(topicData.posts[x].timestamp, 10);
@ -321,6 +325,9 @@ var express = require('express'),
metaTags: [{ metaTags: [{
name: "title", name: "title",
content: topicData.topic_name content: topicData.topic_name
}, {
name: "description",
content: sanitize(topicData.main_posts[0].content.substr(0, 255)).escape().replace('\n', '')
}, { }, {
property: 'og:title', property: 'og:title',
content: topicData.topic_name + ' | ' + (meta.config.title || 'NodeBB') content: topicData.topic_name + ' | ' + (meta.config.title || 'NodeBB')

@ -580,7 +580,7 @@ module.exports.init = function(io) {
notifText = 'New message from <strong>' + username + '</strong>'; notifText = 'New message from <strong>' + username + '</strong>';
if (!isUserOnline(touid)) { if (!isUserOnline(touid)) {
notifications.create(notifText, 5, 'javascript:app.openChat(&apos;' + username + '&apos;, ' + uid + ');', 'notification_' + uid + '_' + touid, function(nid) { notifications.create(notifText, 'javascript:app.openChat(&apos;' + username + '&apos;, ' + uid + ');', 'notification_' + uid + '_' + touid, function(nid) {
notifications.push(nid, [touid], function(success) { notifications.push(nid, [touid], function(success) {
}); });

Loading…
Cancel
Save