From e2b535dfaf79416078be7e763192b89094208767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 25 Feb 2015 12:43:56 -0500 Subject: [PATCH 01/12] closes #1973 --- src/search.js | 9 ++--- src/topics.js | 1 + src/topics/suggested.js | 74 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/topics/suggested.js diff --git a/src/search.js b/src/search.js index 190a764bf2..8ccdf77d78 100644 --- a/src/search.js +++ b/src/search.js @@ -58,14 +58,14 @@ function searchInContent(query, data, callback) { async.parallel({ pids: function(next) { if (data.searchIn === 'posts' || data.searchIn === 'titlesposts') { - searchQuery('post', query, next); + search.searchQuery('post', query, next); } else { next(null, []); } }, tids: function(next) { if (data.searchIn === 'titles' || data.searchIn === 'titlesposts') { - searchQuery('topic', query, next); + search.searchQuery('topic', query, next); } else { next(null, []); } @@ -93,6 +93,7 @@ function searchInContent(query, data, callback) { mainPids.push(pid); } }); + privileges.posts.filter('read', mainPids, data.uid, next); }, function(pids, next) { @@ -475,10 +476,10 @@ function getMainPids(tids, callback) { }); } -function searchQuery(index, query, callback) { +search.searchQuery = function(index, query, callback) { plugins.fireHook('filter:search.query', { index: index, query: query }, callback); -} +}; diff --git a/src/topics.js b/src/topics.js index a9514dabba..274ffbcf70 100644 --- a/src/topics.js +++ b/src/topics.js @@ -25,6 +25,7 @@ var async = require('async'), require('./topics/follow')(Topics); require('./topics/tags')(Topics); require('./topics/teaser')(Topics); + require('./topics/suggested')(Topics); Topics.exists = function(tid, callback) { db.isSortedSetMember('topics:tid', tid, callback); diff --git a/src/topics/suggested.js b/src/topics/suggested.js new file mode 100644 index 0000000000..3c18d50dbe --- /dev/null +++ b/src/topics/suggested.js @@ -0,0 +1,74 @@ + +'use strict'; + +var async = require('async'), + _ = require('underscore'), + + categories = require('../categories'), + search = require('../search'), + db = require('../database'); + + +module.exports = function(Topics) { + + Topics.getSuggestedTopics = function(tid, uid, start, end, callback) { + async.parallel({ + tagTids: function(next) { + getTidsWithSameTags(tid, next); + }, + searchTids: function(next) { + getSearchTids(tid, uid, next); + }, + categoryTids: function(next) { + getCategoryTids(tid, next); + } + }, function(err, results) { + if (err) { + return callback(err); + } + var tids = results.tagTids.concat(results.searchTids).concat(results.categoryTids); + tids = tids.filter(function(_tid, index, array) { + return parseInt(_tid, 10) !== parseInt(tid, 10) && array.indexOf(_tid) === index; + }).slice(start, end + 1); + + Topics.getTopics(tids, uid, callback); + }); + }; + + function getTidsWithSameTags(tid, callback) { + async.waterfall([ + function(next) { + Topics.getTopicTags(tid, next); + }, + function(tags, next) { + async.map(tags, function(tag, next) { + Topics.getTagTids(tag, 0, -1, next); + }, next); + }, + function(data, next) { + next(null, _.unique(_.flatten(data))); + } + ], callback); + } + + function getSearchTids(tid, uid, callback) { + async.waterfall([ + function(next) { + Topics.getTopicField(tid, 'title', next); + }, + function(title, next) { + search.searchQuery('topic', title, next); + } + ], callback); + } + + function getCategoryTids(tid, callback) { + Topics.getTopicField(tid, 'cid', function(err, cid) { + if (err || !cid) { + return callback(err, []); + } + categories.getTopicIds('cid:' + cid + ':tids', true, 0, 9, callback); + }); + } + +}; \ No newline at end of file From 0c903672d0ae2a5a0f11abee91ecca9caa457b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 25 Feb 2015 12:46:00 -0500 Subject: [PATCH 02/12] uid not used #1973 --- src/topics/suggested.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/topics/suggested.js b/src/topics/suggested.js index 3c18d50dbe..fd3455f726 100644 --- a/src/topics/suggested.js +++ b/src/topics/suggested.js @@ -17,7 +17,7 @@ module.exports = function(Topics) { getTidsWithSameTags(tid, next); }, searchTids: function(next) { - getSearchTids(tid, uid, next); + getSearchTids(tid, next); }, categoryTids: function(next) { getCategoryTids(tid, next); @@ -51,7 +51,7 @@ module.exports = function(Topics) { ], callback); } - function getSearchTids(tid, uid, callback) { + function getSearchTids(tid, callback) { async.waterfall([ function(next) { Topics.getTopicField(tid, 'title', next); From 3202a52a6198688bb86d185ca48783e996889bd3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 25 Feb 2015 13:43:21 -0500 Subject: [PATCH 03/12] refactored notif dropdown, #2771 --- public/src/modules/notifications.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index d0f92d0230..c58287350e 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -58,10 +58,9 @@ define('notifications', ['sounds'], function(sound) { }); notifList.on('click', '.mark-read', function(e) { - var anchorEl = $(this.parentNode), - parentEl = anchorEl.parent(), - nid = anchorEl.attr('data-nid'), - unread = parentEl.hasClass('unread'); + var liEl = $(this.parentNode), + nid = liEl.siblings('a').attr('data-nid'), + unread = liEl.hasClass('unread'); e.preventDefault(); e.stopPropagation(); @@ -71,7 +70,7 @@ define('notifications', ['sounds'], function(sound) { app.alertError(err.message); } - parentEl.toggleClass('unread'); + liEl.toggleClass('unread'); increaseNotifCount(unread ? -1 : 1); }); }); From 55262b399a441e2e39ad6e4f3e566d70b1e186df Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 25 Feb 2015 14:10:00 -0500 Subject: [PATCH 04/12] tweaks and refactoring for #2774 --- public/language/en_GB/error.json | 1 + public/language/en_GB/notifications.json | 1 - src/controllers/index.js | 7 ++++--- src/user/email.js | 17 +++++++---------- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index b2f9a3b86b..6d64645c59 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -25,6 +25,7 @@ "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", + "email-confirm-failed": "We could not confirm your email, please try again later.", "username-too-short": "Username too short", "username-too-long": "Username too long", diff --git a/public/language/en_GB/notifications.json b/public/language/en_GB/notifications.json index 96aef60183..19de8e2c28 100644 --- a/public/language/en_GB/notifications.json +++ b/public/language/en_GB/notifications.json @@ -25,7 +25,6 @@ "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", - "email-confirm-error": "An error occurred...", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", "email-confirm-sent": "Confirmation email sent." } diff --git a/src/controllers/index.js b/src/controllers/index.js index d8ddf81735..7f4afeb4dc 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -119,9 +119,10 @@ Controllers.register = function(req, res, next) { Controllers.confirmEmail = function(req, res, next) { - user.email.confirm(req.params.code, function (data) { - data.status = data.status === 'ok'; - res.render('confirm', data); + user.email.confirm(req.params.code, function (err) { + res.render('confirm', { + error: err ? err.message : '' + }); }); }; diff --git a/src/user/email.js b/src/user/email.js index 74b86c0cfe..e7f56ed21a 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -87,21 +87,18 @@ var async = require('async'), UserEmail.confirm = function(code, callback) { db.getObject('confirm:' + code, function(err, confirmObj) { if (err) { - return callback({ - status:'error' - }); + return callback(new Error('[[error:parse-error]]')); } if (confirmObj && confirmObj.uid && confirmObj.email) { - user.setUserField(confirmObj.uid, 'email:confirmed', 1, function() { - callback({ - status: 'ok' - }); + async.series([ + async.apply(user.setUserField, confirmObj.uid, 'email:confirmed', 1), + async.apply(db.delete, 'confirm:' + code) + ], function(err) { + callback(err ? new Error('[[error:email-confirm-failed]]') : null); }); } else { - callback({ - status: 'not_ok' - }); + callback(new Error('[[error:invalid-data]]')); } }); }; From 094cd6df32305d02d3aa37187928b994cdc6dc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 25 Feb 2015 14:17:30 -0500 Subject: [PATCH 05/12] closes #2777 --- public/src/client/unread.js | 9 ++------- src/categories.js | 11 ++--------- src/privileges/categories.js | 7 +++++++ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 96c54e7536..994d4e1ddb 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -123,25 +123,20 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], } function createCategoryLinks(categories) { - categories = categories.filter(function(category) { - return !category.disabled; - }); - - for(var i=0; i'); + if (category.icon) { link.append(' ' + category.name); } else { link.append(category.name); } - $('') .append(link) .appendTo($('.markread .dropdown-menu')); diff --git a/src/categories.js b/src/categories.js index a4b6ce2092..87c540aaf1 100644 --- a/src/categories.js +++ b/src/categories.js @@ -121,13 +121,7 @@ var async = require('async'), }, function(cids, next) { Categories.getCategories(cids, uid, next); - }, - function(categories, next) { - categories = categories.filter(function(category) { - return !category.disabled; - }); - next(null, categories); - } + } ], callback); }; @@ -286,10 +280,9 @@ var async = require('async'), Categories.getCategoriesData(cids, next); }, function (categories, next) { - // Filter categories to isolate children, and remove disabled categories async.map(cids, function(cid, next) { next(null, categories.filter(function(category) { - return category && parseInt(category.parentCid, 10) === parseInt(cid, 10) && !category.disabled; + return category && parseInt(category.parentCid, 10) === parseInt(cid, 10); })); }, next); } diff --git a/src/privileges/categories.js b/src/privileges/categories.js index 267c9e2c81..4a623eaa27 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -84,6 +84,9 @@ module.exports = function(privileges) { }); async.parallel({ + categories: function(next) { + categories.getMultipleCategoryFields(cids, ['disabled'], next); + }, allowedTo: function(next) { helpers.isUserAllowedTo(privilege, uid, cids, next); }, @@ -98,6 +101,10 @@ module.exports = function(privileges) { return callback(err); } + cids = cids.filter(function(cid, index) { + return !results.categories[index].disabled; + }); + if (results.isAdmin) { return callback(null, cids); } From 77d154bb8b9549523e35c323dcb62f811c27dfb6 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 22 Feb 2015 01:29:40 -0500 Subject: [PATCH 06/12] added ajaxify popstate cache, so back/forward will just put back the already loaded page --- public/src/ajaxify.js | 15 +++++++++++-- public/src/modules/ajaxifyCache.js | 35 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 public/src/modules/ajaxifyCache.js diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index cd87159420..8ba7fef1a6 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -1,9 +1,11 @@ "use strict"; -var ajaxify = ajaxify || {}; +var ajaxify = ajaxify || { + isPopState: false +}; $(document).ready(function() { - require(['templates'], function (templatesModule) { + require(['templates', 'ajaxifyCache'], function (templatesModule, cache) { /*global app, templates, utils, socket, translator, config, RELATIVE_PATH*/ var location = document.location || window.location, @@ -13,7 +15,9 @@ $(document).ready(function() { window.onpopstate = function (event) { if (event !== null && event.state && event.state.url !== undefined && !ajaxify.initialLoad) { + ajaxify.isPopState = true; ajaxify.go(event.state.url, function() { + ajaxify.isPopState = false; $(window).trigger('action:popstate', {url: event.state.url}); }, true); } @@ -55,6 +59,12 @@ $(document).ready(function() { // "quiet": If set to true, will not call pushState app.enterRoom(''); + // If the url is in the cache, load from cache instead + if (cache.get(url)) { return true; } + else { + cache.url = ajaxify.currentPage; + } + $(window).off('scroll'); if ($('#content').hasClass('ajaxifying') && apiXHR) { @@ -114,6 +124,7 @@ $(document).ready(function() { templates.parse(tpl_url, data, function(template) { translator.translate(template, function(translatedTemplate) { setTimeout(function() { + cache.set(); $('#content').html(translatedTemplate); ajaxify.variables.parse(); diff --git a/public/src/modules/ajaxifyCache.js b/public/src/modules/ajaxifyCache.js new file mode 100644 index 0000000000..eaa682b0da --- /dev/null +++ b/public/src/modules/ajaxifyCache.js @@ -0,0 +1,35 @@ +'use strict'; +/* globals define, app, ajaxify */ + +define('ajaxifyCache', function() { + var Cache = { + url: undefined, + DOM: undefined, + tempDOM: undefined + }; + + Cache.set = function() { + Cache.DOM = $('#content > *').detach(); + }; + + Cache.get = function(url) { + if (url === Cache.url && ajaxify.isPopState) { + // Swap DOM elements + setTimeout(function() { + Cache.tempDOM = $('#content > *').detach(); + $('#content').append(Cache.DOM); + Cache.DOM = Cache.tempDOM; + }, 100); // 100ms for realism! :sunglasses: + + // Set the values that normally get set on ajaxify + Cache.url = ajaxify.currentPage; + ajaxify.currentPage = url; + + return true; + } else { + return false; + } + }; + + return Cache; +}); \ No newline at end of file From d8c9ec0d406147b4ac9631fcc7687073ee71ea8e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 22 Feb 2015 21:10:06 -0500 Subject: [PATCH 07/12] on click, topics are marked read from the unread page. Also fixed an issue where isPopState kept getting set to true, causing issues --- public/src/ajaxify.js | 1 + public/src/client/unread.js | 27 ++++++++++++++++++++------- public/src/modules/ajaxifyCache.js | 4 ++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 8ba7fef1a6..7f99f3d2be 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -63,6 +63,7 @@ $(document).ready(function() { if (cache.get(url)) { return true; } else { cache.url = ajaxify.currentPage; + ajaxify.isPopState = false; } $(window).off('scroll'); diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 994d4e1ddb..8a6df4f9cd 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -12,6 +12,8 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], }); Unread.init = function() { + var topicsContainer = $('#topics-container'); + app.enterRoom('recent_posts'); $('#new-topics-alert').on('click', function() { @@ -42,7 +44,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], app.alertSuccess('[[unread:topics_marked_as_read.success]]'); - $('#topics-container').empty(); + topicsContainer.empty(); $('#category-no-topics').removeClass('hidden'); $('.markread').addClass('hidden'); }); @@ -68,11 +70,22 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], }); }); + topicsContainer.on('click', '.topic-title, .replies a', function(e) { + var tid = $(e.target).parents('[data-tid]').attr('data-tid'); + socket.emit('topics.markAsRead', [tid], function(err) { + if(err) { + return app.alertError(err.message); + } + + doneRemovingTids([tid]); + }); + }); + socket.emit('categories.get', onCategoriesLoaded); topicSelect.init(); - if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20) { + if ($("body").height() <= $(window).height() && topicsContainer.children().length >= 20) { $('#load-more-btn').show(); } @@ -83,16 +96,16 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], infinitescroll.init(loadMoreTopics); function loadMoreTopics(direction) { - if(direction < 0 || !$('#topics-container').length) { + if(direction < 0 || !topicsContainer.length) { return; } infinitescroll.loadMore('topics.loadMoreUnreadTopics', { - after: $('#topics-container').attr('data-nextstart') + after: topicsContainer.attr('data-nextstart') }, function(data, done) { if (data.topics && data.topics.length) { recent.onTopicsLoaded('unread', data.topics, true, done); - $('#topics-container').attr('data-nextstart', data.nextStart); + topicsContainer.attr('data-nextstart', data.nextStart); } else { done(); $('#load-more-btn').hide(); @@ -101,10 +114,10 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], } }; - function doneRemovingTids(tids) { + function doneRemovingTids(tids, quiet) { removeTids(tids); - app.alertSuccess('[[unread:topics_marked_as_read.success]]'); + if (!quiet) { app.alertSuccess('[[unread:topics_marked_as_read.success]]'); } if (!$('#topics-container').children().length) { $('#category-no-topics').removeClass('hidden'); diff --git a/public/src/modules/ajaxifyCache.js b/public/src/modules/ajaxifyCache.js index eaa682b0da..1e5c63fa5c 100644 --- a/public/src/modules/ajaxifyCache.js +++ b/public/src/modules/ajaxifyCache.js @@ -15,11 +15,11 @@ define('ajaxifyCache', function() { Cache.get = function(url) { if (url === Cache.url && ajaxify.isPopState) { // Swap DOM elements - setTimeout(function() { + // setTimeout(function() { Cache.tempDOM = $('#content > *').detach(); $('#content').append(Cache.DOM); Cache.DOM = Cache.tempDOM; - }, 100); // 100ms for realism! :sunglasses: + // }, 100); // 100ms for realism! :sunglasses: // Set the values that normally get set on ajaxify Cache.url = ajaxify.currentPage; From e6701c5a1f5a87a526c860f8e25e68ca1a65492d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Feb 2015 11:50:41 -0500 Subject: [PATCH 08/12] actually fixed isPopState error --- public/src/ajaxify.js | 7 ++----- public/src/modules/ajaxifyCache.js | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 7f99f3d2be..adaef4cb8f 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -60,11 +60,8 @@ $(document).ready(function() { app.enterRoom(''); // If the url is in the cache, load from cache instead - if (cache.get(url)) { return true; } - else { - cache.url = ajaxify.currentPage; - ajaxify.isPopState = false; - } + if (cache.get(url, callback)) { return true; } + else { cache.url = ajaxify.currentPage; } $(window).off('scroll'); diff --git a/public/src/modules/ajaxifyCache.js b/public/src/modules/ajaxifyCache.js index 1e5c63fa5c..02313c3d68 100644 --- a/public/src/modules/ajaxifyCache.js +++ b/public/src/modules/ajaxifyCache.js @@ -12,7 +12,7 @@ define('ajaxifyCache', function() { Cache.DOM = $('#content > *').detach(); }; - Cache.get = function(url) { + Cache.get = function(url, callback) { if (url === Cache.url && ajaxify.isPopState) { // Swap DOM elements // setTimeout(function() { @@ -25,6 +25,8 @@ define('ajaxifyCache', function() { Cache.url = ajaxify.currentPage; ajaxify.currentPage = url; + if (typeof callback === 'function') { callback(); } + return true; } else { return false; From 73505e9bd047963039574f462edac6a96da10284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 25 Feb 2015 15:37:33 -0500 Subject: [PATCH 09/12] closes #2247 --- public/language/en_GB/error.json | 1 + src/privileges/posts.js | 68 ++++++++++++++++++++++--------- src/views/admin/settings/post.tpl | 4 ++ 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 6d64645c59..8bb83071fc 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -45,6 +45,7 @@ "category-disabled": "Category disabled", "topic-locked": "Topic Locked", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", "still-uploading": "Please wait for uploads to complete.", "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", diff --git a/src/privileges/posts.js b/src/privileges/posts.js index 7c304a1a2c..5e012dc9e9 100644 --- a/src/privileges/posts.js +++ b/src/privileges/posts.js @@ -4,6 +4,7 @@ var async = require('async'), winston = require('winston'), + meta = require('../meta'), posts = require('../posts'), topics = require('../topics'), user = require('../user'), @@ -108,27 +109,24 @@ module.exports = function(privileges) { }; privileges.posts.canEdit = function(pid, uid, callback) { - helpers.some([ - function(next) { - isPostTopicLocked(pid, function(err, isLocked) { - if (err || isLocked) { - return next(err, false); - } - - helpers.some([ - function(next) { - posts.isOwner(pid, uid, next); - }, - function(next) { - helpers.hasEnoughReputationFor(['privileges:manage_content', 'privileges:manage_topic'], uid, next); - } - ], next); - }); - }, - function(next) { - isAdminOrMod(pid, uid, next); + async.parallel({ + isEditable: async.apply(isPostEditable, pid, uid), + isAdminOrMod: async.apply(isAdminOrMod, pid, uid) + }, function(err, results) { + if (err) { + return callback(err); } - ], callback); + if (results.isAdminOrMod) { + return callback(null, true); + } + if (results.isEditable.isLocked) { + return callback(new Error('[[error:topic-locked]]]')); + } + if (results.isEditable.isEditExpired) { + return callback(new Error('[[error:post-edit-duration-expired, ' + meta.config.postEditDuration + ']]')); + } + callback(null, results.isEditable.editable); + }); }; privileges.posts.canMove = function(pid, uid, callback) { @@ -140,6 +138,36 @@ module.exports = function(privileges) { }); }; + function isPostEditable(pid, uid, callback) { + async.waterfall([ + function(next) { + posts.getPostFields(pid, ['tid', 'timestamp'], next); + }, + function(postData, next) { + var postEditDuration = parseInt(meta.config.postEditDuration, 10); + if (postEditDuration && Date.now() - parseInt(postData.timestamp, 10) > postEditDuration * 1000) { + return callback(null, {isEditExpired: true}); + } + topics.isLocked(postData.tid, next); + }, + function(isLocked, next) { + if (isLocked) { + return callback(null, {isLocked: true}); + } + helpers.some([ + function(next) { + posts.isOwner(pid, uid, next); + }, + function(next) { + helpers.hasEnoughReputationFor(['privileges:manage_content', 'privileges:manage_topic'], uid, next); + } + ], function(err, editable) { + next(err, {editable: editable}); + }); + } + ], callback); + } + function isPostTopicLocked(pid, callback) { posts.getPostField(pid, 'tid', function(err, tid) { if (err) { diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index 98b99f3973..2ec28c23a1 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -37,6 +37,10 @@ +
+ + +
From ec075ca60898441529562636f7318f63cf572501 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 25 Feb 2015 15:50:13 -0500 Subject: [PATCH 10/12] suppressing alertSuccess when clicking into topics from unread page --- public/src/client/unread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 8a6df4f9cd..53cb048463 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -77,7 +77,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], return app.alertError(err.message); } - doneRemovingTids([tid]); + doneRemovingTids([tid], true); }); }); From 37d426dddb3311119b038c073f80c5046d26f881 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 25 Feb 2015 15:56:52 -0500 Subject: [PATCH 11/12] npnp, I totally meant to remove a method that's still in use... --- src/user/notifications.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/user/notifications.js b/src/user/notifications.js index 3a11b06a0a..c3ea4b9c23 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -39,6 +39,20 @@ var async = require('async'), }); }; + UserNotifications.getAll = function(uid, count, callback) { + getNotifications(uid, count, function(err, notifs) { + if (err) { + return callback(err); + } + notifs = notifs.unread.concat(notifs.read); + notifs = notifs.filter(Boolean).sort(function(a, b) { + return b.datetime - a.datetime; + }); + + callback(null, notifs); + }); + }; + function getNotifications(uid, count, callback) { async.parallel({ unread: function(next) { From f00589c8bae0fa46ac117f1998840213781df4b1 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 25 Feb 2015 16:07:03 -0500 Subject: [PATCH 12/12] removing 'see all chats' from chats list, as it is in menu.tpl now --- public/src/modules/chat.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 06e8c75c7d..abc1bb58be 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -59,11 +59,6 @@ define('chat', ['taskbar', 'string', 'sounds', 'forum/chats'], function(taskbar, }); })(userObj); } - - var seeAll = ''; - translator.translate(seeAll, function(translated) { - $(translated).appendTo(chatsListEl); - }); }); });