diff --git a/package.json b/package.json index 6a039fd17c..41d1b7a637 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "request": "~2.25.0", "reds": "~0.2.4", "winston": "~0.7.2", - "nodebb-plugin-mentions": "~0.1.0" + "nodebb-plugin-mentions": "~0.1.0", + "nodebb-plugin-markdown": "~0.1.0" }, "bugs": { "url": "https://github.com/designcreateplay/NodeBB/issues" diff --git a/public/src/forum/admin/users.js b/public/src/forum/admin/users.js index d1fc9ab940..7f67949909 100644 --- a/public/src/forum/admin/users.js +++ b/public/src/forum/admin/users.js @@ -1,6 +1,8 @@ (function() { + var yourid = templates.get('yourid'); + function initUsers() { function isUserAdmin(element) { @@ -10,29 +12,32 @@ function isUserBanned(element) { var parent = $(element).parents('.users-box'); - return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0"); + return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0"); } function getUID(element) { var parent = $(element).parents('.users-box'); - return parent.attr('data-uid'); + return parent.attr('data-uid'); } jQuery('.admin-btn').each(function(index, element) { var adminBtn = $(element); var isAdmin = isUserAdmin(adminBtn); - + var uid = getUID(adminBtn); + if(isAdmin) adminBtn.addClass('btn-success'); else adminBtn.removeClass('btn-success'); + if(uid === yourid) + adminBtn.addClass('disabled'); }); jQuery('.delete-btn').each(function(index, element) { var deleteBtn = $(element); var isAdmin = isUserAdmin(deleteBtn); - + if(isAdmin) deleteBtn.addClass('disabled'); else @@ -43,12 +48,12 @@ var banBtn = $(element); var isAdmin = isUserAdmin(banBtn); var isBanned = isUserBanned(banBtn); - + if(isAdmin) banBtn.addClass('disabled'); else if(isBanned) banBtn.addClass('btn-warning'); - else + else banBtn.removeClass('btn-warning'); }); @@ -59,13 +64,12 @@ var parent = adminBtn.parents('.users-box'); var uid = getUID(adminBtn); - if(isAdmin) { - socket.emit('api:admin.user.removeAdmin', uid); + if(isAdmin && uid != yourid) { + socket.emit('api:admin.user.removeAdmin', uid); adminBtn.removeClass('btn-success'); parent.find('.delete-btn').removeClass('disabled'); parent.attr('data-admin', 0); - } - else { + } else if(uid != yourid) { bootbox.confirm('Do you really want to make "' + parent.attr('data-username') +'" an admin?', function(confirm) { if(confirm) { socket.emit('api:admin.user.makeAdmin', uid); @@ -75,7 +79,7 @@ } }); } - + return false; }); @@ -87,10 +91,10 @@ if(!isAdmin) { bootbox.confirm('Do you really want to delete "' + parent.attr('data-username') +'"?', function(confirm) { - socket.emit('api:admin.user.deleteUser', uid); + socket.emit('api:admin.user.deleteUser', uid); }); } - + return false; }); @@ -108,13 +112,13 @@ parent.attr('data-banned', 0); } else { bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') +'"?', function(confirm) { - socket.emit('api:admin.user.banUser', uid); + socket.emit('api:admin.user.banUser', uid); banBtn.addClass('btn-warning'); parent.attr('data-banned', 1); }); } } - + return false; }); } @@ -122,8 +126,7 @@ jQuery('document').ready(function() { - var yourid = templates.get('yourid'), - timeoutId = 0, + var timeoutId = 0, loadingMoreUsers = false; var url = window.location.href, @@ -146,17 +149,17 @@ timeoutId = setTimeout(function() { var username = $('#search-user').val(); - + jQuery('.icon-spinner').removeClass('none'); socket.emit('api:admin.user.search', username); - + }, 250); }); - + initUsers(); - + socket.removeAllListeners('api:admin.user.search'); - + socket.on('api:admin.user.search', function(data) { var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ users: data @@ -164,7 +167,7 @@ userListEl = document.querySelector('.users'); userListEl.innerHTML = html; - jQuery('.icon-spinner').addClass('none'); + jQuery('.icon-spinner').addClass('none'); if(data && data.length === 0) { $('#user-notfound-notify').html('User not found!') @@ -178,10 +181,10 @@ .addClass('label-success') .removeClass('label-danger'); } - + initUsers(); }); - + function onUsersLoaded(users) { var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ users: users }); $('#users-container').append(html); @@ -200,8 +203,8 @@ if(set) { loadingMoreUsers = true; socket.emit('api:users.loadMore', { - set: set, - after: $('#users-container').children().length + set: set, + after: $('#users-container').children().length }, function(data) { if(data.users.length) { onUsersLoaded(data.users); diff --git a/public/src/forum/footer.js b/public/src/forum/footer.js index 4d53a81b92..c892b300eb 100644 --- a/public/src/forum/footer.js +++ b/public/src/forum/footer.js @@ -132,6 +132,7 @@ }); } }); + notifList.addEventListener('click', function(e) { var target; switch(e.target.nodeName) { @@ -144,6 +145,7 @@ if (nid > 0) socket.emit('api:notifications.mark_read', nid); } }); + socket.on('event:new_notification', function() { document.querySelector('.notifications a i').className = 'icon-circle active'; app.alert({ @@ -158,19 +160,26 @@ socket.on('chatMessage', function(data) { - var username = data.username; - var fromuid = data.fromuid; - var message = data.message; require(['chat'], function(chat) { - var chatModal = chat.createModalIfDoesntExist(username, fromuid); + var chatModal = chat.createModalIfDoesntExist(data.username, data.fromuid, function(created, modal) { + if(!created) + chat.appendChatMessage(modal, data.message, data.timestamp); + }); + chatModal.show(); chat.bringModalToTop(chatModal); - - chat.appendChatMessage(chatModal, message); }); }); + socket.on('chatGoOffline', function(data) { + require(['chat'], function(chat) { + if(chat.modalOpen(data.uid)) { + var modal = chat.getModal(data.uid); + chat.appendChatMessage(modal, data.username + ' went offline\n', data.timestamp); + } + }); + }) require(['mobileMenu'], function(mobileMenu) { mobileMenu.init(); diff --git a/public/src/forum/register.js b/public/src/forum/register.js index b8525255ca..a312a542b2 100644 --- a/public/src/forum/register.js +++ b/public/src/forum/register.js @@ -1,161 +1,151 @@ (function() { - var username = document.getElementById('username'), - password = document.getElementById('password'), - password_confirm = document.getElementById('password-confirm'), - register = document.getElementById('register'), - emailEl = document.getElementById('email'), - username_notify = document.getElementById('username-notify'), - email_notify = document.getElementById('email-notify'), - password_notify = document.getElementById('password-notify'), - password_confirm_notify = document.getElementById('password-confirm-notify'), - usernamevalid = false; - emailexists = false, - emailvalid = false, - userexists = false, - passwordsmatch = false, - passwordvalid = false; + var username = $('#username'), + password = $('#password'), + password_confirm = $('#password-confirm'), + register = $('#register'), + emailEl = $('#email'), + username_notify = $('#username-notify'), + email_notify = $('#email-notify'), + password_notify = $('#password-notify'), + password_confirm_notify = $('#password-confirm-notify'), + validationError = false, + successIcon = ''; - $(username).on('keyup change', function() { - usernamevalid = utils.isUserNameValid(username.value); + function showError(element, msg) { + element.html(msg); + element.parent().removeClass('alert-success').addClass('alert-danger'); + element.show(); + validationError = true; + } - - if(username.value.length < 3) { - username_notify.innerHTML = 'Username too short'; - username_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - } else if(username.value.length > 13) { - username_notify.innerHTML = 'Username too long'; - username_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - } else if(!usernamevalid) { - username_notify.innerHTML = 'Invalid username'; - username_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - } else { - socket.emit('user.exists', {username: username.value}); + function showSuccess(element, msg) { + element.html(msg); + element.parent().removeClass('alert-danger').addClass('alert-success'); + element.show(); + } + + function validateEmail() { + if(!emailEl.val()) { + validationError = true; + //email_notify.hide(); + return; } - }); - $(emailEl).on('keyup change', function() { - emailvalid = utils.isEmailValid(email.value); - - if(!emailvalid) { - email_notify.innerHTML = 'Invalid email address'; - email_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; + if(!utils.isEmailValid(emailEl.val())) { + showError(email_notify, 'Invalid email address.'); } else - socket.emit('user.email.exists', { email: emailEl.value }); + socket.emit('user.email.exists', { email: emailEl.val() }); + } + + emailEl.on('blur', function() { + validateEmail(); }); - - $(password).on('keyup', function() { - passwordvalid = utils.isPasswordValid(password.value); - if (password.value.length < 6) { - password_notify.innerHTML = 'Password too short'; - password_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - } else if(!passwordvalid) { - password_notify.innerHTML = 'Invalid password'; - password_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - } else { - password_notify.innerHTML = ''; - password_notify.parentNode.className = 'input-group-addon btn-success label-success'; + + function validateUsername() { + if(!username.val()) { + validationError = true; + //username_notify.hide(); + return; } - - if(password.value !== password_confirm.value && password_confirm.value.length > 0) { - password_confirm_notify.innerHTML = 'Passwords must match!'; - password_confirm_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - passwordsmatch = false; - } else if (password.value === password_confirm.value && password_confirm.value.length > 0) { - password_confirm_notify.innerHTML = ''; - password_confirm_notify.parentNode.className = 'input-group-addon btn-success label-success'; - passwordsmatch = true; + + if(username.val().length < config.minimumUsernameLength) { + showError(username_notify, 'Username too short!'); + } else if(username.val().length > config.maximumUsernameLength) { + showError(username_notify, 'Username too long!'); + } else if(!utils.isUserNameValid(username.val())) { + showError(username_notify, 'Invalid username!'); + } else { + socket.emit('user.exists', {username: username.val()}); } + } + + username.on('keyup', function() { + jQuery('#yourUsername').html(this.value.length > 0 ? this.value : 'username'); }); - - $(password_confirm).on('keyup', function() { - if(password.value !== password_confirm.value) { - password_confirm_notify.innerHTML = 'Passwords must match!'; - password_confirm_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; - passwordsmatch = false; + username.on('blur', function() { + validateUsername(); + }); + + function validatePassword() { + if(!password.val()){ + validationError = true; + //password_notify.hide(); + return; } - else { - password_confirm_notify.innerHTML = ''; - password_confirm_notify.parentNode.className = 'input-group-addon btn-success label-success'; - passwordsmatch = true; + + if (password.val().length < config.minimumPasswordLength) { + showError(password_notify, 'Password too short!'); + } else if(password.val().length > config.maximumPasswordLength) { + showError(password_notify, 'Password too long!'); + } else if(!utils.isPasswordValid(password.val())) { + showError(password_notify, 'Invalid password!'); + } else { + showSuccess(password_notify, successIcon); } + + if(password.val() !== password_confirm.val() && password_confirm.val() !== '') { + showError(password_confirm_notify, 'Passwords must match!'); + } + } + + $(password).on('blur', function() { + validatePassword(); + }); + + function validatePasswordConfirm() { + if(!password.val() || password_notify.hasClass('alert-error')) { + //password_confirm_notify.hide(); + return; + } + + if(password.val() !== password_confirm.val()) { + showError(password_confirm_notify, 'Passwords must match!'); + } else { + showSuccess(password_confirm_notify, successIcon); + } + } + + $(password_confirm).on('blur', function() { + validatePasswordConfirm(); }); ajaxify.register_events(['user.exists', 'user.email.exists']); socket.on('user.exists', function(data) { - userexists = data.exists; if (data.exists === true) { - username_notify.innerHTML = 'Username exists'; - username_notify.parentNode.className = 'input-group-addon btn-warning label-warning'; + showError(username_notify, 'Username already taken!'); } else { - username_notify.innerHTML = ''; - username_notify.parentNode.className = 'input-group-addon btn-success label-success'; + showSuccess(username_notify, successIcon); } }); - - socket.on('user.email.exists', function(data) { - emailexists = data.exists; + socket.on('user.email.exists', function(data) { if (data.exists === true) { - email_notify.innerHTML = 'Email Address exists'; - email_notify.className = 'label label-warning'; - } - else { - email_notify.innerHTML = ''; - email_notify.parentNode.className = 'input-group-addon btn-success label-success'; + showError(email_notify, 'Email address already taken!'); + } else { + showSuccess(email_notify, successIcon); } }); // Alternate Logins - var altLoginEl = document.querySelector('.alt-logins'); - altLoginEl.addEventListener('click', function(e) { - var target; - switch(e.target.nodeName) { - case 'LI': target = e.target; break; - case 'I': target = e.target.parentNode; break; - } - if (target) { - document.location.href = target.getAttribute('data-url'); - } + $('.alt-logins li').on('click', function(e) { + document.location.href = $(this).attr('data-url'); }); function validateForm() { - var validated = true; - - if (username.value.length < 2 || !usernamevalid) { - username_notify.innerHTML = 'Invalid username'; - username_notify.className = 'label label-warning'; - validated = false; - } + validationError = false; - if (password.value.length < 5) { - password_notify.innerHTML = 'Password too short'; - validated = false; - } - - if(password.value !== password_confirm.value) { - password_confirm_notify.innerHTML = 'Passwords must match!'; - } + validateEmail(); + validateUsername(); + validatePassword(); + validatePasswordConfirm(); - if (!emailvalid) { - email_notify.innerHTML = 'Invalid email address'; - validated = false; - } - - if(emailexists) { - email_notify.innerHTML = 'Email Address exists'; - validated = false; - } - - if(userexists || !passwordsmatch || !passwordvalid) - validated = false; - - return validated; + return validationError; } - - register.addEventListener('click', function(e) { - if (!validateForm()) e.preventDefault(); - }, false); - -}()); + + register.on('click', function(e) { + if (validateForm()) e.preventDefault(); + }); + +}()); \ No newline at end of file diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 0b850b5889..704bf16b5a 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -13,12 +13,19 @@ define(['taskbar'], function(taskbar) { chatModal.css('zIndex', topZ + 1); } - module.createModalIfDoesntExist = function(username, touid) { - var chatModal = $('#chat-modal-'+touid); + module.getModal = function(touid) { + return $('#chat-modal-' + touid); + } + + module.modalOpen = function(touid) { + return $('#chat-modal-' + touid).length !== 0; + } + module.createModalIfDoesntExist = function(username, touid, callback) { + var chatModal = $('#chat-modal-' + touid); if(!chatModal.length) { var chatModal = $('#chat-modal').clone(); - chatModal.attr('id','chat-modal-'+touid); + chatModal.attr('id','chat-modal-' + touid); var uuid = utils.generateUUID(); chatModal.attr('UUID', uuid); chatModal.appendTo($('body')); @@ -29,7 +36,7 @@ define(['taskbar'], function(taskbar) { }); chatModal.find('#chat-with-name').html(username); - chatModal.find('.close').on('click',function(e){ + chatModal.find('.close').on('click', function(e) { chatModal.hide(); taskbar.discard('chat', uuid); }); @@ -40,9 +47,15 @@ define(['taskbar'], function(taskbar) { addSendHandler(chatModal, touid); - getChatMessages(chatModal, touid); + getChatMessages(chatModal, touid, callback); + + taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username}); + return chatModal; } + if(callback) + callback(false, chatModal); + taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username}); return chatModal; } @@ -59,11 +72,14 @@ define(['taskbar'], function(taskbar) { taskbar.minimize('chat', uuid); } - function getChatMessages(chatModal, touid) { + function getChatMessages(chatModal, touid, callback) { socket.emit('getChatMessages', {touid:touid}, function(messages) { for(var i = 0; i 5; + return password && password.indexOf(' ') === -1; }, // Blatently stolen from: http://phpjs.org/functions/strip_tags/ diff --git a/public/templates/admin/settings.tpl b/public/templates/admin/settings.tpl index b478766f63..f740b46799 100644 --- a/public/templates/admin/settings.tpl +++ b/public/templates/admin/settings.tpl @@ -50,6 +50,14 @@ + + User Settings + + Minimum Username Length + Maximum Username Length + + + Post Settings diff --git a/public/templates/register.tpl b/public/templates/register.tpl index 8881f04c33..7b50f51566 100644 --- a/public/templates/register.tpl +++ b/public/templates/register.tpl @@ -15,11 +15,12 @@ Email Address - + + By default, your email will be hidden from the public. @@ -31,6 +32,7 @@ + A unique username. {minimumUsernameLength}-{maximumUsernameLength} characters. Others can mention you with @username. @@ -42,6 +44,7 @@ + Your password's length must be {minimumPasswordLength}-{maximumPasswordLength} characters. @@ -57,7 +60,8 @@ - Register Now + + Register Now diff --git a/src/categories.js b/src/categories.js index 591010ac35..dab6475543 100644 --- a/src/categories.js +++ b/src/categories.js @@ -74,6 +74,7 @@ var RDB = require('./redis.js'), categoryData.moderator_block_class = results[1].length > 0 ? '' : 'none'; categoryData.moderators = results[1]; categoryData.active_users = results[2]; + categoryData.show_sidebar = categoryData.topics.length > 0 ? 'show':'hidden'; callback(null, categoryData); }); } @@ -234,6 +235,26 @@ var RDB = require('./redis.js'), }); } + Categories.moveActiveUsers = function(tid, oldCid, cid, callback) { + topics.getUids(tid, function(err, uids) { + if(!err && uids) { + function updateUser(uid) { + Categories.addActiveUser(cid, uid); + Categories.isUserActiveIn(oldCid, uid, function(err, active) { + + if(!err && !active) { + Categories.removeActiveUser(oldCid, uid); + } + }); + } + + for(var i=0; i 0) { + var parsedContentDOM = cheerio.load(parsed); + var domain = nconf.get('url'); + + parsedContentDOM('a').each(function() { + this.attr('rel', 'nofollow'); + var href = this.attr('href'); + + if (href && !href.match(domain) && !utils.isRelativeUrl(href)) { + this.attr('href', domain + 'outgoing?url=' + encodeURIComponent(href)); + } + }); + + callback(null, parsedContentDOM.html()); + } else { + callback(null, ''); + } }); - - if (md && md.length > 0) { - var parsedContentDOM = cheerio.load(marked(md)); - var domain = nconf.get('url'); - - parsedContentDOM('a').each(function() { - this.attr('rel', 'nofollow'); - var href = this.attr('href'); - - if (href && !href.match(domain) && !utils.isRelativeUrl(href)) { - this.attr('href', domain + 'outgoing?url=' + encodeURIComponent(href)); - if (!isSignature) this.append(' '); - } - }); - - - html = parsedContentDOM.html(); - } else { - html = ''; - } - - return html; } diff --git a/src/posts.js b/src/posts.js index 4e57088ca7..d292f86523 100644 --- a/src/posts.js +++ b/src/posts.js @@ -34,28 +34,29 @@ var RDB = require('./redis.js'), Posts.addUserInfoToPost = function(post, callback) { user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(err, userData) { - if(err) - return callback(); + if(err) return callback(); - post.username = userData.username || 'anonymous'; - post.userslug = userData.userslug || ''; - post.user_rep = userData.reputation || 0; - post.user_postcount = userData.postcount || 0; - post.user_banned = userData.banned || '0'; - post.picture = userData.picture || require('gravatar').url('', {}, https=nconf.get('https')); - post.signature = postTools.markdownToHTML(userData.signature, true); + postTools.toHTML(userData.signature, function(err, signature) { + post.username = userData.username || 'anonymous'; + post.userslug = userData.userslug || ''; + post.user_rep = userData.reputation || 0; + post.user_postcount = userData.postcount || 0; + post.user_banned = userData.banned || '0'; + post.picture = userData.picture || require('gravatar').url('', {}, https=nconf.get('https')); + post.signature = signature; - if(post.editor !== '') { - user.getUserFields(post.editor, ['username', 'userslug'], function(err, editorData) { - if(err) - return callback(); - post.editorname = editorData.username; - post.editorslug = editorData.userslug; + if(post.editor !== '') { + user.getUserFields(post.editor, ['username', 'userslug'], function(err, editorData) { + if(err) return callback(); + + post.editorname = editorData.username; + post.editorslug = editorData.userslug; + callback(); + }); + } else { callback(); - }); - } else { - callback(); - } + } + }); }); } @@ -64,28 +65,41 @@ var RDB = require('./redis.js'), var posts = []; function getPostSummary(pid, callback) { - Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) { - if(postData.deleted === '1') { - return callback(null); - } - - Posts.addUserInfoToPost(postData, function() { - topics.getTopicFields(postData.tid, ['slug', 'deleted'], function(err, topicData) { - if(err) - return callback(err); - - if(topicData.deleted === '1') - return callback(null); - - if(postData.content) - postData.content = utils.strip_tags(postTools.markdownToHTML(postData.content)); - - postData.relativeTime = utils.relativeTime(postData.timestamp); - postData.topicSlug = topicData.slug; - posts.push(postData); - callback(null); + async.waterfall([ + function(next) { + Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) { + if (postData.deleted === '1') return callback(null); + else { + postData.relativeTime = utils.relativeTime(postData.timestamp); + next(null, postData); + } }); - }); + }, + function(postData, next) { + Posts.addUserInfoToPost(postData, function() { + next(null, postData); + }); + }, + function(postData, next) { + topics.getTopicFields(postData.tid, ['slug', 'deleted'], function(err, topicData) { + if (err) return callback(err); + else if (topicData.deleted === '1') return callback(null); + + postData.topicSlug = topicData.slug; + next(null, postData); + }); + }, + function(postData, next) { + if (postData.content) { + postTools.toHTML(postData.content, function(err, content) { + if (!err) postData.content = utils.strip_tags(content); + next(err, postData); + }); + } else next(null, postData); + } + ], function(err, postData) { + if (!err) posts.push(postData); + callback(err); }); } @@ -144,7 +158,7 @@ var RDB = require('./redis.js'), Posts.getPostsByPids = function(pids, callback) { var posts = []; - function iterator(pid, callback) { + async.eachSeries(pids, function (pid, callback) { Posts.getPostData(pid, function(postData) { if(postData) { postData.relativeTime = utils.relativeTime(postData.timestamp); @@ -152,8 +166,6 @@ var RDB = require('./redis.js'), postData['edited-class'] = postData.editor !== '' ? '' : 'none'; postData['relativeEditTime'] = postData.edited !== '0' ? utils.relativeTime(postData.edited) : ''; - postData.content = postTools.markdownToHTML(postData.content); - if(postData.uploadedImages) { try { postData.uploadedImages = JSON.parse(postData.uploadedImages); @@ -164,13 +176,15 @@ var RDB = require('./redis.js'), } else { postData.uploadedImages = []; } - posts.push(postData); - } - callback(null); - }); - } - async.eachSeries(pids, iterator, function(err) { + postTools.toHTML(postData.content, function(err, content) { + postData.content = content; + posts.push(postData); + callback(null); + }); + } + }); + }, function(err) { if(!err) { callback(null, posts); } else { @@ -310,7 +324,7 @@ var RDB = require('./redis.js'), RDB.spop('cid:' + cid + ':active_users'); } - RDB.sadd('cid:' + cid + ':active_users', uid); + categories.addActiveUser(cid, uid); }); }); @@ -329,8 +343,9 @@ var RDB = require('./redis.js'), }, content: function(next) { plugins.fireHook('filter:post.get', postData, function(postData) { - postData.content = postTools.markdownToHTML(postData.content, false); - next(null, postData.content); + postTools.toHTML(postData.content, function(err, content) { + next(null, content); + }); }); } }, function(err, results) { diff --git a/src/routes/api.js b/src/routes/api.js index f0b166c1f0..64a421b699 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -22,6 +22,10 @@ var user = require('./../user.js'), config['minimumTitleLength'] = meta.config['minimumTitleLength']; config['minimumPostLength'] = meta.config['minimumPostLength']; config['imgurClientIDSet'] = !!meta.config['imgurClientID']; + config['minimumUsernameLength'] = meta.config['minimumUsernameLength']; + config['maximumUsernameLength'] = meta.config['maximumUsernameLength']; + config['minimumPasswordLength'] = meta.config['minimumPasswordLength']; + config['maximumPasswordLength'] = meta.config['maximumPasswordLength']; res.json(200, config); }); @@ -44,8 +48,7 @@ var user = require('./../user.js'), require('async').each(data.categories, iterator, function(err) { data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : 'none'; - data.motd = marked(meta.config.motd || "# NodeBB v " + pkg.version + "\nWelcome to NodeBB, the discussion platform of the future.\n\n Get NodeBB Fork us on Github @dcplabs"); - + data.motd = require('marked')(meta.config.motd || "# NodeBB v " + pkg.version + "\nWelcome to NodeBB, the discussion platform of the future.\n\n Get NodeBB Fork us on Github @dcplabs"); res.json(data); }); @@ -98,7 +101,10 @@ var user = require('./../user.js'), } data.token = res.locals.csrf_token; - + data.minimumUsernameLength = meta.config['minimumUsernameLength']; + data.maximumUsernameLength = meta.config['maximumUsernameLength']; + data.minimumPasswordLength = meta.config['minimumPasswordLength']; + data.maximumPasswordLength = meta.config['maximumPasswordLength']; res.json(data); }); diff --git a/src/routes/user.js b/src/routes/user.js index cd17791c31..f05aed6207 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -4,7 +4,6 @@ var user = require('./../user.js'), fs = require('fs'), utils = require('./../../public/src/utils.js'), path = require('path'), - marked = require('marked'), winston = require('winston'); (function(User) { @@ -353,12 +352,15 @@ var user = require('./../user.js'), userData.posts = posts.filter(function(p) {return p.deleted !== "1";}); userData.isFollowing = isFollowing; - userData.signature = postTools.markdownToHTML(userData.signature, true); if(!userData.profileviews) userData.profileviews = 1; if(callerUID !== userData.uid) user.incrementUserFieldBy(userData.uid, 'profileviews', 1); - res.json(userData); + + postTools.toHTML(userData.signature, function(err, signature) { + userData.signature = signature; + res.json(userData); + }); }); }); } else { diff --git a/src/sitemap.js b/src/sitemap.js index 620e205e88..15f9cb69f6 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -20,11 +20,13 @@ var path = require('path'), var categoryUrls = []; categories.getAllCategories(function(data) { data.categories.forEach(function(category) { - categoryUrls.push({ - url: path.join('category', category.slug), - changefreq: 'weekly', - priority: '0.4' - }); + if (!category.disabled) { + categoryUrls.push({ + url: path.join('category', category.slug), + changefreq: 'weekly', + priority: '0.4' + }); + } }); next(null, categoryUrls); @@ -34,11 +36,13 @@ var path = require('path'), var topicUrls = []; topics.getAllTopics(null, null, function(topics) { topics.forEach(function(topic) { - topicUrls.push({ - url: path.join('topic', topic.slug), - changefreq: 'daily', - priority: '0.6' - }); + if (topic.deleted !== '1') { + topicUrls.push({ + url: path.join('topic', topic.slug), + changefreq: 'daily', + priority: '0.6' + }); + } }); next(null, topicUrls); diff --git a/src/threadTools.js b/src/threadTools.js index a20323eb1f..e217f59b81 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -199,12 +199,19 @@ var RDB = require('./redis.js'), } }); + categories.moveActiveUsers(tid, oldCid, cid, function(err, data) { + if(err) { + winston.err(err); + } + }); + categories.incrementCategoryFieldBy(oldCid, 'topic_count', -1); categories.incrementCategoryFieldBy(cid, 'topic_count', 1); socket.emit('api:topic.move', { status: 'ok' }); + io.sockets.in('topic_' + tid).emit('event:topic_moved', { tid: tid }); @@ -264,7 +271,7 @@ var RDB = require('./redis.js'), topics.getTopicField(tid, 'title', function(err, title) { topics.getTeaser(tid, function(err, teaser) { if (!err) { - notifications.create(teaser.username + ' has posted a reply to: "' + title + '"', null, '/topic/' + tid, 'topic:' + tid, function(nid) { + notifications.create('' + teaser.username + ' has posted a reply to: "' + title + '"', null, '/topic/' + tid, 'topic:' + tid, function(nid) { next(null, nid); }); } else next(err); diff --git a/src/topics.js b/src/topics.js index 363b2c0609..aa227c3be8 100644 --- a/src/topics.js +++ b/src/topics.js @@ -5,7 +5,6 @@ var RDB = require('./redis.js') user = require('./user.js'), categories = require('./categories.js'), posts = require('./posts.js'), - marked = require('marked'), threadTools = require('./threadTools.js'), postTools = require('./postTools'), async = require('async'), @@ -14,9 +13,6 @@ var RDB = require('./redis.js') reds = require('reds'), topicSearch = reds.createSearch('nodebbtopicsearch'); -marked.setOptions({ - breaks: true -}); (function(Topics) { @@ -566,19 +562,23 @@ marked.setOptions({ return callback(err, null); var stripped = postData.content, - timestamp = postData.timestamp; + timestamp = postData.timestamp, + returnObj = { + "username": userData.username, + "picture": userData.picture, + "timestamp" : timestamp + }; - if(postData.content) { + if (postData.content) { stripped = postData.content.replace(/>.+\n\n/, ''); - stripped = utils.strip_tags(postTools.markdownToHTML(stripped)); + postTools.toHTML(stripped, function(err, stripped) { + returnObj.text = utils.strip_tags(stripped); + callback(null, returnObj); + }); + } else { + returnObj.text = ''; + callback(null, returnObj); } - - callback(null, { - "text": stripped, - "username": userData.username, - "picture": userData.picture, - "timestamp" : timestamp - }); }); }); } else callback(new Error('no-teaser-found')); @@ -723,6 +723,28 @@ marked.setOptions({ RDB.lrange('tid:' + tid + ':posts', 0, -1, callback); } + Topics.getUids = function(tid, callback) { + var uids = {}; + Topics.getPids(tid, function(err, pids) { + + function getUid(pid, next) { + posts.getPostField(pid, 'uid', function(uid) { + if(err) + return next(err); + uids[uid] = 1; + next(null); + }); + } + + async.each(pids, getUid, function(err) { + if(err) + return callback(err, null); + + callback(null, Object.keys(uids)); + }); + }); + } + Topics.delete = function(tid) { Topics.setTopicField(tid, 'deleted', 1); RDB.zrem('topics:recent', tid); diff --git a/src/user.js b/src/user.js index 8a5f37f0f3..5f84b492fb 100644 --- a/src/user.js +++ b/src/user.js @@ -3,9 +3,8 @@ var utils = require('./../public/src/utils.js'), crypto = require('crypto'), emailjs = require('emailjs'), meta = require('./meta.js'), - emailjsServer = emailjs.server.connect(meta.config.mailer), + emailjsServer = emailjs.server.connect(meta.config.mailer || '127.0.0.1'), bcrypt = require('bcrypt'), - marked = require('marked'), notifications = require('./notifications.js'), topics = require('./topics.js'), async = require('async'); @@ -581,7 +580,7 @@ var utils = require('./../public/src/utils.js'), User.getUserField(uid, 'username', function(err, username) { RDB.smembers('followers:' + uid, function(err, followers) { topics.getTopicField(tid, 'slug', function(err, slug) { - var message = username + ' made a new post'; + var message = '' + username + ' made a new post'; notifications.create(message, 5, nconf.get('url') + 'topic/' + slug + '#' + pid, 'notification_'+ Date.now(), function(nid) { notifications.push(nid, followers); diff --git a/src/webserver.js b/src/webserver.js index 8eb83f76aa..79f9ca741c 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -6,7 +6,6 @@ var express = require('express'), path = require('path'), redis = require('redis'), redisServer = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host')), - marked = require('marked'), utils = require('../public/src/utils.js'), pkg = require('../package.json'), fs = require('fs'), @@ -67,12 +66,12 @@ var express = require('express'), app.use(express.session({ store: new RedisStore({ client: redisServer, - ttl: 60*60*24*14 + ttl: 60*60*24*30 }), secret: nconf.get('secret'), key: 'express.sid', cookie: { - maxAge: 60*60*24*30 // 30 days + maxAge: 60*60*24*30*1000 // 30 days } })); app.use(express.csrf()); diff --git a/src/websockets.js b/src/websockets.js index 2bb328bf50..f75eeda2a0 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -32,7 +32,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }), (function(io) { var users = {}, userSockets = {}, - rooms = {} + rooms = {}, + chats = {}; global.io = io; @@ -55,7 +56,14 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }), io.sockets.in('global').emit('api:user.isOnline', isUserOnline(uid)); user.getUserField(uid, 'username', function(err, username) { - socket.emit('event:connect', {status: 1, username:username}); + socket.emit('event:connect', {status: 1, username:username, uid:uid}); + + if(chats[uid]) { + for(var i=0; i'; - notifications.create(finalMessage, 5, '#', 'notification_' + uid + '_' + touid, function(nid) { + if(!isUserOnline(touid)) { + notifications.create(notifText, 5, '#', 'notification_' + uid + '_' + touid, function(nid) { notifications.push(nid, [touid], function(success) { }); }); + } - require('./messaging').addMessage(uid, touid, msg, function(err, message) { + require('./messaging').addMessage(uid, touid, msg, function(err, message) { + var numSockets = 0; - }); + if(userSockets[touid]) { + numSockets = userSockets[touid].length; + + for(var x=0; x