Baris Usakli
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 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")
* [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?

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

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

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

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

@ -1,4 +1,9 @@
(function() {
define(['forum/accountheader'], function(header) {
var Account = {};
Account.init = function() {
header.init();
var yourid = templates.get('yourid'),
theirid = templates.get('theirid'),
isFollowing = templates.get('isFollowing');
@ -59,9 +64,21 @@
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');
function handleUserOnline(data) {
if (data.online) {
onlineStatus.find('span span').text('online');
onlineStatus.find('i').attr('class', 'icon-circle');
@ -69,17 +86,7 @@
onlineStatus.find('span span').text('offline');
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');
var uploadedPicture = templates.get('uploadedpicture');
$(document).ready(function() {
define(['forum/accountheader'], function(header) {
var AccountEdit = {};
AccountEdit.init = function() {
header.init();
var gravatarPicture = templates.get('gravatarpicture');
var uploadedPicture = templates.get('uploadedpicture');
$('#uploadForm').submit(function() {
status('uploading the file ...');
AccountEdit.status('uploading the file ...');
$('#upload-progress-bar').css('width', '0%');
$('#upload-progress-box').show();
$('#upload-progress-box').removeClass('hide');
if (!$('#userPhotoInput').val()) {
error('select an image to upload!');
AccountEdit.error('select an image to upload!');
return false;
}
@ -23,7 +25,7 @@ $(document).ready(function() {
$(this).ajaxSubmit({
error: function(xhr) {
error('Error: ' + xhr.status);
AccountEdit.error('Error: ' + xhr.status);
},
uploadProgress: function(event, position, total, percent) {
@ -34,7 +36,7 @@ $(document).ready(function() {
success: function(response) {
if (response.error) {
error(response.error);
AccountEdit.error(response.error);
return;
}
@ -46,54 +48,20 @@ $(document).ready(function() {
uploadedPicture = imageUrlOnServer;
setTimeout(function() {
hideAlerts();
AccountEdit.hideAlerts();
$('#upload-picture-modal').modal('hide');
}, 750);
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
success('File uploaded successfully!');
AccountEdit.success('File uploaded successfully!');
}
});
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 = '';
$('#submitBtn').on('click', function() {
@ -126,37 +94,9 @@ $(document).ready(function() {
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() {
selectedImageType = '';
updateImages();
AccountEdit.updateImages();
$('#change-picture-modal').modal('show');
$('#change-picture-modal').removeClass('hide');
@ -180,7 +120,7 @@ $(document).ready(function() {
$('#change-picture-modal').modal('hide');
if (selectedImageType) {
changeUserPicture(selectedImageType);
AccountEdit.changeUserPicture(selectedImageType);
if (selectedImageType == 'gravatar')
$('#user-current-picture').attr('src', gravatarPicture);
@ -200,7 +140,7 @@ $(document).ready(function() {
$('#upload-picture-modal').modal('show');
$('#upload-picture-modal').removeClass('hide');
hideAlerts();
AccountEdit.hideAlerts();
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'),
theirid = templates.get('theirid');
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();
AccountHeader.createMenu();
var editLink = $('#editLink');
var settingsLink = $('#settingsLink');
@ -37,6 +24,20 @@
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() {
@ -15,5 +19,7 @@ $(document).ready(function() {
});
return false;
});
};
return AccountSettings;
});

@ -1,3 +1,7 @@
define(function() {
var Categories = {};
Categories.init = function() {
var modified_categories = {};
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
(function() {
function showCreateCategoryModal() {
$('#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() {
// On menu click, change "active" state
var menuEl = document.querySelector('.sidebar-nav'),
@ -91,7 +16,9 @@ var nodebb_admin = (function(nodebb_admin) {
});
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');
@ -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'),
createModal = $('#create-modal'),
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']);
socket.on('api:get_all_rooms', function(data) {
@ -21,5 +22,7 @@
app.enter_room('admin');
socket.emit('api:get_all_rooms');
};
}());
return Admin;
});

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

@ -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.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() {
Themes.init = function() {
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);
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
@ -74,8 +40,10 @@ var nodebb_admin = (function(nodebb_admin) {
revertEl.addEventListener('click', function() {
bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) {
if (confirm) {
nodebb_admin.remove('theme:id');
nodebb_admin.remove('theme:src');
require(['forum/admin/settings'], function(Settings) {
Settings.remove('theme:id');
Settings.remove('theme:src');
});
}
});
}, false);
@ -115,4 +83,33 @@ var nodebb_admin = (function(nodebb_admin) {
instListEl.innerHTML = '';
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'),
loadMoreEl = document.getElementById('topics_loadmore');
@ -76,7 +79,6 @@ $(document).ready(function() {
topicEls[x].removeAttribute('data-locked');
topicEls[x].removeAttribute('data-deleted');
}
});
socket.on('api:topic.pin', function(response) {
if (response.status === 'ok') {
@ -125,3 +127,7 @@ socket.on('api:topic.restore', function(response) {
$(btnEl).removeClass('active');
}
});
};
return Topics;
});

@ -1,5 +1,7 @@
(function() {
define(function() {
var Users = {};
Users.init = function() {
var yourid = templates.get('yourid');
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'),
room = 'category_' + cid,
twitterEl = document.getElementById('twitter-intent'),
@ -35,36 +38,7 @@
'event:new_topic'
]);
function onNewTopic(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();
}
socket.on('event:new_topic', onNewTopic);
socket.on('event:new_topic', Category.onNewTopic);
socket.emit('api:categories.getRecentReplies', cid);
socket.on('api:categories.getRecentReplies', function (posts) {
@ -97,7 +71,45 @@
$('#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({
topics: topics
@ -113,26 +125,18 @@
}
function loadMoreTopics(cid) {
Category.loadMoreTopics = function(cid) {
loadingMoreTopics = true;
socket.emit('api:category.loadMore', {
cid: cid,
after: $('#topics-container').children().length
}, function (data) {
if (data.topics.length) {
onTopicsLoaded(data.topics);
Category.onTopicsLoaded(data.topics);
}
loadingMoreTopics = false;
});
}
$(window).off('scroll').on('scroll', function (ev) {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics(cid);
}
return Category;
});
})();

@ -1,7 +1,13 @@
(function() {
$(document).ready(function() {
define(['forum/accountheader'], function(header) {
var AccountHeader = {};
AccountHeader.init = function() {
header.init();
$('.user-favourite-posts .topic-row').on('click', function() {
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'),
theirid = templates.get('theirid'),
followersCount = templates.get('followersCount');
$(document).ready(function() {
if (parseInt(followersCount, 10) === 0) {
$('#no-followers-notice').removeClass('hide');
}
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'),
theirid = templates.get('theirid'),
followingCount = templates.get('followingCount');
$(document).ready(function() {
if (parseInt(followingCount, 10) === 0) {
$('#no-following-notice').removeClass('hide');
}
@ -34,7 +36,7 @@
}
app.addCommasToNumbers();
});
};
}());
return Following;
});

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

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

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

@ -1,4 +1,7 @@
(function() {
define(function() {
var ResetPassword = {};
ResetPassword.init = function() {
var inputEl = document.getElementById('email'),
errorEl = document.getElementById('error'),
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 resetEl = document.getElementById('reset'),
@ -49,4 +52,7 @@
$('#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');
$('.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'),
tid = templates.get('topic_id'),
postListEl = document.getElementById('post-container'),
@ -713,4 +716,7 @@
window.onscroll = updateHeader;
window.onload = updateHeader;
})();
};
return Topic;
});

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

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

@ -98,6 +98,3 @@
<input type="hidden" template-variable="yourid" value="{yourid}" />
<input type="hidden" template-variable="theirid" value="{theirid}" />
<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>
<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>
</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-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>
<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>
<script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
require(['forum/admin/settings'], function(Settings) {
Settings.prepare();
});
</script>

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

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

@ -96,5 +96,3 @@
</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}";
</script>
<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">
<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>
@ -25,7 +24,10 @@
<script>
require.config({
baseUrl: "{relative_path}/src/modules",
waitSeconds: 3
waitSeconds: 3,
paths: {
"forum": '../forum'
}
});
</script>
<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>
</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>
<script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
require(['forum/admin/settings'], function(Settings) {
Settings.prepare();
});
</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>.
</p>
</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 />
<label>Site Description</label>
<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>
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
<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>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 />
<div class="checkbox">
<label>
<input type="checkbox" data-field="useOutgoingLinksPage"> <strong>Use Outgoing Links Warning Page</strong>
</label>
</div>
</div>
</form>
<button class="btn btn-lg btn-primary" id="save">Save</button>
<script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
require(['forum/admin/settings'], function(Settings) {
Settings.prepare();
});
</script>

@ -20,7 +20,7 @@ jQuery(document).ready(function () {
slug = 'category/' + category.slug;
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
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.
</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">
<button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button>
</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>
<script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
require(['forum/admin/settings'], function(Settings) {
Settings.prepare();
});
</script>

@ -42,6 +42,3 @@
<button id="load-more-users-btn" class="btn btn-primary">Load More</button>
</div>
<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="facebook-share-url" value="{facebook-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>
<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="theirid" value="{theirid}" />
<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="theirid" value="{theirid}" />
<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>
<script>
$.getScript(RELATIVE_PATH + '/src/forum/footer.js');
require(['forum/footer']);
</script>
</body>

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

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

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

@ -80,5 +80,3 @@
</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>
</form>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/reset.js"></script>

@ -34,6 +34,3 @@
</form>
</div>
<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>
</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="topic_name" value="{topic_name}" />
<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>
<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>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/users.js"></script>

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

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

@ -15,28 +15,21 @@ var RDB = require('./redis.js'),
});
});
},
create: function(text, score, path, uniqueId, callback) {
/*
* Score guide:
* 0 Low priority messages (probably unused)
* 5 Normal messages
* 10 High priority messages
*
create: function(text, path, uniqueId, callback) {
/**
* 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
* (un)read list contains the same uniqueId, it will be removed, and
* the new one put in its place.
*/
RDB.incr('notifications:next_nid', function(err, nid) {
RDB.hmset(
'notifications:' + nid,
'text', text || '',
'score', score || 5,
'path', path || null,
'datetime', Date.now(),
'uniqueId', uniqueId || utils.generateUUID(),
function(err, status) {
if (status === 'OK') callback(nid);
RDB.hmset('notifications:' + nid, {
text: text || '',
path: path || null,
datetime: Date.now(),
uniqueId: uniqueId || utils.generateUUID()
}, function(err, status) {
if (!err) callback(nid);
});
});
},
@ -51,7 +44,7 @@ var RDB = require('./redis.js'),
if (parseInt(uids[x]) > 0) {
(function(uid) {
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');
if (callback) callback(true);
});
@ -98,7 +91,7 @@ var RDB = require('./redis.js'),
if (parseInt(uid) > 0) {
notifications.get(nid, function(notif_data) {
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();
});
}

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

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

@ -17,6 +17,17 @@
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) {
if (error !== null) {
winston.err(error);

@ -28,6 +28,7 @@ var user = require('./../user.js'),
config.minimumUsernameLength = meta.config.minimumUsernameLength;
config.maximumUsernameLength = meta.config.maximumUsernameLength;
config.minimumPasswordLength = meta.config.minimumPasswordLength;
config.useOutgoingLinksPage = meta.config.useOutgoingLinksPage;
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) {
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) {
async.parallel([
function(next) {
topics.getTopicField(tid, 'title', function(err, title) {
topics.getTeaser(tid, function(err, teaser) {
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);
});
} else next(err);
});
});
},
function(next) {
ThreadTools.get_followers(tid, function(err, followers) {
exceptUid = parseInt(exceptUid, 10);
if (followers.indexOf(exceptUid) !== -1) followers.splice(followers.indexOf(exceptUid), 1);
next(null, followers);
});

@ -15,15 +15,18 @@ schema = require('./schema.js'),
topicSearch = reds.createSearch('nodebbtopicsearch'),
validator = require('validator');
(function(Topics) {
Topics.getTopicData = function(tid, callback) {
RDB.hgetall('topic:' + tid, function(err, data) {
if (err === null)
if (err === null) {
if(data)
data.title = validator.sanitize(data.title).escape();
callback(data);
else
} else {
console.log(err);
}
});
}
@ -95,7 +98,7 @@ schema = require('./schema.js'),
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) {
@ -658,7 +661,6 @@ schema = require('./schema.js'),
var slug = tid + '/' + utils.slugify(title);
var timestamp = Date.now();
title = validator.sanitize(title).escape();
RDB.hmset('topic:' + tid, {
'tid': tid,
'uid': uid,

@ -1,193 +1,74 @@
var RDB = require('./redis.js'),
async = require('async'),
winston = require('winston'),
user = require('./user'),
Groups = require('./groups');
notifications = require('./notifications')
Upgrade = {};
Upgrade.upgrade = function() {
winston.info('Beginning Redis database schema update');
function upgradeCategory(cid, callback) {
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);
},
async.series([
function(next) {
if (userData.postcount)
RDB.zadd('users:postcount', userData.postcount, uid, next);
else
next(null);
},
RDB.hget('notifications:1', 'score', function(err, score) {
if (score) {
async.series([
function(next) {
if (userData.reputation)
RDB.zadd('users:reputation', userData.reputation, uid, next);
else
next(null);
RDB.keys('uid:*:notifications:flag', function(err, keys) {
if (keys.length > 0) {
winston.info('[2013/10/03] Removing deprecated Notification Flags');
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) {
if (userData.userslug)
RDB.hset('userslug:uid', userData.userslug, uid, next);
else
next(null);
},
function(next) {
if (userData.email)
RDB.hset('email:uid', userData.email, uid, next);
else
next(null);
}
], function(err, result) {
callback(err);
winston.info('[2013/10/03] Updating Notifications');
RDB.keys('uid:*:notifications:*', function(err, keys) {
async.each(keys, function(key, next) {
RDB.zrange(key, 0, -1, function(err, nids) {
async.each(nids, function(nid, next) {
notifications.get(nid, function(notif_data) {
RDB.zadd(key, notif_data.datetime, nid, next);
});
}, 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) {
Groups.create('Administrators', 'Forum Administrators', next);
}
], function(err, results) {
var gid = results[1].gid;
async.each(results[0], function(uid, next) {
Groups.join(gid, uid, next);
}, callback);
});
RDB.keys('notifications:*', function(err, keys) {
if (keys.length > 0) {
winston.info('[2013/10/03] Removing Notification Scores');
async.each(keys, function(key, next) {
if (key === 'notifications:next_nid') return next();
RDB.hdel(key, 'score', next);
}, next);
} else {
winston.info('Administrators group OK')
callback();
winston.info('[2013/10/03] No Notification Scores found. Good.');
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);
], next);
} 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);
}
});
});
},
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) {
// Add new schema updates here
], function(err) {
if (!err) {
winston.info('upgraded user hashes');
next(null, null);
winston.info('Redis schema update complete!');
process.exit();
} 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) {
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);
});
});
@ -888,7 +888,7 @@ var utils = require('./../public/src/utils.js'),
async.parallel({
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
var unread = [];
@ -910,7 +910,7 @@ var utils = require('./../public/src/utils.js'),
});
},
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
var read = [];
@ -932,22 +932,6 @@ var utils = require('./../public/src/utils.js'),
});
}
}, 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
if (notifications.read.length + notifications.unread.length > maxNotifs) {
notifications.read.length = maxNotifs - notifications.unread.length;

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

@ -580,7 +580,7 @@ module.exports.init = function(io) {
notifText = 'New message from <strong>' + username + '</strong>';
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) {
});

Loading…
Cancel
Save