diff --git a/package.json b/package.json
index a440575644..e4f665bfc6 100644
--- a/package.json
+++ b/package.json
@@ -38,8 +38,8 @@
"prompt": "~0.2.11",
"uglify-js": "~2.4.0",
"validator": "~1.5.1",
- "nodebb-plugin-mentions": "~0.1.16",
- "nodebb-plugin-markdown": "~0.2.1",
+ "nodebb-plugin-mentions": "~0.1",
+ "nodebb-plugin-markdown": "~0.3",
"nodebb-theme-vanilla": "~0.0.12",
"nodebb-theme-cerulean": "0.0.10",
"cron": "~1.0.1",
diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js
index 192a707142..f148dc2d49 100644
--- a/public/src/modules/composer.js
+++ b/public/src/modules/composer.js
@@ -94,9 +94,8 @@ define(['taskbar'], function(taskbar) {
var postContainer = $(composerTemplate[0]);
- if(config.imgurClientIDSet) {
+ if(config.allowFileUploads || config.imgurClientIDSet)
initializeFileReader(post_uuid);
- }
var postData = composer.posts[post_uuid],
titleEl = postContainer.find('.title'),
diff --git a/public/templates/admin/settings.tpl b/public/templates/admin/settings.tpl
index 5f334cad02..aa364e4742 100644
--- a/public/templates/admin/settings.tpl
+++ b/public/templates/admin/settings.tpl
@@ -123,7 +123,7 @@
diff --git a/public/templates/category.tpl b/public/templates/category.tpl
index 8b56a89107..30dd871851 100644
--- a/public/templates/category.tpl
+++ b/public/templates/category.tpl
@@ -10,12 +10,13 @@
-
+
+
diff --git a/src/categories.js b/src/categories.js
index ac65ae2405..b511ede803 100644
--- a/src/categories.js
+++ b/src/categories.js
@@ -80,6 +80,7 @@ var db = require('./database.js'),
'category_id': category_id,
'active_users': [],
'topics': [],
+ 'disableSocialButtons': meta.config.disableSocialButtons !== undefined ? parseInt(meta.config.disableSocialButtons, 10) !== 0 : false,
'twitter-intent-url': 'https://twitter.com/intent/tweet?url=' + encodeURIComponent(nconf.get('url') + 'category/' + category_slug) + '&text=' + encodeURIComponent(category_name),
'facebook-share-url': 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(nconf.get('url') + 'category/' + category_slug),
'google-share-url': 'https://plus.google.com/share?url=' + encodeURIComponent(nconf.get('url') + 'category/' + category_slug),
diff --git a/src/database/mongo.js b/src/database/mongo.js
index f9b6ee8eea..fceb39d00f 100644
--- a/src/database/mongo.js
+++ b/src/database/mongo.js
@@ -50,13 +50,20 @@
function createCollections() {
db.createCollection('objects', function(err, collection) {
if(err) {
- winston.error("Error creating collection " + err.message);
+ winston.error('Error creating collection ' + err.message);
return;
}
+
if(collection) {
- collection.ensureIndex({_key :1}, {background:true}, function(err, name){
+ collection.ensureIndex({_key :1}, {background:true}, function(err, name) {
if(err) {
- winston.error("Error creating index " + err.message);
+ winston.error('Error creating index ' + err.message);
+ }
+ });
+
+ collection.ensureIndex({'expireAt':1}, {expireAfterSeconds:0, background:true}, function(err, name) {
+ if(err) {
+ winston.error('Error creating index ' + err.message);
}
});
}
@@ -64,13 +71,13 @@
db.createCollection('search', function(err, collection) {
if(err) {
- winston.error("Error creating collection " + err.message);
+ winston.error('Error creating collection ' + err.message);
return;
}
if(collection) {
collection.ensureIndex({content:'text'}, {background:true}, function(err, name){
if(err) {
- winston.error("Error creating index " + err.message);
+ winston.error('Error creating index ' + err.message);
}
});
}
@@ -241,6 +248,14 @@
});
}
+ module.expire = function(key, seconds, callback) {
+ module.expireAt(key, Math.round(Date.now() / 1000) + seconds, callback);
+ }
+
+ module.expireAt = function(key, timestamp, callback) {
+ module.setObjectField(key, 'expireAt', new Date(timestamp * 1000), callback);
+ }
+
//hashes
module.setObject = function(key, data, callback) {
data['_key'] = key;
@@ -254,6 +269,9 @@
module.setObjectField = function(key, field, value, callback) {
var data = {};
// if there is a '.' in the field name it inserts subdocument in mongo, replace '.'s with \uff0E
+ if(typeof field !== 'string') {
+ field = field.toString();
+ }
field = field.replace(/\./g, '\uff0E');
data[field] = value;
db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
@@ -303,7 +321,7 @@
var _fields = {};
for(var i=0; i
0) {
return opts.express.ofn(req,res,next);
- }
- else {
+ } else {
return next();
}
}
@@ -140,11 +150,13 @@ var opts = {
for(var v in clients) {
var client = clients[v];
- if(client.oEmit != undefined && client.oEmit != client.emit)
+ if(client.oEmit != undefined && client.oEmit != client.emit) {
client.emit = client.oEmit;
+ }
- if(client.$oEmit != undefined && client.$oEmit != client.$emit)
+ if(client.$oEmit != undefined && client.$oEmit != client.$emit) {
client.$emit = client.$oEmit;
+ }
}
}
diff --git a/src/postTools.js b/src/postTools.js
index 5af411e55e..37d1ac0a53 100644
--- a/src/postTools.js
+++ b/src/postTools.js
@@ -152,7 +152,7 @@ var winston = require('winston'),
// Delete the thread if it is the last undeleted post
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
if (err && err.message === 'no-undeleted-pids-found') {
- threadTools.delete(postData.tid, function(err) {
+ threadTools.delete(postData.tid, uid, function(err) {
if (err) {
winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
}
diff --git a/src/posts.js b/src/posts.js
index 0eb19b4a24..0ab4222321 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -360,24 +360,26 @@ var db = require('./database'),
Posts.uploadPostImage = function(image, callback) {
- if(!meta.config.imgurClientID) {
- return callback('imgurClientID not set', null);
- }
+ if(meta.config.imgurClientID) {
+ if(!image) {
+ return callback('invalid image', null);
+ }
- if(!image) {
- return callback('invalid image', null);
+ require('./imgur').upload(meta.config.imgurClientID, image.data, 'base64', function(err, data) {
+ if(err) {
+ callback(err.message, null);
+ } else {
+ callback(null, {
+ url: data.link,
+ name: image.name
+ });
+ }
+ });
+ } else if (meta.config.allowFileUploads) {
+ Posts.uploadPostFile(image, callback);
+ } else {
+ callback('Uploads are disabled!');
}
-
- require('./imgur').upload(meta.config.imgurClientID, image.data, 'base64', function(err, data) {
- if(err) {
- callback(err.message, null);
- } else {
- callback(null, {
- url: data.link,
- name: image.name
- });
- }
- });
}
Posts.uploadPostFile = function(file, callback) {
@@ -400,7 +402,7 @@ var db = require('./database'),
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
fs.writeFile(uploadPath, buffer, function (err) {
- if(err) {
+ if(err) {
callback(err.message, null);
} else {
callback(null, {
diff --git a/src/routes/api.js b/src/routes/api.js
index 3f0dbc270c..c637dd58f5 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -66,7 +66,7 @@ var path = require('path'),
data.motd_class = (parseInt(meta.config.show_motd, 10) === 1 || meta.config.show_motd === undefined) ? '' : ' none';
data.motd_class += (meta.config.motd && meta.config.motd.length > 0 ? '' : ' default');
- data.motd = require('marked')(meta.config.motd || "\n\n# NodeBB v" + pkg.version + "\nWelcome to NodeBB, the discussion platform of the future.");
+ data.motd = require('marked')(meta.config.motd || "\n\n# NodeBB v" + pkg.version + "\nWelcome to NodeBB, the discussion platform of the future.");
res.json(data);
});
});
diff --git a/src/routes/user.js b/src/routes/user.js
index dd5491b43e..144cf08508 100644
--- a/src/routes/user.js
+++ b/src/routes/user.js
@@ -539,7 +539,7 @@ var fs = require('fs'),
if (!data.birthday) {
data.age = '';
} else {
- data.age = new Date().getFullYear() - new Date(data.birthday).getFullYear();
+ data.age = Math.floor((new Date().getTime() - new Date(data.birthday).getTime()) / 31536000000);
}
function canSeeEmail() {
@@ -581,4 +581,4 @@ var fs = require('fs'),
};
-}(exports));
\ No newline at end of file
+}(exports));
diff --git a/src/threadTools.js b/src/threadTools.js
index 859d6faac4..b69c42fa7c 100644
--- a/src/threadTools.js
+++ b/src/threadTools.js
@@ -91,7 +91,7 @@ var winston = require('winston'),
}
}
- ThreadTools.delete = function(tid, callback) {
+ ThreadTools.delete = function(tid, uid, callback) {
topics.delete(tid);
db.decrObjectField('global', 'topicCount');
@@ -112,7 +112,7 @@ var winston = require('winston'),
}
}
- ThreadTools.restore = function(tid, socket, callback) {
+ ThreadTools.restore = function(tid, uid, callback) {
topics.restore(tid);
db.incrObjectField('global', 'topicCount');
ThreadTools.unlock(tid);
diff --git a/src/webserver.js b/src/webserver.js
index e5e3915ec5..035e8ae0b3 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -55,7 +55,7 @@ var path = require('path'),
/**
* `options` object requires: req, res
- * accepts: metaTags
+ * accepts: metaTags, linkTags
*/
app.build_header = function (options, callback) {
var custom_header = {
@@ -83,8 +83,6 @@ var path = require('path'),
rel: 'apple-touch-icon',
href: meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'
}],
- metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])),
- linkTags = utils.buildLinkTags(defaultLinkTags.concat(options.linkTags || [])),
templateValues = {
cssSrc: meta.config['theme:src'] || nconf.get('relative_path') + '/vendor/bootstrap/css/bootstrap.min.css',
pluginCSS: plugins.cssFiles.map(function(file) { return { path: file + (meta.config['cache-buster'] ? '?v=' + meta.config['cache-buster'] : '') }; }),
@@ -96,16 +94,30 @@ var path = require('path'),
browserTitle: meta.config.title || 'NodeBB',
csrf: options.res.locals.csrf_token,
relative_path: nconf.get('relative_path'),
- meta_tags: metaString,
- link_tags: linkTags,
clientScripts: clientScripts,
navigation: custom_header.navigation,
'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
allowRegistration: meta.config.allowRegistration === undefined || parseInt(meta.config.allowRegistration, 10) === 1
+ },
+ escapeList = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ "'": ''',
+ '"': '"'
};
var uid = '0';
+ // Meta Tags
+ templateValues.meta_tags = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || []).map(function(tag) {
+ tag.content = tag.content.replace(/[&<>'"]/g, function(tag) {
+ return escapeList[tag] || tag;
+ });
+ return tag;
+ }));
+ templateValues.link_tags = utils.buildLinkTags(defaultLinkTags.concat(options.linkTags || []));
+
if(options.req.user && options.req.user.uid) {
uid = options.req.user.uid;
}
diff --git a/src/websockets.js b/src/websockets.js
index b435d4b6ec..f6bf315df3 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -562,7 +562,7 @@ websockets.init = function(io) {
socket.on('api:topic.delete', function(data) {
threadTools.privileges(data.tid, uid, function(err, privileges) {
if (!err && privileges.editable) {
- threadTools.delete(data.tid, function(err) {
+ threadTools.delete(data.tid, uid, function(err) {
if (!err) {
emitTopicPostStats();
socket.emit('api:topic.delete', {
@@ -578,7 +578,7 @@ websockets.init = function(io) {
socket.on('api:topic.restore', function(data) {
threadTools.privileges(data.tid, uid, function(err, privileges) {
if (!err && privileges.editable) {
- threadTools.restore(data.tid, socket, function(err) {
+ threadTools.restore(data.tid, uid, function(err) {
emitTopicPostStats();
socket.emit('api:topic.restore', {