mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
Merge remote-tracking branch 'origin/master' into user-icons
Conflicts: public/src/client/account/edit.js src/middleware/middleware.js src/socket.io/meta.js src/socket.io/user/picture.js src/user.js src/views/admin/manage/group.tpl
This commit is contained in:
18
package.json
18
package.json
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~1.4.2",
|
||||
"bcryptjs": "~2.2.1",
|
||||
"bcryptjs": "~2.3.0",
|
||||
"body-parser": "^1.9.0",
|
||||
"colors": "^1.1.0",
|
||||
"compression": "^1.1.0",
|
||||
@@ -39,18 +39,18 @@
|
||||
"mkdirp": "~0.5.0",
|
||||
"mmmagic": "^0.4.0",
|
||||
"morgan": "^1.3.2",
|
||||
"nconf": "~0.7.1",
|
||||
"nodebb-plugin-composer-default": "1.0.16",
|
||||
"nconf": "~0.8.2",
|
||||
"nodebb-plugin-composer-default": "1.0.19",
|
||||
"nodebb-plugin-dbsearch": "0.2.17",
|
||||
"nodebb-plugin-emoji-extended": "0.4.14",
|
||||
"nodebb-plugin-emoji-extended": "0.4.15",
|
||||
"nodebb-plugin-markdown": "4.0.6",
|
||||
"nodebb-plugin-mentions": "1.0.6",
|
||||
"nodebb-plugin-mentions": "1.0.8",
|
||||
"nodebb-plugin-soundpack-default": "0.1.4",
|
||||
"nodebb-plugin-spam-be-gone": "0.4.2",
|
||||
"nodebb-rewards-essentials": "0.0.5",
|
||||
"nodebb-theme-lavender": "2.0.6",
|
||||
"nodebb-theme-persona": "3.0.45",
|
||||
"nodebb-theme-vanilla": "4.0.20",
|
||||
"nodebb-theme-lavender": "2.0.8",
|
||||
"nodebb-theme-persona": "3.0.56",
|
||||
"nodebb-theme-vanilla": "4.0.24",
|
||||
"nodebb-widget-essentials": "2.0.2",
|
||||
"npm": "^2.1.4",
|
||||
"passport": "^0.3.0",
|
||||
@@ -73,7 +73,7 @@
|
||||
"underscore.deep": "^0.5.1",
|
||||
"validator": "^4.0.5",
|
||||
"winston": "^1.0.1",
|
||||
"xregexp": "~2.0.0"
|
||||
"xregexp": "~3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.13.0",
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
"group-name-too-short": "Group name too short",
|
||||
"group-already-exists": "Group already exists",
|
||||
"group-name-change-not-allowed": "Group name change not allowed",
|
||||
"group-already-member": "You are already part of this group",
|
||||
"group-already-member": "Already part of this group",
|
||||
"group-not-member": "Not a member of this group",
|
||||
"group-needs-owner": "This group requires at least one owner",
|
||||
"group-already-invited": "This user has already been invited",
|
||||
"group-already-requested": "Your membership request has already been submitted",
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"composer.discard": "Are you sure you wish to discard this post?",
|
||||
"composer.submit_and_lock": "Submit and Lock",
|
||||
"composer.toggle_dropdown": "Toggle Dropdown",
|
||||
"composer.uploading": "Uploading %1",
|
||||
|
||||
"bootbox.ok": "OK",
|
||||
"bootbox.cancel": "Cancel",
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
"new_message_from": "New message from <strong>%1</strong>",
|
||||
"upvoted_your_post_in": "<strong>%1</strong> has upvoted your post in <strong>%2</strong>.",
|
||||
"moved_your_post": "<strong>%1</strong> has moved your post.",
|
||||
"moved_your_topic": "<strong>%1</strong> has moved your topic.",
|
||||
"moved_your_post": "<strong>%1</strong> has moved your post to <strong>%2</strong>",
|
||||
"moved_your_topic": "<strong>%1</strong> has moved <strong>%2</strong>",
|
||||
"favourited_your_post_in": "<strong>%1</strong> has favourited your post in <strong>%2</strong>.",
|
||||
"user_flagged_post_in": "<strong>%1</strong> flagged a post in <strong>%2</strong>",
|
||||
"user_posted_to" : "<strong>%1</strong> has posted a reply to: <strong>%2</strong>",
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
"chat": "Chatting with %1",
|
||||
|
||||
"account/edit": "Editing \"%1\"",
|
||||
"account/edit/password": "Editing password of \"%1\"",
|
||||
"account/edit/username": "Editing username of \"%1\"",
|
||||
"account/edit/email": "Editing email of \"%1\"",
|
||||
"account/following": "People %1 follows",
|
||||
"account/followers": "People who follow %1",
|
||||
"account/posts": "Posts made by %1",
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
"bookmark_instructions" : "Click here to return to the last unread post in this thread.",
|
||||
|
||||
"flag_title": "Flag this post for moderation",
|
||||
"flag_confirm": "Are you sure you want to flag this post?",
|
||||
"flag_success": "This post has been flagged for moderation.",
|
||||
"deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.",
|
||||
|
||||
@@ -117,5 +116,10 @@
|
||||
"most_votes": "Most votes",
|
||||
"most_posts": "Most posts",
|
||||
|
||||
"stale_topic_warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?"
|
||||
"stale_topic_warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
|
||||
|
||||
"spam": "Spam",
|
||||
"offensive": "Offensive",
|
||||
"custom-flag-reason": "Enter a flagging reason"
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
"profile_update_success": "Profile has been updated successfully!",
|
||||
"change_picture": "Change Picture",
|
||||
"change_username": "Change Username",
|
||||
"change_email": "Change Email",
|
||||
"edit": "Edit",
|
||||
"default_picture": "Default Icon",
|
||||
"uploaded_picture": "Uploaded Picture",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#navigation {
|
||||
|
||||
|
||||
#active-navigation {
|
||||
.active {
|
||||
background-color: #eee;
|
||||
@@ -23,6 +24,9 @@
|
||||
.iconPicker i {
|
||||
cursor: pointer;
|
||||
}
|
||||
.form-group {
|
||||
min-height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
.group {
|
||||
.current_members {
|
||||
[component="groups/members"] {
|
||||
padding: 0;
|
||||
tbody {
|
||||
max-height: 500px;
|
||||
display: block;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 100px;
|
||||
.member-name {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
.border-radius(3px);
|
||||
|
||||
&.disabled {
|
||||
-webkit-filter: grayscale(30%);
|
||||
background-color: #888!important;
|
||||
.opacity(0.5);
|
||||
}
|
||||
}
|
||||
@@ -54,4 +54,4 @@
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
function selectMenuItem(url) {
|
||||
url = url
|
||||
.replace(/\/\d+$/, '')
|
||||
.split('/').slice(0, 3).join('/');
|
||||
.split('/').slice(0, 3).join('/')
|
||||
.split('?')[0];
|
||||
|
||||
// If index is requested, load the dashboard
|
||||
if (url === 'admin') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
/*global define, socket, app, bootbox, templates, ajaxify, RELATIVE_PATH, Sortable */
|
||||
/*global define, socket, app, bootbox, templates, ajaxify, Sortable */
|
||||
|
||||
define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-serializeobject.min'], function() {
|
||||
var Categories = {}, newCategoryId = -1, sortables;
|
||||
@@ -17,12 +17,17 @@ define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-seri
|
||||
|
||||
// Enable/Disable toggle events
|
||||
$('.categories').on('click', 'button[data-action="toggle"]', function() {
|
||||
var self = $(this),
|
||||
rowEl = self.parents('li'),
|
||||
cid = rowEl.attr('data-cid'),
|
||||
disabled = rowEl.hasClass('disabled');
|
||||
var $this = $(this),
|
||||
cid = $this.attr('data-cid'),
|
||||
parentEl = $this.parents('li[data-cid="' + cid + '"]'),
|
||||
disabled = parentEl.hasClass('disabled');
|
||||
|
||||
Categories.toggle(cid, disabled);
|
||||
var children = parentEl.find('li[data-cid]').map(function() {
|
||||
return $(this).attr('data-cid');
|
||||
}).get();
|
||||
|
||||
Categories.toggle([cid].concat(children), !disabled);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -94,14 +99,16 @@ define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-seri
|
||||
}
|
||||
};
|
||||
|
||||
Categories.toggle = function(cid, disabled) {
|
||||
Categories.toggle = function(cids, disabled) {
|
||||
var payload = {};
|
||||
|
||||
payload[cid] = {
|
||||
disabled: disabled ? 1 : 0
|
||||
};
|
||||
cids.forEach(function(cid) {
|
||||
payload[cid] = {
|
||||
disabled: disabled ? 1 : 0
|
||||
};
|
||||
});
|
||||
|
||||
socket.emit('admin.categories.update', payload, function(err, result) {
|
||||
socket.emit('admin.categories.update', payload, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ define('admin/manage/category', [
|
||||
}
|
||||
|
||||
categories = categories.filter(function(category) {
|
||||
return category && parseInt(category.cid, 10) !== parseInt(ajaxify.data.category.cid, 10);
|
||||
return category && !category.disabled && parseInt(category.cid, 10) !== parseInt(ajaxify.data.category.cid, 10);
|
||||
});
|
||||
|
||||
templates.parse('partials/category_list', {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"use strict";
|
||||
/*global define, templates, socket, ajaxify, app, admin, bootbox, utils, config */
|
||||
/*global define, templates, socket, ajaxify, app, bootbox, translator */
|
||||
|
||||
define('admin/manage/group', [
|
||||
'forum/groups/memberlist',
|
||||
'iconSelect',
|
||||
'admin/modules/colorpicker'
|
||||
], function(iconSelect, colorpicker) {
|
||||
], function(memberList, iconSelect, colorpicker) {
|
||||
var Groups = {};
|
||||
|
||||
Groups.init = function() {
|
||||
var groupDetailsSearch = $('#group-details-search'),
|
||||
groupDetailsSearchResults = $('#group-details-search-results'),
|
||||
groupMembersEl = $('ul.current_members'),
|
||||
groupIcon = $('#group-icon'),
|
||||
changeGroupUserTitle = $('#change-group-user-title'),
|
||||
changeGroupLabelColor = $('#change-group-label-color'),
|
||||
@@ -20,6 +20,8 @@ define('admin/manage/group', [
|
||||
|
||||
var groupName = ajaxify.data.group.name;
|
||||
|
||||
memberList.init();
|
||||
|
||||
changeGroupUserTitle.keyup(function() {
|
||||
groupLabelPreview.text(changeGroupUserTitle.val());
|
||||
});
|
||||
@@ -46,10 +48,16 @@ define('admin/manage/group', [
|
||||
}
|
||||
|
||||
groupDetailsSearchResults.empty();
|
||||
|
||||
for (x = 0; x < numResults; x++) {
|
||||
foundUser = $('<li />');
|
||||
foundUser
|
||||
.attr({title: results.users[x].username, 'data-uid': results.users[x].uid})
|
||||
.attr({title: results.users[x].username,
|
||||
'data-uid': results.users[x].uid,
|
||||
'data-username': results.users[x].username,
|
||||
'data-userslug': results.users[x].userslug,
|
||||
'data-picture': results.users[x].picture
|
||||
})
|
||||
.append($('<img />').attr('src', results.users[x].picture))
|
||||
.append($('<span />').html(results.users[x].username));
|
||||
|
||||
@@ -64,45 +72,74 @@ define('admin/manage/group', [
|
||||
|
||||
groupDetailsSearchResults.on('click', 'li[data-uid]', function() {
|
||||
var userLabel = $(this),
|
||||
uid = parseInt(userLabel.attr('data-uid'), 10),
|
||||
members = [];
|
||||
uid = parseInt(userLabel.attr('data-uid'), 10);
|
||||
|
||||
groupMembersEl.find('li[data-uid]').each(function() {
|
||||
members.push(parseInt($(this).attr('data-uid'), 10));
|
||||
});
|
||||
|
||||
if (members.indexOf(uid) === -1) {
|
||||
socket.emit('admin.groups.join', {
|
||||
groupName: groupName,
|
||||
uid: uid
|
||||
}, function(err, data) {
|
||||
if (!err) {
|
||||
groupMembersEl.append(userLabel.clone(true));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
groupMembersEl.on('click', 'li[data-uid]', function() {
|
||||
var uid = $(this).attr('data-uid');
|
||||
|
||||
bootbox.confirm('Are you sure you want to remove this user?', function(confirm) {
|
||||
if (!confirm) {
|
||||
return;
|
||||
socket.emit('admin.groups.join', {
|
||||
groupName: groupName,
|
||||
uid: uid
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
socket.emit('admin.groups.leave', {
|
||||
groupName: groupName,
|
||||
uid: uid
|
||||
}, function(err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
groupMembersEl.find('li[data-uid="' + uid + '"]').remove();
|
||||
var member = {
|
||||
uid: userLabel.attr('data-uid'),
|
||||
username: userLabel.attr('data-username'),
|
||||
userslug: userLabel.attr('data-userslug'),
|
||||
picture: userLabel.attr('data-picture')
|
||||
};
|
||||
|
||||
templates.parse('partials/groups/memberlist', 'members', {group: {isOwner: ajaxify.data.group.isOwner, members: [member]}}, function(html) {
|
||||
translator.translate(html, function(html) {
|
||||
$('[component="groups/members"] tr').first().before(html);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('[component="groups/members"]').on('click', '[data-action]', function() {
|
||||
var btnEl = $(this),
|
||||
userRow = btnEl.parents('[data-uid]'),
|
||||
ownerFlagEl = userRow.find('.member-name i'),
|
||||
isOwner = !ownerFlagEl.hasClass('invisible') ? true : false,
|
||||
uid = userRow.attr('data-uid'),
|
||||
action = btnEl.attr('data-action');
|
||||
|
||||
switch(action) {
|
||||
case 'toggleOwnership':
|
||||
socket.emit('groups.' + (isOwner ? 'rescind' : 'grant'), {
|
||||
toUid: uid,
|
||||
groupName: groupName
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
ownerFlagEl.toggleClass('invisible');
|
||||
});
|
||||
break;
|
||||
|
||||
case 'kick':
|
||||
bootbox.confirm('Are you sure you want to remove this user?', function(confirm) {
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
socket.emit('admin.groups.leave', {
|
||||
uid: uid,
|
||||
groupName: groupName
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
userRow.slideUp().remove();
|
||||
});
|
||||
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
$('#group-icon').on('click', function() {
|
||||
iconSelect.init(groupIcon);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
/*global define, socket, app, admin, utils, bootbox, RELATIVE_PATH*/
|
||||
/*global define, socket, app, utils, bootbox*/
|
||||
|
||||
define('admin/manage/tags', [
|
||||
'forum/infinitescroll',
|
||||
@@ -25,12 +25,12 @@ define('admin/manage/tags', [
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(function() {
|
||||
socket.emit('topics.searchAndLoadTags', {query: $('#tag-search').val()}, function(err, tags) {
|
||||
socket.emit('topics.searchAndLoadTags', {query: $('#tag-search').val()}, function(err, result) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
infinitescroll.parseAndTranslate('admin/manage/tags', 'tags', {tags: tags}, function(html) {
|
||||
infinitescroll.parseAndTranslate('admin/manage/tags', 'tags', {tags: result.tags}, function(html) {
|
||||
$('.tag-list').html(html);
|
||||
utils.makeNumbersHumanReadable(html.find('.human-readable-number'));
|
||||
timeoutId = 0;
|
||||
@@ -43,7 +43,7 @@ define('admin/manage/tags', [
|
||||
}
|
||||
|
||||
function handleModify() {
|
||||
$('#modify').on('click', function(ev) {
|
||||
$('#modify').on('click', function() {
|
||||
var tagsToModify = $('.tag-row.selected');
|
||||
if (!tagsToModify.length) {
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,7 @@ $(document).ready(function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
app.enterRoom('');
|
||||
app.leaveCurrentRoom();
|
||||
|
||||
$(window).off('scroll');
|
||||
|
||||
@@ -56,6 +56,7 @@ $(document).ready(function() {
|
||||
|
||||
url = ajaxify.start(url, quiet);
|
||||
|
||||
$('body').removeClass(ajaxify.data.bodyClass);
|
||||
$('#footer, #content').removeClass('hide').addClass('ajaxifying');
|
||||
|
||||
ajaxify.loadData(url, function(err, data) {
|
||||
@@ -141,6 +142,7 @@ $(document).ready(function() {
|
||||
|
||||
templates.parse(tpl_url, data, function(template) {
|
||||
translator.translate(template, function(translatedTemplate) {
|
||||
$('body').addClass(data.bodyClass);
|
||||
$('#content').html(translatedTemplate);
|
||||
|
||||
ajaxify.end(url, tpl_url);
|
||||
@@ -222,9 +224,7 @@ $(document).ready(function() {
|
||||
|
||||
$(window).trigger('action:ajaxify.dataLoaded', {url: url, data: data});
|
||||
|
||||
if (callback) {
|
||||
callback(null, data);
|
||||
}
|
||||
callback(null, data);
|
||||
},
|
||||
error: function(data, textStatus) {
|
||||
if (data.status === 0 && textStatus === 'error') {
|
||||
|
||||
@@ -71,12 +71,14 @@ app.cacheBuster = null;
|
||||
}
|
||||
});
|
||||
|
||||
require(['taskbar', 'helpers'], function(taskbar, helpers) {
|
||||
require(['taskbar', 'helpers', 'forum/pagination'], function(taskbar, helpers, pagination) {
|
||||
taskbar.init();
|
||||
|
||||
// templates.js helpers
|
||||
helpers.register();
|
||||
|
||||
pagination.init();
|
||||
|
||||
$(window).trigger('action:app.load');
|
||||
});
|
||||
});
|
||||
@@ -143,14 +145,25 @@ app.cacheBuster = null;
|
||||
'icon:text': app.user['icon:text']
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
app.alertError(err.message);
|
||||
return;
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
app.currentRoom = room;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.leaveCurrentRoom = function() {
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
socket.emit('meta.rooms.leaveCurrent', function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
app.currentRoom = '';
|
||||
});
|
||||
}
|
||||
|
||||
function highlightNavigationLink() {
|
||||
var path = window.location.pathname;
|
||||
$('#main-nav li').removeClass('active');
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, ajaxify, socket, app, config, utils, bootbox */
|
||||
/* globals define, ajaxify, socket, app, config, templates, bootbox */
|
||||
|
||||
define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], function(header, uploader, translator) {
|
||||
var AccountEdit = {},
|
||||
uploadedPicture = '',
|
||||
selectedImageType = '',
|
||||
currentEmail;
|
||||
selectedImageType = '';
|
||||
|
||||
AccountEdit.init = function() {
|
||||
uploadedPicture = ajaxify.data.uploadedpicture;
|
||||
@@ -23,12 +22,9 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
});
|
||||
});
|
||||
|
||||
currentEmail = $('#inputEmail').val();
|
||||
|
||||
handleImageChange();
|
||||
handleAccountDelete();
|
||||
handleEmailConfirm();
|
||||
handlePasswordChange();
|
||||
updateSignature();
|
||||
updateAboutMe();
|
||||
};
|
||||
@@ -36,8 +32,6 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
function updateProfile() {
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
username: $('#inputUsername').val(),
|
||||
email: $('#inputEmail').val(),
|
||||
fullname: $('#inputFullname').val(),
|
||||
website: $('#inputWebsite').val(),
|
||||
birthday: $('#inputBirthday').val(),
|
||||
@@ -57,27 +51,13 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
$('#user-current-picture').attr('src', data.picture);
|
||||
}
|
||||
|
||||
if (data.userslug) {
|
||||
var oldslug = $('.account-username-box').attr('data-userslug');
|
||||
$('.account-username-box a').each(function() {
|
||||
$(this).attr('href', $(this).attr('href').replace(oldslug, data.userslug));
|
||||
});
|
||||
|
||||
$('.account-username-box').attr('data-userslug', data.userslug);
|
||||
}
|
||||
|
||||
if (currentEmail !== data.email) {
|
||||
currentEmail = data.email;
|
||||
$('#confirm-email').removeClass('hide');
|
||||
}
|
||||
|
||||
updateHeader(data.picture, userData.username, data.userslug);
|
||||
updateHeader(data.picture);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateHeader(picture, username, userslug) {
|
||||
function updateHeader(picture) {
|
||||
require(['components'], function(components) {
|
||||
if (parseInt(ajaxify.data.theirid, 10) !== parseInt(ajaxify.data.yourid, 10)) {
|
||||
return;
|
||||
@@ -88,11 +68,6 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
if (picture) {
|
||||
components.get('header/userpicture').attr('src', picture);
|
||||
}
|
||||
|
||||
if (username && userslug) {
|
||||
components.get('header/profilelink').attr('href', config.relative_path + '/user/' + userslug);
|
||||
components.get('header/username').text(username);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -273,88 +248,6 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
});
|
||||
}
|
||||
|
||||
function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var password = $('#inputNewPassword');
|
||||
var password_confirm = $('#inputNewPasswordAgain');
|
||||
var passwordvalid = false;
|
||||
var passwordsmatch = false;
|
||||
|
||||
function onPasswordChanged() {
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
showError(password_notify, '[[user:change_password_error_length]]');
|
||||
passwordvalid = false;
|
||||
} else if (!utils.isPasswordValid(password.val())) {
|
||||
showError(password_notify, '[[user:change_password_error]]');
|
||||
passwordvalid = false;
|
||||
} else {
|
||||
showSuccess(password_notify);
|
||||
passwordvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onPasswordConfirmChanged() {
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, '[[user:change_password_error_match]]');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
if (password.val()) {
|
||||
showSuccess(password_confirm_notify);
|
||||
} else {
|
||||
password_confirm_notify.parent().removeClass('alert-success alert-danger');
|
||||
password_confirm_notify.children().show();
|
||||
password_confirm_notify.find('.msg').html('');
|
||||
}
|
||||
|
||||
passwordsmatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
password.on('blur', onPasswordChanged);
|
||||
password_confirm.on('blur', onPasswordConfirmChanged);
|
||||
|
||||
$('#changePasswordBtn').on('click', function() {
|
||||
onPasswordChanged();
|
||||
onPasswordConfirmChanged();
|
||||
|
||||
var btn = $(this);
|
||||
if ((passwordvalid && passwordsmatch) || app.user.isAdmin) {
|
||||
btn.addClass('disabled').find('i').removeClass('hide');
|
||||
socket.emit('user.changePassword', {
|
||||
'currentPassword': currentPassword.val(),
|
||||
'newPassword': password.val(),
|
||||
'uid': ajaxify.data.theirid
|
||||
}, function(err) {
|
||||
btn.removeClass('disabled').find('i').addClass('hide');
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if (err) {
|
||||
onPasswordChanged();
|
||||
onPasswordConfirmChanged();
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
app.alertSuccess('[[user:change_password_success]]');
|
||||
});
|
||||
} else {
|
||||
if (!passwordsmatch) {
|
||||
app.alertError('[[user:change_password_error_match]]');
|
||||
}
|
||||
|
||||
if (!passwordvalid) {
|
||||
app.alertError('[[user:change_password_error]]');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function changeUserPicture(type, callback) {
|
||||
socket.emit('user.changePicture', {
|
||||
type: type,
|
||||
@@ -384,24 +277,6 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'],
|
||||
});
|
||||
}
|
||||
|
||||
function showError(element, msg) {
|
||||
translator.translate(msg, function(msg) {
|
||||
element.find('.error').html(msg).removeClass('hide').siblings().addClass('hide');
|
||||
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
});
|
||||
}
|
||||
|
||||
function showSuccess(element) {
|
||||
element.find('.success').removeClass('hide').siblings().addClass('hide');
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
return AccountEdit;
|
||||
});
|
||||
});
|
||||
|
||||
39
public/src/client/account/edit/email.js
Normal file
39
public/src/client/account/edit/email.js
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, ajaxify, socket, app */
|
||||
|
||||
define('forum/account/edit/email', ['forum/account/header'], function(header) {
|
||||
var AccountEditEmail = {};
|
||||
|
||||
AccountEditEmail.init = function() {
|
||||
header.init();
|
||||
|
||||
$('#submitBtn').on('click', function () {
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
email: $('#inputNewEmail').val(),
|
||||
password: $('#inputCurrentPassword').val()
|
||||
};
|
||||
|
||||
if (!userData.email) {
|
||||
return;
|
||||
}
|
||||
|
||||
var btn = $(this);
|
||||
btn.addClass('disabled').find('i').removeClass('hide');
|
||||
|
||||
socket.emit('user.changeUsernameEmail', userData, function(err) {
|
||||
btn.removeClass('disabled').find('i').addClass('hide');
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
ajaxify.go('user/' + ajaxify.data.userslug);
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
return AccountEditEmail;
|
||||
});
|
||||
116
public/src/client/account/edit/password.js
Normal file
116
public/src/client/account/edit/password.js
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, ajaxify, socket, app, config, utils */
|
||||
|
||||
define('forum/account/edit/password', ['forum/account/header', 'translator'], function(header, translator) {
|
||||
var AccountEditPassword = {};
|
||||
|
||||
AccountEditPassword.init = function() {
|
||||
header.init();
|
||||
|
||||
handlePasswordChange();
|
||||
};
|
||||
|
||||
function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var password = $('#inputNewPassword');
|
||||
var password_confirm = $('#inputNewPasswordAgain');
|
||||
var passwordvalid = false;
|
||||
var passwordsmatch = false;
|
||||
|
||||
function onPasswordChanged() {
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
showError(password_notify, '[[user:change_password_error_length]]');
|
||||
passwordvalid = false;
|
||||
} else if (!utils.isPasswordValid(password.val())) {
|
||||
showError(password_notify, '[[user:change_password_error]]');
|
||||
passwordvalid = false;
|
||||
} else {
|
||||
showSuccess(password_notify);
|
||||
passwordvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onPasswordConfirmChanged() {
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, '[[user:change_password_error_match]]');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
if (password.val()) {
|
||||
showSuccess(password_confirm_notify);
|
||||
} else {
|
||||
password_confirm_notify.parent().removeClass('alert-success alert-danger');
|
||||
password_confirm_notify.children().show();
|
||||
password_confirm_notify.find('.msg').html('');
|
||||
}
|
||||
|
||||
passwordsmatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
password.on('blur', onPasswordChanged);
|
||||
password_confirm.on('blur', onPasswordConfirmChanged);
|
||||
|
||||
$('#changePasswordBtn').on('click', function() {
|
||||
onPasswordChanged();
|
||||
onPasswordConfirmChanged();
|
||||
|
||||
var btn = $(this);
|
||||
if ((passwordvalid && passwordsmatch) || app.user.isAdmin) {
|
||||
btn.addClass('disabled').find('i').removeClass('hide');
|
||||
socket.emit('user.changePassword', {
|
||||
'currentPassword': currentPassword.val(),
|
||||
'newPassword': password.val(),
|
||||
'uid': ajaxify.data.theirid
|
||||
}, function(err) {
|
||||
btn.removeClass('disabled').find('i').addClass('hide');
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if (err) {
|
||||
onPasswordChanged();
|
||||
onPasswordConfirmChanged();
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
ajaxify.go('user/' + ajaxify.data.userslug);
|
||||
app.alertSuccess('[[user:change_password_success]]');
|
||||
});
|
||||
} else {
|
||||
if (!passwordsmatch) {
|
||||
app.alertError('[[user:change_password_error_match]]');
|
||||
}
|
||||
|
||||
if (!passwordvalid) {
|
||||
app.alertError('[[user:change_password_error]]');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function showError(element, msg) {
|
||||
translator.translate(msg, function(msg) {
|
||||
element.find('.error').html(msg).removeClass('hide').siblings().addClass('hide');
|
||||
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
});
|
||||
}
|
||||
|
||||
function showSuccess(element) {
|
||||
element.find('.success').removeClass('hide').siblings().addClass('hide');
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
return AccountEditPassword;
|
||||
});
|
||||
43
public/src/client/account/edit/username.js
Normal file
43
public/src/client/account/edit/username.js
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, ajaxify, socket, app, utils, config */
|
||||
|
||||
define('forum/account/edit/username', ['forum/account/header'], function(header) {
|
||||
var AccountEditUsername = {};
|
||||
|
||||
AccountEditUsername.init = function() {
|
||||
header.init();
|
||||
|
||||
$('#submitBtn').on('click', function updateUsername() {
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
username: $('#inputNewUsername').val(),
|
||||
password: $('#inputCurrentPassword').val()
|
||||
};
|
||||
|
||||
if (!userData.username) {
|
||||
return;
|
||||
}
|
||||
var btn = $(this);
|
||||
btn.addClass('disabled').find('i').removeClass('hide');
|
||||
socket.emit('user.changeUsernameEmail', userData, function(err) {
|
||||
btn.removeClass('disabled').find('i').addClass('hide');
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
var userslug = utils.slugify(userData.username);
|
||||
if (userData.username && userslug && parseInt(userData.uid, 10) === parseInt(app.user.uid, 10)) {
|
||||
$('[component="header/profilelink"]').attr('href', config.relative_path + '/user/' + userslug);
|
||||
$('[component="header/username"]').text(userData.username);
|
||||
}
|
||||
|
||||
ajaxify.go('user/' + userslug);
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
return AccountEditUsername;
|
||||
});
|
||||
@@ -2,7 +2,6 @@
|
||||
/* global define, config, templates, app, utils, ajaxify, socket */
|
||||
|
||||
define('forum/category', [
|
||||
'forum/pagination',
|
||||
'forum/infinitescroll',
|
||||
'share',
|
||||
'navigator',
|
||||
@@ -10,13 +9,12 @@ define('forum/category', [
|
||||
'sort',
|
||||
'components',
|
||||
'translator'
|
||||
|
||||
], function(pagination, infinitescroll, share, navigator, categoryTools, sort, components, translator) {
|
||||
], function(infinitescroll, share, navigator, categoryTools, sort, components, translator) {
|
||||
var Category = {};
|
||||
|
||||
$(window).on('action:ajaxify.start', function(ev, data) {
|
||||
if (ajaxify.currentPage !== data.url) {
|
||||
navigator.hide();
|
||||
navigator.disable();
|
||||
|
||||
removeListeners();
|
||||
}
|
||||
@@ -41,12 +39,12 @@ define('forum/category', [
|
||||
|
||||
sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.data.slug);
|
||||
|
||||
enableInfiniteLoadingOrPagination();
|
||||
|
||||
if (!config.usePagination) {
|
||||
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback);
|
||||
}
|
||||
|
||||
enableInfiniteLoadingOrPagination();
|
||||
|
||||
$('[component="category"]').on('click', '[component="topic/header"]', function() {
|
||||
var clickedIndex = $(this).parents('[data-index]').attr('data-index');
|
||||
$('[component="category/topic"]').each(function(index, el) {
|
||||
@@ -112,7 +110,7 @@ define('forum/category', [
|
||||
|
||||
if (config.usePagination) {
|
||||
var page = Math.ceil((parseInt(bookmarkIndex, 10) + 1) / config.topicsPerPage);
|
||||
if (parseInt(page, 10) !== pagination.currentPage) {
|
||||
if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) {
|
||||
pagination.loadPage(page, function() {
|
||||
Category.scrollToTopic(bookmarkIndex, clickedIndex, 400);
|
||||
});
|
||||
@@ -175,8 +173,7 @@ define('forum/category', [
|
||||
if (!config.usePagination) {
|
||||
infinitescroll.init($('[component="category"]'), Category.loadMoreTopics);
|
||||
} else {
|
||||
navigator.hide();
|
||||
pagination.init(ajaxify.data.currentPage, ajaxify.data.pageCount);
|
||||
navigator.disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll',
|
||||
|
||||
Chats.switchChat = function(uid, username) {
|
||||
if (!$('[component="chat/messages"]').length) {
|
||||
ajaxify.go('chats/' + username);
|
||||
return ajaxify.go('chats/' + utils.slugify(username));
|
||||
}
|
||||
|
||||
var contactEl = $('.chats-list [data-uid="' + uid + '"]');
|
||||
|
||||
@@ -7,21 +7,13 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu
|
||||
Chat.prepareDOM();
|
||||
translator.prepareDOM();
|
||||
|
||||
function updateUnreadTopicCount(err, count) {
|
||||
if (err) {
|
||||
return console.warn('Error updating unread count', err);
|
||||
}
|
||||
|
||||
function updateUnreadTopicCount(count) {
|
||||
$('#unread-count i')
|
||||
.toggleClass('unread-count', count > 0)
|
||||
.attr('data-content', count > 20 ? '20+' : count);
|
||||
}
|
||||
|
||||
function updateUnreadChatCount(err, count) {
|
||||
if (err) {
|
||||
return console.warn('Error updating unread count', err);
|
||||
}
|
||||
|
||||
function updateUnreadChatCount(count) {
|
||||
components.get('chat/icon')
|
||||
.toggleClass('unread-count', count > 0)
|
||||
.attr('data-content', count > 20 ? '20+' : count);
|
||||
@@ -62,11 +54,20 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu
|
||||
socket.on('event:new_post', onNewPost);
|
||||
}
|
||||
|
||||
socket.on('event:unread.updateCount', updateUnreadTopicCount);
|
||||
socket.emit('user.getUnreadCount', updateUnreadTopicCount);
|
||||
if (app.user.uid) {
|
||||
socket.emit('user.getUnreadCounts', function(err, data) {
|
||||
if (err) {
|
||||
return app.alert(err.message);
|
||||
}
|
||||
|
||||
updateUnreadTopicCount(data.unreadTopicCount);
|
||||
updateUnreadChatCount(data.unreadChatCount);
|
||||
Notifications.updateNotifCount(data.unreadNotificationCount);
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('event:unread.updateCount', updateUnreadTopicCount);
|
||||
socket.on('event:unread.updateChatCount', updateUnreadChatCount);
|
||||
socket.emit('user.getUnreadChatCount', updateUnreadChatCount);
|
||||
|
||||
initUnreadTopics();
|
||||
});
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
"use strict";
|
||||
/* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH, utils */
|
||||
/* globals define, socket, ajaxify, app, bootbox, utils */
|
||||
|
||||
define('forum/groups/details', [
|
||||
'forum/groups/memberlist',
|
||||
'iconSelect',
|
||||
'components',
|
||||
'vendor/colorpicker/colorpicker',
|
||||
'vendor/jquery/draggable-background/backgroundDraggable'
|
||||
], function(memberList, iconSelect, components) {
|
||||
|
||||
define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescroll', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect, components, infinitescroll) {
|
||||
var Details = {
|
||||
cover: {}
|
||||
};
|
||||
|
||||
var searchInterval;
|
||||
var groupName;
|
||||
|
||||
Details.init = function() {
|
||||
var detailsPage = components.get('groups/container'),
|
||||
settingsFormEl = detailsPage.find('form');
|
||||
var detailsPage = components.get('groups/container');
|
||||
|
||||
groupName = ajaxify.data.group.name;
|
||||
|
||||
@@ -20,8 +25,8 @@ define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescrol
|
||||
Details.initialiseCover();
|
||||
}
|
||||
|
||||
handleMemberSearch();
|
||||
handleMemberInfiniteScroll();
|
||||
memberList.init();
|
||||
|
||||
handleMemberInvitations();
|
||||
|
||||
components.get('groups/activity').find('.content img:not(.not-responsive)').addClass('img-responsive');
|
||||
@@ -291,44 +296,6 @@ define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescrol
|
||||
});
|
||||
};
|
||||
|
||||
function handleMemberSearch() {
|
||||
$('[component="groups/members/search"]').on('keyup', function() {
|
||||
var query = $(this).val();
|
||||
if (searchInterval) {
|
||||
clearInterval(searchInterval);
|
||||
searchInterval = 0;
|
||||
}
|
||||
|
||||
searchInterval = setTimeout(function() {
|
||||
socket.emit('groups.searchMembers', {groupName: groupName, query: query}, function(err, results) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
infinitescroll.parseAndTranslate('groups/details', 'members', {
|
||||
group: {
|
||||
members: results.users,
|
||||
isOwner: ajaxify.data.group.isOwner
|
||||
}
|
||||
}, function(html) {
|
||||
$('[component="groups/members"] tbody').html(html);
|
||||
$('[component="groups/members"]').attr('data-nextstart', 20);
|
||||
});
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMemberInfiniteScroll() {
|
||||
$('[component="groups/members"] tbody').on('scroll', function() {
|
||||
var $this = $(this);
|
||||
var bottom = ($this[0].scrollHeight - $this.height()) * 0.9;
|
||||
if ($this.scrollTop() > bottom) {
|
||||
loadMoreMembers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleMemberInvitations() {
|
||||
if (ajaxify.data.group.isOwner) {
|
||||
var searchInput = $('[component="groups/members/invite"]');
|
||||
@@ -349,48 +316,5 @@ define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescrol
|
||||
}
|
||||
}
|
||||
|
||||
function loadMoreMembers() {
|
||||
|
||||
var members = $('[component="groups/members"]');
|
||||
if (members.attr('loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
members.attr('loading', 1);
|
||||
socket.emit('groups.loadMoreMembers', {
|
||||
groupName: groupName,
|
||||
after: members.attr('data-nextstart')
|
||||
}, function(err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (data && data.users.length) {
|
||||
onMembersLoaded(data.users, function() {
|
||||
members.removeAttr('loading');
|
||||
members.attr('data-nextstart', data.nextStart);
|
||||
});
|
||||
} else {
|
||||
members.removeAttr('loading');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onMembersLoaded(users, callback) {
|
||||
users = users.filter(function(user) {
|
||||
return !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length;
|
||||
});
|
||||
|
||||
infinitescroll.parseAndTranslate('groups/details', 'members', {
|
||||
group: {
|
||||
members: users,
|
||||
isOwner: ajaxify.data.group.isOwner
|
||||
}
|
||||
}, function(html) {
|
||||
$('[component="groups/members"] tbody').append(html);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
return Details;
|
||||
});
|
||||
97
public/src/client/groups/memberlist.js
Normal file
97
public/src/client/groups/memberlist.js
Normal file
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
/* globals define, socket, ajaxify, app */
|
||||
|
||||
define('forum/groups/memberlist', ['components', 'forum/infinitescroll'], function(components, infinitescroll) {
|
||||
|
||||
var MemberList = {};
|
||||
var searchInterval;
|
||||
var groupName;
|
||||
|
||||
MemberList.init = function() {
|
||||
groupName = ajaxify.data.group.name;
|
||||
|
||||
handleMemberSearch();
|
||||
handleMemberInfiniteScroll();
|
||||
};
|
||||
|
||||
function handleMemberSearch() {
|
||||
$('[component="groups/members/search"]').on('keyup', function() {
|
||||
var query = $(this).val();
|
||||
if (searchInterval) {
|
||||
clearInterval(searchInterval);
|
||||
searchInterval = 0;
|
||||
}
|
||||
|
||||
searchInterval = setTimeout(function() {
|
||||
socket.emit('groups.searchMembers', {groupName: groupName, query: query}, function(err, results) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
parseAndTranslate(results.users, function(html) {
|
||||
$('[component="groups/members"] tbody').html(html);
|
||||
$('[component="groups/members"]').attr('data-nextstart', 20);
|
||||
});
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMemberInfiniteScroll() {
|
||||
$('[component="groups/members"] tbody').on('scroll', function() {
|
||||
var $this = $(this);
|
||||
var bottom = ($this[0].scrollHeight - $this.innerHeight()) * 0.9;
|
||||
|
||||
if ($this.scrollTop() > bottom) {
|
||||
loadMoreMembers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadMoreMembers() {
|
||||
var members = $('[component="groups/members"]');
|
||||
if (members.attr('loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
members.attr('loading', 1);
|
||||
socket.emit('groups.loadMoreMembers', {
|
||||
groupName: groupName,
|
||||
after: members.attr('data-nextstart')
|
||||
}, function(err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
if (data && data.users.length) {
|
||||
onMembersLoaded(data.users, function() {
|
||||
members.removeAttr('loading');
|
||||
members.attr('data-nextstart', data.nextStart);
|
||||
});
|
||||
} else {
|
||||
members.removeAttr('loading');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onMembersLoaded(users, callback) {
|
||||
users = users.filter(function(user) {
|
||||
return !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length;
|
||||
});
|
||||
|
||||
parseAndTranslate(users, function(html) {
|
||||
$('[component="groups/members"] tbody').append(html);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function parseAndTranslate(users, callback) {
|
||||
infinitescroll.parseAndTranslate('groups/details', 'members', {
|
||||
group: {
|
||||
members: users,
|
||||
isOwner: ajaxify.data.group.isOwner
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
|
||||
return MemberList;
|
||||
});
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
/* globals define, socket, app */
|
||||
|
||||
define('forum/notifications', ['components', 'notifications'], function(components, notifs) {
|
||||
define('forum/notifications', ['components', 'notifications', 'forum/infinitescroll'], function(components, notifs, infinitescroll) {
|
||||
var Notifications = {};
|
||||
|
||||
Notifications.init = function() {
|
||||
var listEl = $('.notifications-list');
|
||||
listEl.on('click', '[component="notifications/item/link"]', function(e) {
|
||||
listEl.on('click', '[component="notifications/item/link"]', function() {
|
||||
var nid = $(this).parents('[data-nid]').attr('data-nid');
|
||||
socket.emit('notifications.markRead', nid, function(err) {
|
||||
if (err) {
|
||||
@@ -28,7 +28,32 @@ define('forum/notifications', ['components', 'notifications'], function(componen
|
||||
notifs.updateNotifCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
infinitescroll.init(loadMoreNotifications);
|
||||
};
|
||||
|
||||
function loadMoreNotifications(direction) {
|
||||
if (direction < 0) {
|
||||
return;
|
||||
}
|
||||
var notifList = $('.notifications-list');
|
||||
infinitescroll.loadMore('notifications.loadMore', {
|
||||
after: notifList.attr('data-nextstart')
|
||||
}, function(data, done) {
|
||||
if (!data) {
|
||||
return done();
|
||||
}
|
||||
notifList.attr('data-nextstart', data.nextStart);
|
||||
if (!data.notifications || !data.notifications.length) {
|
||||
return done();
|
||||
}
|
||||
infinitescroll.parseAndTranslate('notifications', 'notifications', {notifications: data.notifications}, function(html) {
|
||||
notifList.append(html);
|
||||
html.find('.timeago').timeago();
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Notifications;
|
||||
});
|
||||
|
||||
@@ -4,14 +4,8 @@
|
||||
define('forum/pagination', function() {
|
||||
var pagination = {};
|
||||
|
||||
pagination.currentPage = 0;
|
||||
pagination.pageCount = 0;
|
||||
|
||||
pagination.init = function(currentPage, pageCount) {
|
||||
pagination.currentPage = parseInt(currentPage, 10);
|
||||
pagination.pageCount = parseInt(pageCount, 10);
|
||||
|
||||
$('.pagination').on('click', '.select-page', function(e) {
|
||||
pagination.init = function() {
|
||||
$('body').on('click', '.pagination .select-page', function(e) {
|
||||
e.preventDefault();
|
||||
bootbox.prompt('Enter page number:', function(pageNum) {
|
||||
pagination.loadPage(pageNum);
|
||||
@@ -22,10 +16,14 @@ define('forum/pagination', function() {
|
||||
pagination.loadPage = function(page, callback) {
|
||||
callback = callback || function() {};
|
||||
page = parseInt(page, 10);
|
||||
if (!utils.isNumber(page) || page < 1 || page > pagination.pageCount) {
|
||||
if (!utils.isNumber(page) || page < 1 || page > ajaxify.data.pagination.pageCount) {
|
||||
return;
|
||||
}
|
||||
var url = window.location.pathname.slice(1).split('/').slice(0, 3).join('/') + '?page=' + page;
|
||||
|
||||
var query = utils.params();
|
||||
query.page = page;
|
||||
|
||||
var url = window.location.pathname + '?' + $.param(query);
|
||||
ajaxify.go(url, callback);
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ define('forum/tags', ['forum/infinitescroll'], function(infinitescroll) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
onTagsLoaded(results, true, function() {
|
||||
onTagsLoaded(results.tags, true, function() {
|
||||
timeoutId = 0;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
/* globals define, app, socket, config, ajaxify, RELATIVE_PATH, utils */
|
||||
|
||||
define('forum/topic', [
|
||||
'forum/pagination',
|
||||
'forum/infinitescroll',
|
||||
'forum/topic/threadTools',
|
||||
'forum/topic/postTools',
|
||||
@@ -14,13 +13,13 @@ define('forum/topic', [
|
||||
'navigator',
|
||||
'sort',
|
||||
'components'
|
||||
], function(pagination, infinitescroll, threadTools, postTools, events, browsing, posts, navigator, sort, components) {
|
||||
], function(infinitescroll, threadTools, postTools, events, browsing, posts, navigator, sort, components) {
|
||||
var Topic = {},
|
||||
currentUrl = '';
|
||||
|
||||
$(window).on('action:ajaxify.start', function(ev, data) {
|
||||
if (ajaxify.currentPage !== data.url) {
|
||||
navigator.hide();
|
||||
navigator.disable();
|
||||
components.get('navbar/title').find('span').text('').hide();
|
||||
app.removeAlert('bookmark');
|
||||
|
||||
@@ -147,7 +146,7 @@ define('forum/topic', [
|
||||
if (components.get('post/anchor', postIndex).length) {
|
||||
return navigator.scrollToPostIndex(postIndex, true);
|
||||
}
|
||||
} else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && ajaxify.data.postcount > 10) {
|
||||
} else if (bookmark && (!config.usePagination || (config.usePagination && ajaxify.data.pagination.currentPage === 1)) && ajaxify.data.postcount > 5) {
|
||||
app.alert({
|
||||
alert_id: 'bookmark',
|
||||
message: '[[topic:bookmark_instructions]]',
|
||||
@@ -217,13 +216,10 @@ define('forum/topic', [
|
||||
if (!config.usePagination) {
|
||||
infinitescroll.init($('[component="topic"]'), posts.loadMorePosts);
|
||||
} else {
|
||||
navigator.hide();
|
||||
|
||||
pagination.init(parseInt(ajaxify.data.currentPage, 10), parseInt(ajaxify.data.pageCount, 10));
|
||||
navigator.disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateTopicTitle() {
|
||||
if ($(window).scrollTop() > 50) {
|
||||
components.get('navbar/title').find('span').text(ajaxify.data.title).show();
|
||||
@@ -281,7 +277,7 @@ define('forum/topic', [
|
||||
var bookmarkKey = 'topic:' + ajaxify.data.tid + ':bookmark';
|
||||
var currentBookmark = ajaxify.data.bookmark || localStorage.getItem(bookmarkKey);
|
||||
|
||||
if (!currentBookmark || parseInt(index, 10) > parseInt(currentBookmark, 10)) {
|
||||
if (ajaxify.data.postcount > 5 && (!currentBookmark || parseInt(index, 10) > parseInt(currentBookmark, 10))) {
|
||||
if (app.user.uid) {
|
||||
socket.emit('topics.bookmark', {
|
||||
'tid': ajaxify.data.tid,
|
||||
|
||||
@@ -162,6 +162,7 @@ define('forum/topic/events', [
|
||||
function onPostPurged(pid) {
|
||||
components.get('post', 'pid', pid).fadeOut(500, function() {
|
||||
$(this).remove();
|
||||
posts.showBottomPostBar();
|
||||
});
|
||||
|
||||
postTools.updatePostCount();
|
||||
|
||||
64
public/src/client/topic/flag.js
Normal file
64
public/src/client/topic/flag.js
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, app, socket, templates, translator */
|
||||
|
||||
define('forum/topic/flag', [], function() {
|
||||
|
||||
var Flag = {},
|
||||
flagModal,
|
||||
flagCommit;
|
||||
|
||||
Flag.showFlagModal = function(pid) {
|
||||
parseModal(function(html) {
|
||||
flagModal = $(html);
|
||||
|
||||
flagModal.on('hidden.bs.modal', function() {
|
||||
flagModal.remove();
|
||||
});
|
||||
|
||||
flagCommit = flagModal.find('#flag-post-commit');
|
||||
|
||||
flagModal.on('click', '.flag-reason', function() {
|
||||
flagPost(pid, $(this).text());
|
||||
});
|
||||
|
||||
flagCommit.on('click', function() {
|
||||
flagPost(pid, flagModal.find('#flag-reason-custom').val());
|
||||
});
|
||||
|
||||
flagModal.modal('show');
|
||||
|
||||
flagModal.find('#flag-reason-custom').on('keyup blur change', checkFlagButtonEnable);
|
||||
});
|
||||
};
|
||||
|
||||
function parseModal(callback) {
|
||||
templates.parse('partials/modals/flag_post_modal', {}, function(html) {
|
||||
translator.translate(html, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function flagPost(pid, reason) {
|
||||
if (!pid || !reason) {
|
||||
return;
|
||||
}
|
||||
socket.emit('posts.flag', {pid: pid, reason: reason}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
flagModal.modal('hide');
|
||||
app.alertSuccess('[[topic:flag_success]]');
|
||||
});
|
||||
}
|
||||
|
||||
function checkFlagButtonEnable() {
|
||||
if (flagModal.find('#flag-reason-custom').val()) {
|
||||
flagCommit.removeAttr('disabled');
|
||||
} else {
|
||||
flagCommit.attr('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
return Flag;
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, app, ajaxify, bootbox, socket, templates, utils */
|
||||
/* globals define, app, ajaxify, bootbox, socket, templates, utils, config */
|
||||
|
||||
define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator'], function(share, navigator, components, translator) {
|
||||
|
||||
@@ -15,6 +15,8 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
share.addShareHandlers(topicName);
|
||||
|
||||
addVoteHandler();
|
||||
|
||||
PostTools.updatePostCount(ajaxify.data.postcount);
|
||||
};
|
||||
|
||||
PostTools.toggle = function(pid, isDeleted) {
|
||||
@@ -28,20 +30,16 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
postEl.find('[component="post/purge"]').toggleClass('hidden', !isDeleted);
|
||||
};
|
||||
|
||||
PostTools.updatePostCount = function() {
|
||||
socket.emit('topics.postcount', ajaxify.data.tid, function(err, postCount) {
|
||||
if (!err) {
|
||||
var postCountEl = components.get('topic/post-count');
|
||||
postCountEl.html(postCount).attr('title', postCount);
|
||||
utils.makeNumbersHumanReadable(postCountEl);
|
||||
navigator.setCount(postCount);
|
||||
}
|
||||
});
|
||||
PostTools.updatePostCount = function(postCount) {
|
||||
var postCountEl = components.get('topic/post-count');
|
||||
postCountEl.html(postCount).attr('title', postCount);
|
||||
utils.makeNumbersHumanReadable(postCountEl);
|
||||
navigator.setCount(postCount);
|
||||
};
|
||||
|
||||
function addVoteHandler() {
|
||||
components.get('topic').on('mouseenter', '[data-pid] .votes', function() {
|
||||
loadDataAndCreateTooltip($(this));
|
||||
components.get('topic').on('mouseenter', '[data-pid] [component="post/vote-count"]', function() {
|
||||
loadDataAndCreateTooltip($(this).parent());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,6 +53,13 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
}
|
||||
|
||||
function createTooltip(el, data) {
|
||||
function doCreateTooltip(title) {
|
||||
el.attr('title', title).tooltip('fixTitle').tooltip('show');
|
||||
el.on('hidden.bs.tooltip', function() {
|
||||
el.tooltip('destroy');
|
||||
el.off('hidden.bs.tooltip');
|
||||
});
|
||||
}
|
||||
var usernames = data.usernames;
|
||||
if (!usernames.length) {
|
||||
return;
|
||||
@@ -63,11 +68,11 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
usernames = usernames.join(', ').replace(/,/g, '|');
|
||||
translator.translate('[[topic:users_and_others, ' + usernames + ', ' + data.otherCount + ']]', function(translated) {
|
||||
translated = translated.replace(/\|/g, ',');
|
||||
el.attr('title', translated).tooltip('destroy').tooltip('show');
|
||||
doCreateTooltip(translated);
|
||||
});
|
||||
} else {
|
||||
usernames = usernames.join(', ');
|
||||
el.attr('title', usernames).tooltip('destroy').tooltip('show');
|
||||
doCreateTooltip(usernames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,40 +108,42 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/flag"]', function() {
|
||||
flagPost(getData($(this), 'data-pid'));
|
||||
var pid = getData($(this), 'data-pid');
|
||||
require(['forum/topic/flag'], function(flag) {
|
||||
flag.showFlagModal(pid);
|
||||
});
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/edit"]', function(e) {
|
||||
postContainer.on('click', '[component="post/edit"]', function() {
|
||||
var btn = $(this);
|
||||
$(window).trigger('action:composer.post.edit', {
|
||||
pid: getData(btn, 'data-pid')
|
||||
});
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/delete"]', function(e) {
|
||||
postContainer.on('click', '[component="post/delete"]', function() {
|
||||
togglePostDelete($(this), tid);
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/restore"]', function(e) {
|
||||
postContainer.on('click', '[component="post/restore"]', function() {
|
||||
togglePostDelete($(this), tid);
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/purge"]', function(e) {
|
||||
postContainer.on('click', '[component="post/purge"]', function() {
|
||||
purgePost($(this), tid);
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/move"]', function(e) {
|
||||
postContainer.on('click', '[component="post/move"]', function() {
|
||||
openMovePostModal($(this));
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/chat"]', function(e) {
|
||||
postContainer.on('click', '[component="post/chat"]', function() {
|
||||
openChat($(this));
|
||||
});
|
||||
}
|
||||
|
||||
function onReplyClicked(button, tid, topicName) {
|
||||
showStaleWarning(function(proceed) {
|
||||
console.log('proceed is', proceed);
|
||||
if (!proceed) {
|
||||
var selectionText = '',
|
||||
selection = window.getSelection ? window.getSelection() : document.selection.createRange();
|
||||
@@ -363,22 +370,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
});
|
||||
}
|
||||
|
||||
function flagPost(pid) {
|
||||
translator.translate('[[topic:flag_confirm]]', function(message) {
|
||||
bootbox.confirm(message, function(confirm) {
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
socket.emit('posts.flag', pid, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
app.alertSuccess('[[topic:flag_success]]');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openChat(button) {
|
||||
var post = button.parents('[data-pid]');
|
||||
|
||||
@@ -28,6 +28,8 @@ define('forum/topic/posts', [
|
||||
});
|
||||
|
||||
updatePostCounts(data.posts);
|
||||
ajaxify.data.postcount ++;
|
||||
postTools.updatePostCount(ajaxify.data.postcount);
|
||||
|
||||
if (config.usePagination) {
|
||||
onNewPostPagination(data);
|
||||
@@ -51,15 +53,15 @@ define('forum/topic/posts', [
|
||||
|
||||
var posts = data.posts;
|
||||
|
||||
pagination.pageCount = Math.max(1, Math.ceil((posts[0].topic.postcount - 1) / config.postsPerPage));
|
||||
ajaxify.data.pagination.pageCount = Math.max(1, Math.ceil((posts[0].topic.postcount - 1) / config.postsPerPage));
|
||||
var direction = config.topicPostSort === 'oldest_to_newest' || config.topicPostSort === 'most_votes' ? 1 : -1;
|
||||
|
||||
var isPostVisible = (pagination.currentPage === pagination.pageCount && direction === 1) || (pagination.currentPage === 1 && direction === -1);
|
||||
var isPostVisible = (ajaxify.data.pagination.currentPage === ajaxify.data.pagination.pageCount && direction === 1) || (ajaxify.data.pagination.currentPage === 1 && direction === -1);
|
||||
|
||||
if (isPostVisible) {
|
||||
createNewPosts(data, components.get('post').not('[data-index=0]'), direction, scrollToPost);
|
||||
} else if (parseInt(posts[0].uid, 10) === parseInt(app.user.uid, 10)) {
|
||||
pagination.loadPage(pagination.pageCount, scrollToPost);
|
||||
pagination.loadPage(ajaxify.data.pagination.pageCount, scrollToPost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,17 +222,15 @@ define('forum/topic/posts', [
|
||||
$this.wrap('<a href="' + $this.attr('src') + '" target="_blank">');
|
||||
}
|
||||
});
|
||||
postTools.updatePostCount();
|
||||
addBlockquoteEllipses(posts.find('[component="post/content"] > blockquote'));
|
||||
|
||||
addBlockquoteEllipses(posts.find('[component="post/content"] > blockquote > blockquote'));
|
||||
hidePostToolsForDeletedPosts(posts);
|
||||
showBottomPostBar();
|
||||
Posts.showBottomPostBar();
|
||||
};
|
||||
|
||||
function showBottomPostBar() {
|
||||
if (components.get('post').length > 1 || !components.get('post', 'index', 0).length) {
|
||||
$('.bottom-post-bar').removeClass('hidden');
|
||||
}
|
||||
}
|
||||
Posts.showBottomPostBar = function() {
|
||||
$('.bottom-post-bar').toggleClass('hidden', components.get('post').length <= 1 && !!components.get('post', 'index', 0).length);
|
||||
};
|
||||
|
||||
function hidePostToolsForDeletedPosts(posts) {
|
||||
posts.each(function() {
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
var helpers = {};
|
||||
|
||||
helpers.displayMenuItem = function(data, index) {
|
||||
var item = data.navigation[index],
|
||||
properties = item.properties;
|
||||
var item = data.navigation[index];
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
var properties = item.properties;
|
||||
|
||||
if (properties) {
|
||||
if ((properties.loggedIn && !data.config.loggedIn) ||
|
||||
@@ -37,7 +40,7 @@
|
||||
property = tag.property ? 'property="' + tag.property + '" ' : '',
|
||||
content = tag.content ? 'content="' + tag.content.replace(/\n/g, ' ') + '" ' : '';
|
||||
|
||||
return '<meta ' + name + property + content + '/>';
|
||||
return '<meta ' + name + property + content + '/>\n\t';
|
||||
};
|
||||
|
||||
helpers.buildLinkTag = function(tag) {
|
||||
@@ -47,7 +50,7 @@
|
||||
href = tag.href ? 'href="' + tag.href + '" ' : '',
|
||||
sizes = tag.sizes ? 'sizes="' + tag.sizes + '" ' : '';
|
||||
|
||||
return '<link ' + link + rel + type + sizes + href + '/>';
|
||||
return '<link ' + link + rel + type + sizes + href + '/>\n\t';
|
||||
};
|
||||
|
||||
helpers.stringify = function(obj) {
|
||||
|
||||
@@ -18,7 +18,7 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
||||
toTop = toTop || function() {};
|
||||
toBottom = toBottom || function() {};
|
||||
|
||||
$(window).on('scroll', navigator.update);
|
||||
$(window).off('scroll', navigator.update).on('scroll', navigator.update);
|
||||
|
||||
$('.pagination-block .dropdown-menu').off('click').on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
@@ -74,7 +74,12 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
||||
toggle(true);
|
||||
};
|
||||
|
||||
navigator.hide = function() {
|
||||
navigator.disable = function() {
|
||||
count = 0;
|
||||
index = 1;
|
||||
navigator.selector = navigator.callback = null;
|
||||
$(window).off('scroll', navigator.update);
|
||||
|
||||
toggle(false);
|
||||
};
|
||||
|
||||
@@ -92,13 +97,19 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
||||
|
||||
var middleOfViewport = $(window).scrollTop() + $(window).height() / 2;
|
||||
|
||||
index = parseInt($(navigator.selector).first().attr('data-index'), 10);
|
||||
|
||||
index = parseInt($(navigator.selector).first().attr('data-index'), 10) + 1;
|
||||
var previousDistance = Number.MAX_VALUE;
|
||||
$(navigator.selector).each(function() {
|
||||
index++;
|
||||
if ($(this).offset().top > middleOfViewport) {
|
||||
var distanceToMiddle = Math.abs(middleOfViewport - $(this).offset().top);
|
||||
|
||||
if (distanceToMiddle > previousDistance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distanceToMiddle < previousDistance) {
|
||||
index = parseInt($(this).attr('data-index'), 10) + 1;
|
||||
previousDistance = distanceToMiddle;
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof navigator.callback === 'function') {
|
||||
@@ -162,7 +173,7 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
||||
if (config.usePagination) {
|
||||
var page = Math.max(1, Math.ceil(postIndex / config.postsPerPage));
|
||||
|
||||
if (parseInt(page, 10) !== pagination.currentPage) {
|
||||
if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) {
|
||||
pagination.loadPage(page, function() {
|
||||
navigator.scrollToPostIndex(postIndex, highlight, duration);
|
||||
});
|
||||
|
||||
@@ -64,14 +64,6 @@ define('notifications', ['sounds', 'translator', 'components'], function(sound,
|
||||
Notifications.updateNotifCount(count);
|
||||
}
|
||||
|
||||
socket.emit('notifications.getCount', function(err, count) {
|
||||
if (!err) {
|
||||
Notifications.updateNotifCount(count);
|
||||
} else {
|
||||
Notifications.updateNotifCount(0);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('event:new_notification', function(notifData) {
|
||||
app.alert({
|
||||
alert_id: 'new_notif',
|
||||
|
||||
@@ -10,7 +10,7 @@ define('sort', ['components'], function(components) {
|
||||
var currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]');
|
||||
currentSetting.find('i').addClass('fa-check');
|
||||
|
||||
components.get('topic').on('click', '[component="thread/sort"] a', function() {
|
||||
$('.category, .topic').on('click', '[component="thread/sort"] a', function() {
|
||||
var newSetting = $(this).attr('data-sort');
|
||||
socket.emit(method, newSetting, function(err) {
|
||||
if (err) {
|
||||
|
||||
@@ -5,31 +5,30 @@ define('sounds', ['buzz'], function(buzz) {
|
||||
var Sounds = {};
|
||||
|
||||
var loadedSounds = {};
|
||||
var eventSoundMapping = {};
|
||||
var files = {};
|
||||
|
||||
loadFiles();
|
||||
|
||||
loadMapping();
|
||||
var eventSoundMapping;
|
||||
var files;
|
||||
|
||||
socket.on('event:sounds.reloadMapping', loadMapping);
|
||||
|
||||
function loadFiles() {
|
||||
socket.emit('modules.sounds.getSounds', function(err, sounds) {
|
||||
if (err) {
|
||||
return app.alertError('[sounds] Could not initialise!');
|
||||
}
|
||||
|
||||
files = sounds;
|
||||
});
|
||||
}
|
||||
|
||||
function loadMapping() {
|
||||
function loadMapping(callback) {
|
||||
callback = callback || function() {};
|
||||
socket.emit('modules.sounds.getMapping', function(err, mapping) {
|
||||
if (err) {
|
||||
return app.alertError('[sounds] Could not load sound mapping!');
|
||||
}
|
||||
eventSoundMapping = mapping;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function loadData(callback) {
|
||||
socket.emit('modules.sounds.getData', function(err, data) {
|
||||
if (err) {
|
||||
return app.alertError('[sounds] Could not load sound mapping!');
|
||||
}
|
||||
eventSoundMapping = data.mapping;
|
||||
files = data.files;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,22 +37,37 @@ define('sounds', ['buzz'], function(buzz) {
|
||||
}
|
||||
|
||||
function loadFile(fileName, callback) {
|
||||
function createSound() {
|
||||
if (files && files[fileName]) {
|
||||
loadedSounds[fileName] = new buzz.sound(files[fileName]);
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
if (isSoundLoaded(fileName)) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (files && files[fileName]) {
|
||||
loadedSounds[fileName] = new buzz.sound(files[fileName]);
|
||||
if (!files || !files[fileName]) {
|
||||
return loadData(createSound);
|
||||
}
|
||||
callback();
|
||||
createSound();
|
||||
}
|
||||
|
||||
Sounds.play = function(name) {
|
||||
function play() {
|
||||
Sounds.playFile(eventSoundMapping[name]);
|
||||
}
|
||||
|
||||
if (!config.notificationSounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sounds.playFile(eventSoundMapping[name]);
|
||||
if (!eventSoundMapping) {
|
||||
return loadData(play);
|
||||
}
|
||||
|
||||
play();
|
||||
};
|
||||
|
||||
Sounds.playFile = function(fileName) {
|
||||
|
||||
@@ -381,6 +381,12 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof String.prototype.rtrim != 'function') {
|
||||
String.prototype.rtrim = function() {
|
||||
return this.replace(/\s+$/g, '');
|
||||
};
|
||||
}
|
||||
|
||||
if ('undefined' !== typeof window) {
|
||||
window.utils = module.exports;
|
||||
}
|
||||
|
||||
20
public/vendor/jquery/timeago/locales/jquery.timeago.it-short.js
vendored
Normal file
20
public/vendor/jquery/timeago/locales/jquery.timeago.it-short.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Italian shortened
|
||||
jQuery.timeago.settings.strings = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: "",
|
||||
suffixFromNow: "",
|
||||
seconds: "1m",
|
||||
minute: "1m",
|
||||
minutes: "%dm",
|
||||
hour: "1h",
|
||||
hours: "%dh",
|
||||
day: "1g",
|
||||
days: "%dg",
|
||||
month: "1me",
|
||||
months: "%dme",
|
||||
year: "1a",
|
||||
years: "%da",
|
||||
wordSeparator: " ",
|
||||
numbers: []
|
||||
};
|
||||
@@ -53,17 +53,11 @@ module.exports = function(Categories) {
|
||||
}
|
||||
|
||||
if (category.description) {
|
||||
plugins.fireHook('filter:parse.raw', category.description, function(err, parsedDescription) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
category.descriptionParsed = parsedDescription;
|
||||
category.description = validator.escape(category.description);
|
||||
callback(null, category);
|
||||
});
|
||||
} else {
|
||||
callback(null, category);
|
||||
category.description = validator.escape(category.description);
|
||||
category.descriptionParsed = category.descriptionParsed || category.description;
|
||||
}
|
||||
|
||||
callback(null, category);
|
||||
}
|
||||
|
||||
Categories.getCategoryField = function(cid, field, callback) {
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
var async = require('async'),
|
||||
winston = require('winston'),
|
||||
validator = require('validator'),
|
||||
_ = require('underscore'),
|
||||
|
||||
meta = require('../meta'),
|
||||
db = require('../database'),
|
||||
posts = require('../posts'),
|
||||
topics = require('../topics'),
|
||||
privileges = require('../privileges'),
|
||||
plugins = require('../plugins');
|
||||
privileges = require('../privileges');
|
||||
|
||||
module.exports = function(Categories) {
|
||||
Categories.getRecentReplies = function(cid, uid, count, callback) {
|
||||
@@ -38,25 +37,21 @@ module.exports = function(Categories) {
|
||||
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
async.map(categoryData, getRecentTopicPids, next);
|
||||
async.map(categoryData, getRecentTopicTids, next);
|
||||
},
|
||||
function(results, next) {
|
||||
var pids = _.flatten(results);
|
||||
var tids = _.flatten(results);
|
||||
|
||||
pids = pids.filter(function(pid, index, array) {
|
||||
return !!pid && array.indexOf(pid) === index;
|
||||
tids = tids.filter(function(tid, index, array) {
|
||||
return !!tid && array.indexOf(tid) === index;
|
||||
});
|
||||
privileges.posts.filter('read', pids, uid, next);
|
||||
privileges.topics.filterTids('read', tids, uid, next);
|
||||
},
|
||||
function(pids, next) {
|
||||
if (meta.config.teaserPost === 'first') {
|
||||
getMainPosts(pids, uid, next);
|
||||
} else {
|
||||
posts.getPostSummaryByPids(pids, uid, {stripTags: true}, next);
|
||||
}
|
||||
function(tids, next) {
|
||||
getTopics(tids, next);
|
||||
},
|
||||
function(posts, next) {
|
||||
assignPostsToCategories(categoryData, posts);
|
||||
function(topics, next) {
|
||||
assignTopicsToCategories(categoryData, topics);
|
||||
|
||||
bubbleUpChildrenPosts(categoryData);
|
||||
|
||||
@@ -65,29 +60,86 @@ module.exports = function(Categories) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
function getMainPosts(pids, uid, callback) {
|
||||
function getRecentTopicTids(category, callback) {
|
||||
var count = parseInt(category.numRecentReplies, 10);
|
||||
if (!count) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
if (count === 1) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange('cid:' + category.cid + ':pids', 0, 0, next);
|
||||
},
|
||||
function (pid, next) {
|
||||
posts.getPostField(pid, 'tid', next);
|
||||
},
|
||||
function (tid, next) {
|
||||
next(null, [tid]);
|
||||
}
|
||||
], callback);
|
||||
return;
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
pinnedTids: function(next) {
|
||||
db.getSortedSetRevRangeByScore('cid:' + category.cid + ':tids', 0, -1, '+inf', Date.now(), next);
|
||||
},
|
||||
tids: function(next) {
|
||||
db.getSortedSetRevRangeByScore('cid:' + category.cid + ':tids', 0, Math.max(1, count), Date.now(), 0, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
results.tids = results.tids.concat(results.pinnedTids);
|
||||
|
||||
callback(null, results.tids);
|
||||
});
|
||||
}
|
||||
|
||||
function getTopics(tids, callback) {
|
||||
var topicData;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
var keys = pids.map(function(pid) {
|
||||
return 'post:' + pid;
|
||||
});
|
||||
db.getObjectsFields(keys, ['tid'], next);
|
||||
function (next) {
|
||||
topics.getTopicsFields(tids, ['tid', 'mainPid', 'slug', 'title', 'teaserPid', 'cid', 'postcount'], next);
|
||||
},
|
||||
function(posts, next) {
|
||||
var keys = posts.map(function(post) {
|
||||
return 'topic:' + post.tid;
|
||||
function (_topicData, next) {
|
||||
topicData = _topicData;
|
||||
topicData.forEach(function(topic) {
|
||||
topic.teaserPid = topic.teaserPid || topic.mainPid;
|
||||
});
|
||||
db.getObjectsFields(keys, ['mainPid'], next);
|
||||
|
||||
topics.getTeasers(topicData, next);
|
||||
},
|
||||
function(topics, next) {
|
||||
var mainPids = topics.map(function(topic) {
|
||||
return topic.mainPid;
|
||||
function (teasers, next) {
|
||||
teasers.forEach(function(teaser, index) {
|
||||
if (teaser) {
|
||||
teaser.cid = topicData[index].cid;
|
||||
teaser.tid = teaser.uid = teaser.user.uid = undefined;
|
||||
teaser.topic = {
|
||||
slug: topicData[index].slug,
|
||||
title: validator.escape(topicData[index].title)
|
||||
};
|
||||
}
|
||||
});
|
||||
posts.getPostSummaryByPids(mainPids, uid, {stripTags: true}, next);
|
||||
teasers = teasers.filter(Boolean);
|
||||
next(null, teasers);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
function assignTopicsToCategories(categories, topics) {
|
||||
categories.forEach(function(category) {
|
||||
category.posts = topics.filter(function(topic) {
|
||||
return topic.cid && parseInt(topic.cid, 10) === parseInt(category.cid, 10);
|
||||
}).sort(function(a, b) {
|
||||
return b.pid - a.pid;
|
||||
}).slice(0, parseInt(category.numRecentReplies, 10));
|
||||
});
|
||||
}
|
||||
|
||||
function bubbleUpChildrenPosts(categoryData) {
|
||||
categoryData.forEach(function(category) {
|
||||
if (category.posts.length) {
|
||||
@@ -97,7 +149,7 @@ module.exports = function(Categories) {
|
||||
getPostsRecursive(category, posts);
|
||||
|
||||
posts.sort(function(a, b) {
|
||||
return b.timestamp - a.timestamp;
|
||||
return b.pid - a.pid;
|
||||
});
|
||||
if (posts.length) {
|
||||
category.posts = [posts[0]];
|
||||
@@ -115,64 +167,6 @@ module.exports = function(Categories) {
|
||||
});
|
||||
}
|
||||
|
||||
function assignPostsToCategories(categories, posts) {
|
||||
categories.forEach(function(category) {
|
||||
category.posts = posts.filter(function(post) {
|
||||
return post.category && (parseInt(post.category.cid, 10) === parseInt(category.cid, 10) ||
|
||||
parseInt(post.category.parentCid, 10) === parseInt(category.cid, 10));
|
||||
}).sort(function(a, b) {
|
||||
return b.timestamp - a.timestamp;
|
||||
}).slice(0, parseInt(category.numRecentReplies, 10));
|
||||
});
|
||||
}
|
||||
|
||||
function getRecentTopicPids(category, callback) {
|
||||
var count = parseInt(category.numRecentReplies, 10);
|
||||
if (!count) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
db.getSortedSetRevRange('cid:' + category.cid + ':pids', 0, 0, function(err, pids) {
|
||||
if (err || !Array.isArray(pids) || !pids.length) {
|
||||
return callback(err, []);
|
||||
}
|
||||
|
||||
if (count === 1) {
|
||||
return callback(null, pids);
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
pinnedTids: function(next) {
|
||||
db.getSortedSetRevRangeByScore('cid:' + category.cid + ':tids', 0, -1, '+inf', Date.now(), next);
|
||||
},
|
||||
tids: function(next) {
|
||||
db.getSortedSetRevRangeByScore('cid:' + category.cid + ':tids', 0, Math.max(0, count), Date.now(), 0, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
results.tids = results.tids.concat(results.pinnedTids);
|
||||
|
||||
async.map(results.tids, topics.getLatestUndeletedPid, function(err, topicPids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
pids = pids.concat(topicPids).filter(function(pid, index, array) {
|
||||
return !!pid && array.indexOf(pid) === index;
|
||||
}).sort(function(a, b) {
|
||||
return b - a;
|
||||
}).slice(0, count);
|
||||
|
||||
callback(null, pids);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Categories.moveRecentReplies = function(tid, oldCid, cid) {
|
||||
updatePostCount(tid, oldCid, cid);
|
||||
topics.getPids(tid, function(err, pids) {
|
||||
|
||||
@@ -68,6 +68,8 @@ module.exports = function(Categories) {
|
||||
|
||||
if (key === 'order') {
|
||||
updateOrder(cid, value, callback);
|
||||
} else if (key === 'description') {
|
||||
parseDescription(cid, value, callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
@@ -119,4 +121,13 @@ module.exports = function(Categories) {
|
||||
});
|
||||
}
|
||||
|
||||
function parseDescription(cid, description, callback) {
|
||||
plugins.fireHook('filter:parse.raw', description, function(err, parsedDescription) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
Categories.setCategoryField(cid, 'descriptionParsed', parsedDescription, callback);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ chatsController.get = function(req, res, callback) {
|
||||
// In case a userNAME is passed in instead of a slug, the route should not 404
|
||||
var slugified = utils.slugify(req.params.userslug);
|
||||
if (req.params.userslug && req.params.userslug !== slugified) {
|
||||
return res.redirect(nconf.get('relative_path') + '/chats/' + slugified);
|
||||
return helpers.redirect(res, '/chats/' + slugified);
|
||||
}
|
||||
|
||||
async.parallel({
|
||||
|
||||
@@ -14,6 +14,48 @@ var async = require('async'),
|
||||
var editController = {};
|
||||
|
||||
editController.get = function(req, res, callback) {
|
||||
accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, function(err, userData) {
|
||||
if (err || !userData) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
|
||||
userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]);
|
||||
|
||||
res.render('account/edit', userData);
|
||||
});
|
||||
};
|
||||
|
||||
editController.password = function(req, res, next) {
|
||||
renderRoute('password', req, res, next);
|
||||
};
|
||||
|
||||
editController.username = function(req, res, next) {
|
||||
renderRoute('username', req, res, next);
|
||||
};
|
||||
|
||||
editController.email = function(req, res, next) {
|
||||
renderRoute('email', req, res, next);
|
||||
};
|
||||
|
||||
function renderRoute(name, req, res, next) {
|
||||
getUserData(req, next, function(err, userData) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';
|
||||
userData.breadcrumbs = helpers.buildBreadcrumbs([
|
||||
{text: userData.username, url: '/user/' + userData.userslug},
|
||||
{text: '[[user:edit]]', url: '/user/' + userData.userslug + '/edit'},
|
||||
{text: '[[user:' + name + ']]'}
|
||||
]);
|
||||
|
||||
res.render('account/edit/' + name, userData);
|
||||
});
|
||||
}
|
||||
|
||||
function getUserData(req, next, callback) {
|
||||
var userData;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
@@ -22,7 +64,7 @@ editController.get = function(req, res, callback) {
|
||||
function(data, next) {
|
||||
userData = data;
|
||||
if (!userData) {
|
||||
return callback();
|
||||
return next();
|
||||
}
|
||||
db.getObjectField('user:' + userData.uid, 'password', next);
|
||||
}
|
||||
@@ -33,13 +75,9 @@ editController.get = function(req, res, callback) {
|
||||
|
||||
userData['username:disableEdit'] = parseInt(meta.config['username:disableEdit'], 10) === 1;
|
||||
userData.hasPassword = !!password;
|
||||
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
|
||||
userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]);
|
||||
|
||||
res.render('account/edit', userData);
|
||||
callback(null, userData);
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
editController.uploadPicture = function (req, res, next) {
|
||||
var userPhoto = req.files.files[0];
|
||||
|
||||
@@ -7,12 +7,13 @@ var user = require('../../user'),
|
||||
var notificationsController = {};
|
||||
|
||||
notificationsController.get = function(req, res, next) {
|
||||
user.notifications.getAll(req.uid, 40, function(err, notifications) {
|
||||
user.notifications.getAll(req.uid, 0, 39, function(err, notifications) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.render('notifications', {
|
||||
notifications: notifications,
|
||||
nextStart: 40,
|
||||
title: '[[pages:notifications]]',
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:notifications]]'}])
|
||||
});
|
||||
|
||||
@@ -24,7 +24,8 @@ var adminController = {
|
||||
navigation: require('./admin/navigation'),
|
||||
themes: require('./admin/themes'),
|
||||
users: require('./admin/users'),
|
||||
uploads: require('./admin/uploads')
|
||||
uploads: require('./admin/uploads'),
|
||||
info: require('./admin/info')
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -59,12 +59,13 @@ groupsController.get = function(req, res, callback) {
|
||||
if (!exists) {
|
||||
return callback();
|
||||
}
|
||||
groups.get(groupName, {uid: req.uid}, next);
|
||||
groups.get(groupName, {uid: req.uid, truncateUserList: true, userListCount: 20}, next);
|
||||
}
|
||||
], function(err, group) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
group.isOwner = true;
|
||||
res.render('admin/manage/group', {group: group});
|
||||
});
|
||||
};
|
||||
|
||||
29
src/controllers/admin/info.js
Normal file
29
src/controllers/admin/info.js
Normal file
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var os = require('os');
|
||||
|
||||
var infoController = {};
|
||||
|
||||
infoController.get = function(req, res, next) {
|
||||
|
||||
var data = {
|
||||
process: {
|
||||
pid: process.pid,
|
||||
title: process.title,
|
||||
arch: process.arch,
|
||||
platform: process.platform,
|
||||
version: process.version,
|
||||
versions: process.versions,
|
||||
memoryUsage: process.memoryUsage(),
|
||||
uptime: process.uptime()
|
||||
},
|
||||
os: {
|
||||
hostname: os.hostname()
|
||||
}
|
||||
};
|
||||
|
||||
res.render('admin/development/info', {info: JSON.stringify(data, null, 4)});
|
||||
};
|
||||
|
||||
|
||||
module.exports = infoController;
|
||||
@@ -114,7 +114,7 @@ function validateUpload(req, res, next, uploadedFile, allowedTypes) {
|
||||
}
|
||||
});
|
||||
|
||||
res.json({error: '[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]'});
|
||||
res.json({error: '[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]'});
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ apiController.getConfig = function(req, res, next) {
|
||||
config.categoryTopicSort = meta.config.categoryTopicSort || 'newest_to_oldest';
|
||||
config.csrf_token = req.csrfToken();
|
||||
config.searchEnabled = plugins.hasListeners('filter:search.query');
|
||||
config.bootswatchSkin = 'default';
|
||||
|
||||
if (!req.user) {
|
||||
return filterConfig();
|
||||
@@ -103,6 +104,7 @@ apiController.getConfig = function(req, res, next) {
|
||||
config.topicPostSort = settings.topicPostSort || config.topicPostSort;
|
||||
config.categoryTopicSort = settings.categoryTopicSort || config.categoryTopicSort;
|
||||
config.topicSearchEnabled = settings.topicSearchEnabled || false;
|
||||
config.bootswatchSkin = settings.bootswatchSkin || config.bootswatchSkin;
|
||||
|
||||
filterConfig();
|
||||
});
|
||||
|
||||
@@ -65,6 +65,15 @@ categoriesController.list = function(req, res, next) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
data.categories.forEach(function(category) {
|
||||
if (category && Array.isArray(category.posts) && category.posts.length) {
|
||||
category.teaser = {
|
||||
url: nconf.get('relative_path') + '/topic/' + category.posts[0].topic.slug + '/' + category.posts[0].index,
|
||||
timestampISO: category.posts[0].timestamp
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
data.title = '[[pages:categories]]';
|
||||
if (req.path.startsWith('/api/categories') || req.path.startsWith('/categories')) {
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{text: data.title}]);
|
||||
@@ -81,7 +90,7 @@ categoriesController.list = function(req, res, next) {
|
||||
|
||||
categoriesController.get = function(req, res, callback) {
|
||||
var cid = req.params.category_id,
|
||||
page = parseInt(req.query.page, 10) || 1,
|
||||
currentPage = parseInt(req.query.page, 10) || 1,
|
||||
pageCount = 1,
|
||||
userPrivileges;
|
||||
|
||||
@@ -127,7 +136,7 @@ categoriesController.get = function(req, res, callback) {
|
||||
return helpers.redirect(res, '/category/' + cid + '/' + req.params.slug + (topicIndex > topicCount ? '/' + topicCount : ''));
|
||||
}
|
||||
|
||||
if (settings.usePagination && (page < 1 || page > pageCount)) {
|
||||
if (settings.usePagination && (currentPage < 1 || currentPage > pageCount)) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
@@ -135,7 +144,7 @@ categoriesController.get = function(req, res, callback) {
|
||||
topicIndex = Math.max(topicIndex - (settings.topicsPerPage - 1), 0);
|
||||
} else if (!req.query.page) {
|
||||
var index = Math.max(parseInt((topicIndex || 0), 10), 0);
|
||||
page = Math.ceil((index + 1) / settings.topicsPerPage);
|
||||
currentPage = Math.ceil((index + 1) / settings.topicsPerPage);
|
||||
topicIndex = 0;
|
||||
}
|
||||
|
||||
@@ -149,7 +158,7 @@ categoriesController.get = function(req, res, callback) {
|
||||
set = 'cid:' + cid + ':tids:posts';
|
||||
}
|
||||
|
||||
var start = (page - 1) * settings.topicsPerPage + topicIndex,
|
||||
var start = (currentPage - 1) * settings.topicsPerPage + topicIndex,
|
||||
stop = start + settings.topicsPerPage - 1;
|
||||
|
||||
next(null, {
|
||||
@@ -194,8 +203,11 @@ categoriesController.get = function(req, res, callback) {
|
||||
});
|
||||
},
|
||||
function(categoryData, next) {
|
||||
if (!categoryData.children.length) {
|
||||
return next(null, categoryData);
|
||||
}
|
||||
var allCategories = [];
|
||||
categories.flattenCategories(allCategories, [categoryData]);
|
||||
categories.flattenCategories(allCategories, categoryData.children);
|
||||
categories.getRecentTopicReplies(allCategories, req.uid, function(err) {
|
||||
next(err, categoryData);
|
||||
});
|
||||
@@ -249,12 +261,10 @@ categoriesController.get = function(req, res, callback) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
data.currentPage = page;
|
||||
data.pageCount = pageCount;
|
||||
data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
|
||||
data.rssFeedUrl = nconf.get('relative_path') + '/category/' + data.cid + '.rss';
|
||||
data.title = data.name;
|
||||
data.pagination = pagination.create(data.currentPage, data.pageCount);
|
||||
data.pagination = pagination.create(currentPage, pageCount);
|
||||
data.pagination.rel.forEach(function(rel) {
|
||||
rel.href = nconf.get('url') + '/category/' + data.slug + rel.href;
|
||||
res.locals.linkTags.push(rel);
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
validator = require('validator'),
|
||||
|
||||
meta = require('../meta'),
|
||||
plugins = require('../plugins'),
|
||||
search = require('../search'),
|
||||
categories = require('../categories'),
|
||||
@@ -17,6 +18,10 @@ searchController.search = function(req, res, next) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!req.user && parseInt(meta.config.allowGuestSearching, 10) !== 1) {
|
||||
return helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
var page = Math.max(1, parseInt(req.query.page, 10)) || 1;
|
||||
if (req.query.categories && !Array.isArray(req.query.categories)) {
|
||||
req.query.categories = [req.query.categories];
|
||||
@@ -51,6 +56,7 @@ searchController.search = function(req, res, next) {
|
||||
searchData.pagination = pagination.create(page, searchData.pageCount, req.query);
|
||||
searchData.showAsPosts = !req.query.showAs || req.query.showAs === 'posts';
|
||||
searchData.showAsTopics = req.query.showAs === 'topics';
|
||||
searchData.title = '[[global:header.search]]';
|
||||
searchData.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]);
|
||||
searchData.expandSearch = !req.params.term;
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ var topicsController = {},
|
||||
topicsController.get = function(req, res, callback) {
|
||||
var tid = req.params.topic_id,
|
||||
sort = req.query.sort,
|
||||
currentPage = parseInt(req.query.page, 10) || 1,
|
||||
pageCount = 1,
|
||||
userPrivileges;
|
||||
|
||||
if ((req.params.post_index && !utils.isNumber(req.params.post_index)) || !utils.isNumber(tid)) {
|
||||
@@ -56,14 +58,13 @@ topicsController.get = function(req, res, callback) {
|
||||
|
||||
var settings = results.settings;
|
||||
var postCount = parseInt(results.topic.postcount, 10);
|
||||
var pageCount = Math.max(1, Math.ceil((postCount - 1) / settings.postsPerPage));
|
||||
var page = parseInt(req.query.page, 10) || 1;
|
||||
pageCount = Math.max(1, Math.ceil((postCount - 1) / settings.postsPerPage));
|
||||
|
||||
if (utils.isNumber(req.params.post_index) && (req.params.post_index < 1 || req.params.post_index > postCount)) {
|
||||
return helpers.redirect(res, '/topic/' + req.params.topic_id + '/' + req.params.slug + (req.params.post_index > postCount ? '/' + postCount : ''));
|
||||
}
|
||||
|
||||
if (settings.usePagination && (page < 1 || page > pageCount)) {
|
||||
if (settings.usePagination && (currentPage < 1 || currentPage > pageCount)) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
@@ -105,10 +106,10 @@ topicsController.get = function(req, res, callback) {
|
||||
index = Math.max(0, req.params.post_index - 1) || 0;
|
||||
}
|
||||
|
||||
page = Math.max(1, Math.ceil(index / settings.postsPerPage));
|
||||
currentPage = Math.max(1, Math.ceil(index / settings.postsPerPage));
|
||||
}
|
||||
|
||||
var start = (page - 1) * settings.postsPerPage + postIndex,
|
||||
var start = (currentPage - 1) * settings.postsPerPage + postIndex,
|
||||
stop = start + settings.postsPerPage - 1;
|
||||
|
||||
topics.getTopicWithPosts(tid, set, req.uid, start, stop, reverse, function (err, topicData) {
|
||||
@@ -120,9 +121,6 @@ topicsController.get = function(req, res, callback) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
topicData.pageCount = pageCount;
|
||||
topicData.currentPage = page;
|
||||
|
||||
topics.modifyByPrivilege(topicData.posts, results.privileges);
|
||||
|
||||
plugins.fireHook('filter:controllers.topic.get', topicData, next);
|
||||
@@ -238,7 +236,7 @@ topicsController.get = function(req, res, callback) {
|
||||
},
|
||||
{
|
||||
rel: 'canonical',
|
||||
href: nconf.get('url') + '/topic/' + topicData.slug
|
||||
href: nconf.get('url') + '/topic/' + topicData.slug + (currentPage > 1 ? '?page=' + currentPage : '')
|
||||
}
|
||||
];
|
||||
|
||||
@@ -261,7 +259,7 @@ topicsController.get = function(req, res, callback) {
|
||||
data['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1;
|
||||
data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
|
||||
data.rssFeedUrl = nconf.get('relative_path') + '/topic/' + data.tid + '.rss';
|
||||
data.pagination = pagination.create(data.currentPage, data.pageCount);
|
||||
data.pagination = pagination.create(currentPage, pageCount);
|
||||
data.pagination.rel.forEach(function(rel) {
|
||||
rel.href = nconf.get('url') + '/topic/' + data.slug + rel.href;
|
||||
res.locals.linkTags.push(rel);
|
||||
|
||||
@@ -58,6 +58,9 @@ usersController.getUsersSortedByPosts = function(req, res, next) {
|
||||
};
|
||||
|
||||
usersController.getUsersSortedByReputation = function(req, res, next) {
|
||||
if (parseInt(meta.config['reputation:disabled'], 10) === 1) {
|
||||
return next();
|
||||
}
|
||||
usersController.getUsers('users:reputation', 0, 49, req, res, next);
|
||||
};
|
||||
|
||||
@@ -217,6 +220,7 @@ usersController.getMap = function(req, res, next) {
|
||||
|
||||
res.render('usersMap', {
|
||||
rooms: data,
|
||||
'reputation:disabled': parseInt(meta.config['reputation:disabled'], 10) === 1,
|
||||
title: '[[pages:users/map]]',
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{text: '[[global:users]]', url: '/users'}, {text: '[[global:map]]'}])
|
||||
});
|
||||
@@ -230,6 +234,7 @@ function render(req, res, data, next) {
|
||||
}
|
||||
|
||||
data.templateData.inviteOnly = meta.config.registrationType === 'invite-only';
|
||||
data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
|
||||
res.render('users', data.templateData);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -178,25 +178,55 @@
|
||||
|
||||
module.info = function(db, callback) {
|
||||
async.parallel({
|
||||
serverStats: function(next) {
|
||||
serverStatus: function(next) {
|
||||
db.command({'serverStatus': 1}, next);
|
||||
},
|
||||
stats: function(next) {
|
||||
db.stats({scale:1024}, next);
|
||||
db.command({'dbStats': 1}, next);
|
||||
},
|
||||
listCollections: function(next) {
|
||||
db.listCollections().toArray(function(err, items) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
async.map(items, function(collection, next) {
|
||||
db.collection(collection.name).stats(next);
|
||||
}, next);
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var stats = results.stats;
|
||||
var scale = 1024 * 1024;
|
||||
|
||||
results.listCollections = results.listCollections.map(function(collectionInfo) {
|
||||
return {
|
||||
name: collectionInfo.ns,
|
||||
count: collectionInfo.count,
|
||||
size: collectionInfo.size,
|
||||
avgObjSize: collectionInfo.avgObjSize,
|
||||
storageSize: collectionInfo.storageSize,
|
||||
totalIndexSize: collectionInfo.totalIndexSize,
|
||||
indexSizes: collectionInfo.indexSizes
|
||||
};
|
||||
});
|
||||
|
||||
stats.mem = results.serverStatus.mem;
|
||||
stats.collectionData = results.listCollections;
|
||||
stats.network = results.serverStatus.network;
|
||||
stats.raw = JSON.stringify(stats, null, 4);
|
||||
|
||||
stats.avgObjSize = (stats.avgObjSize / 1024).toFixed(2);
|
||||
stats.dataSize = (stats.dataSize / 1024).toFixed(2);
|
||||
stats.storageSize = (stats.storageSize / 1024).toFixed(2);
|
||||
stats.fileSize = (stats.fileSize / 1024).toFixed(2);
|
||||
stats.indexSize = (stats.indexSize / 1024).toFixed(2);
|
||||
stats.mem = results.serverStats.mem;
|
||||
stats.raw = JSON.stringify(stats, null, 4);
|
||||
stats.dataSize = (stats.dataSize / scale).toFixed(2);
|
||||
stats.storageSize = (stats.storageSize / scale).toFixed(2);
|
||||
stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0;
|
||||
stats.indexSize = (stats.indexSize / scale).toFixed(2);
|
||||
stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1';
|
||||
stats.host = results.serverStatus.host;
|
||||
stats.version = results.serverStatus.version;
|
||||
stats.uptime = results.serverStatus.uptime;
|
||||
stats.mongo = true;
|
||||
|
||||
callback(null, stats);
|
||||
|
||||
@@ -6,6 +6,7 @@ helpers.toMap = function(data) {
|
||||
var map = {};
|
||||
for (var i = 0; i<data.length; ++i) {
|
||||
map[data[i]._key] = data[i];
|
||||
data[i]._key = undefined;
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ module.exports = function(db, module) {
|
||||
}
|
||||
}
|
||||
|
||||
db.collection('search' + key).find(searchQuery, {limit: limit}).toArray(function(err, results) {
|
||||
db.collection('search' + key).find(searchQuery, {limit: limit, fields:{_id: 0, id: 1}}).toArray(function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ module.exports = function(db, module) {
|
||||
if (withScores) {
|
||||
fields.score = 1;
|
||||
}
|
||||
db.collection('objects').find({_key:key}, {fields: fields})
|
||||
db.collection('objects').find({_key: key}, {fields: fields})
|
||||
.limit(stop - start + 1)
|
||||
.skip(start)
|
||||
.sort({score: sort})
|
||||
|
||||
@@ -26,13 +26,13 @@ var async = require('async'),
|
||||
|
||||
var now = Date.now();
|
||||
|
||||
if(type === 'upvote' && !unvote) {
|
||||
if (type === 'upvote' && !unvote) {
|
||||
db.sortedSetAdd('uid:' + uid + ':upvote', now, pid);
|
||||
} else {
|
||||
db.sortedSetRemove('uid:' + uid + ':upvote', pid);
|
||||
}
|
||||
|
||||
if(type === 'upvote' || unvote) {
|
||||
if (type === 'upvote' || unvote) {
|
||||
db.sortedSetRemove('uid:' + uid + ':downvote', pid);
|
||||
} else {
|
||||
db.sortedSetAdd('uid:' + uid + ':downvote', now, pid);
|
||||
@@ -213,7 +213,7 @@ var async = require('async'),
|
||||
|
||||
if (voteStatus.upvoted && command === 'downvote' || voteStatus.downvoted && command === 'upvote') { // e.g. User *has* upvoted, and clicks downvote
|
||||
hook = command;
|
||||
} else if (voteStatus.upvoted || voteStatus.downvoted) { // e.g. User *has* upvotes, clicks upvote (so we "unvote")
|
||||
} else if (voteStatus.upvoted || voteStatus.downvoted) { // e.g. User *has* upvoted, clicks upvote (so we "unvote")
|
||||
hook = 'unvote';
|
||||
} else { // e.g. User *has not* voted, clicks upvote
|
||||
hook = command;
|
||||
|
||||
@@ -54,7 +54,7 @@ file.isFileTypeAllowed = function(path, allowedExtensions, callback) {
|
||||
var uploadedFileExtension = mime.extension(mimeType);
|
||||
|
||||
if (allowedExtensions.indexOf(uploadedFileExtension) === -1) {
|
||||
return callback(new Error('[[error:invalid-file-type, ' + allowedExtensions.join(', ') + ']]'));
|
||||
return callback(new Error('[[error:invalid-file-type, ' + allowedExtensions.join(', ') + ']]'));
|
||||
}
|
||||
|
||||
callback();
|
||||
|
||||
@@ -13,7 +13,7 @@ var async = require('async'),
|
||||
"dependencies": ["redis@~0.10.1", "connect-redis@~2.0.0"]
|
||||
},
|
||||
"mongo": {
|
||||
"dependencies": ["mongodb@~2.0.0", "connect-mongo"]
|
||||
"dependencies": ["mongodb@~2.0.0", "connect-mongo@~0.8.2"]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@ var db = require('./database'),
|
||||
touid = params.touid,
|
||||
since = params.since,
|
||||
isNew = params.isNew,
|
||||
count = params.count || parseInt(meta.config.chatMessageInboxSize, 10) || 250;
|
||||
count = params.count || parseInt(meta.config.chatMessageInboxSize, 10) || 250,
|
||||
markRead = params.markRead || true;
|
||||
|
||||
var uids = sortUids(fromuid, touid),
|
||||
min = params.count ? 0 : Date.now() - (terms[since] || terms.day);
|
||||
@@ -135,13 +136,15 @@ var db = require('./database'),
|
||||
getMessages(mids, fromuid, touid, isNew, callback);
|
||||
});
|
||||
|
||||
notifications.markRead('chat_' + touid + '_' + fromuid, fromuid, function(err) {
|
||||
if (err) {
|
||||
winston.error('[messaging] Could not mark notifications related to this chat as read: ' + err.message);
|
||||
}
|
||||
if (markRead) {
|
||||
notifications.markRead('chat_' + touid + '_' + fromuid, fromuid, function(err) {
|
||||
if (err) {
|
||||
winston.error('[messaging] Could not mark notifications related to this chat as read: ' + err.message);
|
||||
}
|
||||
|
||||
userNotifications.pushCount(fromuid);
|
||||
});
|
||||
userNotifications.pushCount(fromuid);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function getMessages(mids, fromuid, touid, isNew, callback) {
|
||||
@@ -271,7 +274,8 @@ var db = require('./database'),
|
||||
fromuid: fromuid,
|
||||
touid: uid,
|
||||
isNew: false,
|
||||
count: 1
|
||||
count: 1,
|
||||
markRead: false
|
||||
}, function(err, teaser) {
|
||||
var teaser = teaser[0];
|
||||
teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s;
|
||||
@@ -286,7 +290,6 @@ var db = require('./database'),
|
||||
|
||||
results.users.forEach(function(user, index) {
|
||||
if (user && parseInt(user.uid, 10)) {
|
||||
Messaging.markRead(uid, uids[index]);
|
||||
user.unread = results.unread[index];
|
||||
user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline';
|
||||
user.teaser = results.teasers[index];
|
||||
@@ -311,7 +314,7 @@ var db = require('./database'),
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
sockets.in('uid_' + uid).emit('event:unread.updateChatCount', null, unreadCount);
|
||||
sockets.in('uid_' + uid).emit('event:unread.updateChatCount', unreadCount);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ var path = require('path'),
|
||||
semver = require('semver'),
|
||||
winston = require('winston'),
|
||||
|
||||
pkg = require.main.require('./package.json');
|
||||
pkg = require('../../package.json');
|
||||
|
||||
module.exports = function(Meta) {
|
||||
Meta.dependencies = {};
|
||||
|
||||
@@ -5,7 +5,6 @@ var winston = require('winston'),
|
||||
path = require('path'),
|
||||
async = require('async'),
|
||||
_ = require('underscore'),
|
||||
os = require('os'),
|
||||
nconf = require('nconf'),
|
||||
fs = require('fs'),
|
||||
file = require('../file'),
|
||||
@@ -50,15 +49,32 @@ module.exports = function(Meta) {
|
||||
'public/src/client/chats.js',
|
||||
'public/src/client/infinitescroll.js',
|
||||
'public/src/client/pagination.js',
|
||||
'public/src/client/recent.js',
|
||||
'public/src/client/unread.js',
|
||||
'public/src/client/topic.js',
|
||||
'public/src/client/topic/browsing.js',
|
||||
'public/src/client/topic/events.js',
|
||||
'public/src/client/topic/flag.js',
|
||||
'public/src/client/topic/fork.js',
|
||||
'public/src/client/topic/move.js',
|
||||
'public/src/client/topic/posts.js',
|
||||
'public/src/client/topic/postTools.js',
|
||||
'public/src/client/topic/threadTools.js',
|
||||
'public/src/client/categories.js',
|
||||
'public/src/client/category.js',
|
||||
'public/src/client/categoryTools.js',
|
||||
|
||||
'public/src/modules/csrf.js',
|
||||
'public/src/modules/translator.js',
|
||||
'public/src/modules/notifications.js',
|
||||
'public/src/modules/chat.js',
|
||||
'public/src/modules/components.js',
|
||||
'public/src/modules/composer/formatting.js',
|
||||
'public/src/modules/composer/controls.js',
|
||||
'public/src/modules/composer/preview.js',
|
||||
'public/src/modules/categories.js',
|
||||
'public/src/modules/sort.js',
|
||||
'public/src/modules/navigator.js',
|
||||
'public/src/modules/topicSelect.js',
|
||||
'public/src/modules/share.js',
|
||||
'public/src/modules/search.js',
|
||||
'public/src/modules/alerts.js',
|
||||
'public/src/modules/taskbar.js',
|
||||
'public/src/modules/helpers.js',
|
||||
'public/src/modules/sounds.js',
|
||||
@@ -72,8 +88,7 @@ module.exports = function(Meta) {
|
||||
async.apply(getPluginScripts), // plugin scripts via filter:scripts.get
|
||||
function(next) { // client scripts via "scripts" config in plugin.json
|
||||
var pluginsScripts = [],
|
||||
pluginDirectories = [],
|
||||
clientScripts = [];
|
||||
pluginDirectories = [];
|
||||
|
||||
pluginsScripts = plugins.clientScripts.filter(function(path) {
|
||||
if (path.endsWith('.js')) {
|
||||
@@ -268,4 +283,4 @@ module.exports = function(Meta) {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,14 +20,16 @@ module.exports = function(Meta) {
|
||||
Meta.settings.set = function(hash, values, callback) {
|
||||
var key = 'settings:' + hash;
|
||||
db.setObject(key, values, function(err) {
|
||||
if (!err) {
|
||||
plugins.fireHook('action:settings.set', {
|
||||
plugin: hash,
|
||||
settings: values
|
||||
});
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback.apply(this, arguments);
|
||||
plugins.fireHook('action:settings.set', {
|
||||
plugin: hash,
|
||||
settings: values
|
||||
});
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ module.exports = function(Meta) {
|
||||
return callback(null, defaults);
|
||||
}
|
||||
|
||||
callback.apply(null, arguments);
|
||||
callback(null, sounds);
|
||||
});
|
||||
};
|
||||
};
|
||||
157
src/middleware/header.js
Normal file
157
src/middleware/header.js
Normal file
@@ -0,0 +1,157 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var nconf = require('nconf');
|
||||
|
||||
var user = require('../user');
|
||||
var meta = require('../meta');
|
||||
var plugins = require('../plugins');
|
||||
var navigation = require('../navigation');
|
||||
var translator = require('../../public/src/modules/translator');
|
||||
|
||||
var controllers = {
|
||||
api: require('../controllers/api'),
|
||||
helpers: require('../controllers/helpers')
|
||||
};
|
||||
|
||||
module.exports = function(app, middleware) {
|
||||
|
||||
middleware.buildHeader = function(req, res, next) {
|
||||
res.locals.renderHeader = true;
|
||||
res.locals.isAPI = false;
|
||||
|
||||
middleware.applyCSRF(req, res, function() {
|
||||
async.parallel({
|
||||
config: function(next) {
|
||||
controllers.api.getConfig(req, res, next);
|
||||
},
|
||||
footer: function(next) {
|
||||
app.render('footer', {loggedIn: (req.user ? parseInt(req.user.uid, 10) !== 0 : false)}, next);
|
||||
},
|
||||
plugins: function(next) {
|
||||
plugins.fireHook('filter:middleware.buildHeader', {req: req, locals: res.locals}, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.locals.config = results.config;
|
||||
|
||||
translator.translate(results.footer, results.config.defaultLang, function(parsedTemplate) {
|
||||
res.locals.footer = parsedTemplate;
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
middleware.renderHeader = function(req, res, data, callback) {
|
||||
var registrationType = meta.config.registrationType || 'normal';
|
||||
var templateValues = {
|
||||
bootswatchCSS: meta.config['theme:src'],
|
||||
title: meta.config.title || '',
|
||||
description: meta.config.description || '',
|
||||
'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
|
||||
'brand:logo': meta.config['brand:logo'] || '',
|
||||
'brand:logo:url': meta.config['brand:logo:url'] || '',
|
||||
'brand:logo:alt': meta.config['brand:logo:alt'] || '',
|
||||
'brand:logo:display': meta.config['brand:logo']?'':'hide',
|
||||
allowRegistration: registrationType === 'normal' || registrationType === 'admin-approval',
|
||||
searchEnabled: plugins.hasListeners('filter:search.query'),
|
||||
config: res.locals.config,
|
||||
relative_path: nconf.get('relative_path'),
|
||||
bodyClass: data.bodyClass
|
||||
};
|
||||
|
||||
templateValues.configJSON = JSON.stringify(res.locals.config);
|
||||
|
||||
async.parallel({
|
||||
isAdmin: function(next) {
|
||||
user.isAdministrator(req.uid, next);
|
||||
},
|
||||
user: function(next) {
|
||||
if (req.uid) {
|
||||
user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'icon:bgColor', 'icon:text', 'status', 'email:confirmed', 'banned'], next);
|
||||
} else {
|
||||
next(null, {
|
||||
username: '[[global:guest]]',
|
||||
userslug: '',
|
||||
picture: meta.config.defaultAvatar,
|
||||
status: 'offline',
|
||||
banned: false,
|
||||
uid: 0
|
||||
});
|
||||
}
|
||||
},
|
||||
navigation: async.apply(navigation.get),
|
||||
tags: async.apply(meta.tags.parse, res.locals.metaTags, res.locals.linkTags)
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (results.user && parseInt(results.user.banned, 10) === 1) {
|
||||
req.logout();
|
||||
return res.redirect('/');
|
||||
}
|
||||
|
||||
results.user.isAdmin = results.isAdmin;
|
||||
results.user.uid = parseInt(results.user.uid, 10);
|
||||
results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1;
|
||||
|
||||
if (res.locals.config.bootswatchSkin !== 'default') {
|
||||
templateValues.bootswatchCSS = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + res.locals.config.bootswatchSkin + '/bootstrap.min.css';
|
||||
}
|
||||
|
||||
templateValues.browserTitle = controllers.helpers.buildTitle(data.title);
|
||||
templateValues.navigation = results.navigation;
|
||||
templateValues.metaTags = results.tags.meta;
|
||||
templateValues.linkTags = results.tags.link;
|
||||
templateValues.isAdmin = results.user.isAdmin;
|
||||
templateValues.user = results.user;
|
||||
templateValues.userJSON = JSON.stringify(results.user);
|
||||
templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1 && meta.config.customCSS;
|
||||
templateValues.customCSS = templateValues.useCustomCSS ? (meta.config.renderedCustomCSS || '') : '';
|
||||
templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1;
|
||||
templateValues.customJS = templateValues.useCustomJS ? meta.config.customJS : '';
|
||||
templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin;
|
||||
templateValues.defaultLang = meta.config.defaultLang || 'en_GB';
|
||||
|
||||
templateValues.template = {name: res.locals.template};
|
||||
templateValues.template[res.locals.template] = true;
|
||||
|
||||
if (req.route && req.route.path === '/') {
|
||||
modifyTitle(templateValues);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:middleware.renderHeader', {templateValues: templateValues, req: req, res: res}, function(err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
app.render('header', data.templateValues, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function modifyTitle(obj) {
|
||||
var title = controllers.helpers.buildTitle('[[pages:home]]');
|
||||
obj.browserTitle = title;
|
||||
|
||||
if (obj.metaTags) {
|
||||
obj.metaTags.forEach(function(tag, i) {
|
||||
if (tag.property === 'og:title') {
|
||||
obj.metaTags[i].content = title;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,21 +7,16 @@ var app,
|
||||
async = require('async'),
|
||||
path = require('path'),
|
||||
csrf = require('csurf'),
|
||||
winston = require('winston'),
|
||||
|
||||
validator = require('validator'),
|
||||
nconf = require('nconf'),
|
||||
ensureLoggedIn = require('connect-ensure-login'),
|
||||
|
||||
plugins = require('../plugins'),
|
||||
navigation = require('../navigation'),
|
||||
meta = require('../meta'),
|
||||
translator = require('../../public/src/modules/translator'),
|
||||
user = require('../user'),
|
||||
groups = require('../groups'),
|
||||
db = require('../database'),
|
||||
categories = require('../categories'),
|
||||
topics = require('../topics'),
|
||||
messaging = require('../messaging'),
|
||||
|
||||
|
||||
analytics = require('../analytics'),
|
||||
|
||||
@@ -69,7 +64,7 @@ middleware.pageView = function(req, res, next) {
|
||||
middleware.pluginHooks = function(req, res, next) {
|
||||
async.each(plugins.loadedHooks['filter:router.page'] || [], function(hookObj, next) {
|
||||
hookObj.method(req, res, next);
|
||||
}, function(req, res) {
|
||||
}, function() {
|
||||
// If it got here, then none of the subscribed hooks did anything, or there were no hooks
|
||||
next();
|
||||
});
|
||||
@@ -87,14 +82,6 @@ middleware.redirectToAccountIfLoggedIn = function(req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
middleware.redirectToLoginIfGuest = function(req, res, next) {
|
||||
if (!req.user || parseInt(req.user.uid, 10) === 0) {
|
||||
return redirectToLogin(req, res);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
middleware.validateFiles = function(req, res, next) {
|
||||
if (!Array.isArray(req.files.files) || !req.files.files.length) {
|
||||
return next(new Error(['[[error:invalid-files]]']));
|
||||
@@ -108,14 +95,6 @@ middleware.prepareAPI = function(req, res, next) {
|
||||
next();
|
||||
};
|
||||
|
||||
middleware.guestSearchingAllowed = function(req, res, next) {
|
||||
if (!req.user && parseInt(meta.config.allowGuestSearching, 10) !== 1) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
middleware.checkGlobalPrivacySettings = function(req, res, next) {
|
||||
if (!req.user && !!parseInt(meta.config.privateUserInfo, 10)) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
@@ -149,11 +128,11 @@ middleware.checkAccountPermissions = function(req, res, next) {
|
||||
};
|
||||
|
||||
middleware.isAdmin = function(req, res, next) {
|
||||
if (!req.user) {
|
||||
return redirectToLogin(req, res);
|
||||
if (!req.uid) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
user.isAdministrator((req.user && req.user.uid) ? req.user.uid : 0, function (err, isAdmin) {
|
||||
user.isAdministrator(req.uid, function (err, isAdmin) {
|
||||
if (err || isAdmin) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -168,219 +147,6 @@ middleware.isAdmin = function(req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
middleware.buildHeader = function(req, res, next) {
|
||||
res.locals.renderHeader = true;
|
||||
res.locals.isAPI = false;
|
||||
|
||||
middleware.applyCSRF(req, res, function() {
|
||||
async.parallel({
|
||||
config: function(next) {
|
||||
controllers.api.getConfig(req, res, next);
|
||||
},
|
||||
footer: function(next) {
|
||||
app.render('footer', {loggedIn: (req.user ? parseInt(req.user.uid, 10) !== 0 : false)}, next);
|
||||
},
|
||||
plugins: function(next) {
|
||||
plugins.fireHook('filter:middleware.buildHeader', {req: req, locals: res.locals}, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.locals.config = results.config;
|
||||
|
||||
translator.translate(results.footer, results.config.defaultLang, function(parsedTemplate) {
|
||||
res.locals.footer = parsedTemplate;
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
middleware.renderHeader = function(req, res, data, callback) {
|
||||
var registrationType = meta.config.registrationType || 'normal';
|
||||
var templateValues = {
|
||||
bootswatchCSS: meta.config['theme:src'],
|
||||
title: meta.config.title || '',
|
||||
description: meta.config.description || '',
|
||||
'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
|
||||
'brand:logo': meta.config['brand:logo'] || '',
|
||||
'brand:logo:url': meta.config['brand:logo:url'] || '',
|
||||
'brand:logo:alt': meta.config['brand:logo:alt'] || '',
|
||||
'brand:logo:display': meta.config['brand:logo']?'':'hide',
|
||||
allowRegistration: registrationType === 'normal' || registrationType === 'admin-approval',
|
||||
searchEnabled: plugins.hasListeners('filter:search.query'),
|
||||
config: res.locals.config,
|
||||
relative_path: nconf.get('relative_path')
|
||||
};
|
||||
|
||||
templateValues.configJSON = JSON.stringify(res.locals.config);
|
||||
|
||||
async.parallel({
|
||||
customCSS: function(next) {
|
||||
templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1;
|
||||
if (!templateValues.useCustomCSS || !meta.config.customCSS || !meta.config.renderedCustomCSS) {
|
||||
return next(null, '');
|
||||
}
|
||||
next(null, meta.config.renderedCustomCSS);
|
||||
},
|
||||
customJS: function(next) {
|
||||
templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1;
|
||||
next(null, templateValues.useCustomJS ? meta.config.customJS : '');
|
||||
},
|
||||
settings: function(next) {
|
||||
if (req.uid) {
|
||||
user.getSettings(req.uid, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
title: function(next) {
|
||||
next(null, controllers.helpers.buildTitle(data.title));
|
||||
},
|
||||
isAdmin: function(next) {
|
||||
user.isAdministrator(req.uid, next);
|
||||
},
|
||||
user: function(next) {
|
||||
if (req.uid) {
|
||||
user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'icon:bgColor', 'icon:text', 'status', 'email:confirmed', 'banned'], next);
|
||||
} else {
|
||||
next(null, {
|
||||
username: '[[global:guest]]',
|
||||
userslug: '',
|
||||
picture: meta.config.defaultAvatar,
|
||||
status: 'offline',
|
||||
banned: false,
|
||||
uid: 0
|
||||
});
|
||||
}
|
||||
},
|
||||
navigation: async.apply(navigation.get),
|
||||
tags: async.apply(meta.tags.parse, res.locals.metaTags, res.locals.linkTags)
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (results.user && parseInt(results.user.banned, 10) === 1) {
|
||||
req.logout();
|
||||
return res.redirect('/');
|
||||
}
|
||||
results.user.isAdmin = results.isAdmin || false;
|
||||
results.user.uid = parseInt(results.user.uid, 10);
|
||||
results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1;
|
||||
|
||||
if (results.settings && results.settings.bootswatchSkin && results.settings.bootswatchSkin !== 'default') {
|
||||
templateValues.bootswatchCSS = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + results.settings.bootswatchSkin + '/bootstrap.min.css';
|
||||
}
|
||||
|
||||
templateValues.browserTitle = results.title;
|
||||
templateValues.navigation = results.navigation;
|
||||
templateValues.metaTags = results.tags.meta;
|
||||
templateValues.linkTags = results.tags.link;
|
||||
templateValues.isAdmin = results.user.isAdmin;
|
||||
templateValues.user = results.user;
|
||||
templateValues.userJSON = JSON.stringify(results.user);
|
||||
templateValues.customCSS = results.customCSS;
|
||||
templateValues.customJS = results.customJS;
|
||||
templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin;
|
||||
templateValues.defaultLang = meta.config.defaultLang || 'en_GB';
|
||||
|
||||
templateValues.template = {name: res.locals.template};
|
||||
templateValues.template[res.locals.template] = true;
|
||||
|
||||
if (req.route && req.route.path === '/') {
|
||||
modifyTitle(templateValues);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:middleware.renderHeader', {templateValues: templateValues, req: req, res: res}, function(err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
app.render('header', data.templateValues, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
middleware.processRender = function(req, res, next) {
|
||||
// res.render post-processing, modified from here: https://gist.github.com/mrlannigan/5051687
|
||||
var render = res.render;
|
||||
res.render = function(template, options, fn) {
|
||||
var self = this,
|
||||
req = this.req,
|
||||
app = req.app,
|
||||
defaultFn = function(err, str){
|
||||
if (err) {
|
||||
return req.next(err);
|
||||
}
|
||||
|
||||
self.send(str);
|
||||
};
|
||||
options = options || {};
|
||||
|
||||
if ('function' === typeof options) {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.loggedIn = req.user ? parseInt(req.user.uid, 10) !== 0 : false;
|
||||
options.relative_path = nconf.get('relative_path');
|
||||
options.template = {name: template};
|
||||
options.template[template] = true;
|
||||
res.locals.template = template;
|
||||
|
||||
if ('function' !== typeof fn) {
|
||||
fn = defaultFn;
|
||||
}
|
||||
|
||||
if (res.locals.isAPI) {
|
||||
if (req.route && req.route.path === '/api/') {
|
||||
options.title = '[[pages:home]]';
|
||||
}
|
||||
|
||||
return res.json(options);
|
||||
}
|
||||
|
||||
var ajaxifyData = encodeURIComponent(JSON.stringify(options));
|
||||
render.call(self, template, options, function(err, str) {
|
||||
if (err) {
|
||||
winston.error(err);
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
str = str + '<input type="hidden" ajaxify-data="' + ajaxifyData + '" />';
|
||||
str = (res.locals.postHeader ? res.locals.postHeader : '') + str + (res.locals.preFooter ? res.locals.preFooter : '');
|
||||
|
||||
if (res.locals.footer) {
|
||||
str = str + res.locals.footer;
|
||||
} else if (res.locals.adminFooter) {
|
||||
str = str + res.locals.adminFooter;
|
||||
}
|
||||
|
||||
if (res.locals.renderHeader || res.locals.renderAdminHeader) {
|
||||
var method = res.locals.renderHeader ? middleware.renderHeader : middleware.admin.renderHeader;
|
||||
method(req, res, options, function(err, template) {
|
||||
if (err) {
|
||||
return fn(err);
|
||||
}
|
||||
str = template + str;
|
||||
var language = res.locals.config ? res.locals.config.userLang || 'en_GB' : 'en_GB';
|
||||
language = req.query.lang || language;
|
||||
translator.translate(str, language, function(translated) {
|
||||
fn(err, translated);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
fn(err, str);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
middleware.routeTouchIcon = function(req, res) {
|
||||
if (meta.config['brand:logo'] && validator.isURL(meta.config['brand:logo'])) {
|
||||
return res.redirect(meta.config['brand:logo']);
|
||||
@@ -403,8 +169,6 @@ middleware.addExpiresHeaders = function(req, res, next) {
|
||||
next();
|
||||
};
|
||||
|
||||
|
||||
|
||||
middleware.privateTagListing = function(req, res, next) {
|
||||
if (!req.user && parseInt(meta.config.privateTagListing, 10) === 1) {
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
@@ -414,32 +178,26 @@ middleware.privateTagListing = function(req, res, next) {
|
||||
};
|
||||
|
||||
middleware.exposeGroupName = function(req, res, next) {
|
||||
if (!req.params.hasOwnProperty('slug')) { return next(); }
|
||||
expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next);
|
||||
};
|
||||
|
||||
groups.getGroupNameByGroupSlug(req.params.slug, function(err, groupName) {
|
||||
middleware.exposeUid = function(req, res, next) {
|
||||
expose('uid', user.getUidByUserslug, 'userslug', req, res, next);
|
||||
};
|
||||
|
||||
function expose(exposedField, method, field, req, res, next) {
|
||||
if (!req.params.hasOwnProperty(field)) {
|
||||
return next();
|
||||
}
|
||||
method(req.params[field], function(err, id) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.locals.groupName = groupName;
|
||||
res.locals[exposedField] = id;
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
middleware.exposeUid = function(req, res, next) {
|
||||
if (req.params.hasOwnProperty('userslug')) {
|
||||
user.getUidByUserslug(req.params.userslug, function(err, uid) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.locals.uid = uid;
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
middleware.requireUser = function(req, res, next) {
|
||||
if (req.user) {
|
||||
@@ -449,32 +207,23 @@ middleware.requireUser = function(req, res, next) {
|
||||
res.render('403', {title: '[[global:403.title]]'});
|
||||
};
|
||||
|
||||
function redirectToLogin(req, res) {
|
||||
req.session.returnTo = nconf.get('relative_path') + req.url.replace(/^\/api/, '');
|
||||
return controllers.helpers.redirect(res, '/login');
|
||||
}
|
||||
|
||||
|
||||
|
||||
function modifyTitle(obj) {
|
||||
var title = controllers.helpers.buildTitle('[[pages:home]]');
|
||||
obj.browserTitle = title;
|
||||
|
||||
if (obj.metaTags) {
|
||||
obj.metaTags.forEach(function(tag, i) {
|
||||
if (tag.property === 'og:title') {
|
||||
obj.metaTags[i].content = title;
|
||||
}
|
||||
});
|
||||
middleware.privateUploads = function(req, res, next) {
|
||||
if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) {
|
||||
return next();
|
||||
}
|
||||
if (req.path.startsWith('/uploads/files')) {
|
||||
return res.status(403).json('not-allowed');
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
module.exports = function(webserver) {
|
||||
app = webserver;
|
||||
middleware.admin = require('./admin')(webserver);
|
||||
|
||||
require('./header')(app, middleware);
|
||||
require('./render')(middleware);
|
||||
require('./maintenance')(middleware);
|
||||
|
||||
return middleware;
|
||||
|
||||
95
src/middleware/render.js
Normal file
95
src/middleware/render.js
Normal file
@@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
var nconf = require('nconf');
|
||||
var translator = require('../../public/src/modules/translator');
|
||||
|
||||
module.exports = function(middleware) {
|
||||
|
||||
middleware.processRender = function(req, res, next) {
|
||||
// res.render post-processing, modified from here: https://gist.github.com/mrlannigan/5051687
|
||||
var render = res.render;
|
||||
res.render = function(template, options, fn) {
|
||||
var self = this,
|
||||
req = this.req,
|
||||
defaultFn = function(err, str){
|
||||
if (err) {
|
||||
return req.next(err);
|
||||
}
|
||||
|
||||
self.send(str);
|
||||
};
|
||||
options = options || {};
|
||||
|
||||
if ('function' === typeof options) {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.loggedIn = req.user ? parseInt(req.user.uid, 10) !== 0 : false;
|
||||
options.relative_path = nconf.get('relative_path');
|
||||
options.template = {name: template};
|
||||
options.template[template] = true;
|
||||
options.bodyClass = buildBodyClass(req);
|
||||
|
||||
res.locals.template = template;
|
||||
|
||||
if (res.locals.isAPI) {
|
||||
if (req.route && req.route.path === '/api/') {
|
||||
options.title = '[[pages:home]]';
|
||||
}
|
||||
|
||||
return res.json(options);
|
||||
}
|
||||
|
||||
if ('function' !== typeof fn) {
|
||||
fn = defaultFn;
|
||||
}
|
||||
|
||||
var ajaxifyData = encodeURIComponent(JSON.stringify(options));
|
||||
render.call(self, template, options, function(err, str) {
|
||||
if (err) {
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
str = str + '<input type="hidden" ajaxify-data="' + ajaxifyData + '" />';
|
||||
str = (res.locals.postHeader ? res.locals.postHeader : '') + str + (res.locals.preFooter ? res.locals.preFooter : '');
|
||||
|
||||
if (res.locals.footer) {
|
||||
str = str + res.locals.footer;
|
||||
} else if (res.locals.adminFooter) {
|
||||
str = str + res.locals.adminFooter;
|
||||
}
|
||||
|
||||
if (res.locals.renderHeader || res.locals.renderAdminHeader) {
|
||||
var method = res.locals.renderHeader ? middleware.renderHeader : middleware.admin.renderHeader;
|
||||
method(req, res, options, function(err, template) {
|
||||
if (err) {
|
||||
return fn(err);
|
||||
}
|
||||
str = template + str;
|
||||
var language = res.locals.config ? res.locals.config.userLang || 'en_GB' : 'en_GB';
|
||||
language = req.query.lang || language;
|
||||
translator.translate(str, language, function(translated) {
|
||||
fn(err, translated);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
fn(err, str);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
function buildBodyClass(req) {
|
||||
var clean = req.path.replace(/^\/api/, '').replace(/^\//, '');
|
||||
var parts = clean.split('/').slice(0, 3);
|
||||
parts.forEach(function(p, index) {
|
||||
parts[index] = index ? parts[index - 1] + '-' + p : 'page-' + (p || 'home');
|
||||
});
|
||||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
};
|
||||
@@ -7,6 +7,7 @@ var admin = {},
|
||||
db = require('../database'),
|
||||
translator = require('../../public/src/modules/translator');
|
||||
|
||||
var navigationCache = null;
|
||||
|
||||
admin.save = function(data, callback) {
|
||||
var order = Object.keys(data),
|
||||
@@ -23,6 +24,7 @@ admin.save = function(data, callback) {
|
||||
return JSON.stringify(data);
|
||||
});
|
||||
|
||||
navigationCache = null;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
db.delete('navigation:enabled', next);
|
||||
@@ -41,10 +43,19 @@ admin.getAdmin = function(callback) {
|
||||
};
|
||||
|
||||
admin.get = function(callback) {
|
||||
if (navigationCache) {
|
||||
return callback(null, navigationCache);
|
||||
}
|
||||
|
||||
db.getSortedSetRange('navigation:enabled', 0, -1, function(err, data) {
|
||||
callback(err, data.map(function(item, idx) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
navigationCache = data.map(function(item, idx) {
|
||||
return JSON.parse(item)[idx];
|
||||
}));
|
||||
});
|
||||
|
||||
callback(null, navigationCache);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
|
||||
var navigation = {},
|
||||
plugins = require('../plugins'),
|
||||
db = require('../database'),
|
||||
admin = require('./admin'),
|
||||
translator = require('../../public/src/modules/translator');
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ var async = require('async'),
|
||||
return callback();
|
||||
}
|
||||
|
||||
db.getObject('notification:' + nid, function(err, notification) {
|
||||
db.getObject('notifications:' + nid, function(err, notification) {
|
||||
if (err || !notification) {
|
||||
return callback(err || new Error('[[error:no-notification]]'));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ pagination.create = function(currentPage, pageCount, queryObj) {
|
||||
prev: {page: 1, active: currentPage > 1},
|
||||
next: {page: 1, active: currentPage < pageCount},
|
||||
rel: [],
|
||||
pages: []
|
||||
pages: [],
|
||||
currentPage: 1,
|
||||
pageCount: 1
|
||||
};
|
||||
}
|
||||
pageCount = parseInt(pageCount, 10);
|
||||
@@ -44,7 +46,7 @@ pagination.create = function(currentPage, pageCount, queryObj) {
|
||||
}
|
||||
}
|
||||
|
||||
var data = {rel: [], pages: pages};
|
||||
var data = {rel: [], pages: pages, currentPage: currentPage, pageCount: pageCount};
|
||||
queryObj.page = previous;
|
||||
data.prev = {page: previous, active: currentPage > 1, qs: qs.stringify(queryObj)};
|
||||
queryObj.page = next;
|
||||
|
||||
@@ -9,7 +9,10 @@ var async = require('async'),
|
||||
|
||||
module.exports = function(Posts) {
|
||||
|
||||
Posts.flag = function(post, uid, callback) {
|
||||
Posts.flag = function(post, uid, reason, callback) {
|
||||
if (!parseInt(uid, 10) || !reason) {
|
||||
return callback();
|
||||
}
|
||||
async.parallel({
|
||||
hasFlagged: async.apply(hasFlagged, post.pid, uid),
|
||||
exists: async.apply(Posts.exists, post.pid)
|
||||
@@ -36,6 +39,9 @@ module.exports = function(Posts) {
|
||||
function(next) {
|
||||
db.sortedSetAdd('pid:' + post.pid + ':flag:uids', now, uid, next);
|
||||
},
|
||||
function(next) {
|
||||
db.sortedSetAdd('pid:' + post.pid + ':flag:uid:reason', 0, uid + ':' + reason, next);
|
||||
},
|
||||
function(next) {
|
||||
if (parseInt(post.uid, 10)) {
|
||||
db.sortedSetAdd('uid:' + post.uid + ':flag:pids', now, post.pid, next);
|
||||
@@ -50,7 +56,7 @@ module.exports = function(Posts) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
], function(err, results) {
|
||||
], function(err) {
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
@@ -80,26 +86,75 @@ module.exports = function(Posts) {
|
||||
},
|
||||
function(next) {
|
||||
db.delete('pid:' + pid + ':flag:uids', next);
|
||||
},
|
||||
function(next) {
|
||||
db.delete('pid:' + pid + ':flag:uid:reason', next);
|
||||
}
|
||||
], function(err, results) {
|
||||
], function(err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
Posts.dismissAllFlags = function(callback) {
|
||||
db.delete('posts:flagged', callback);
|
||||
};
|
||||
|
||||
Posts.getFlags = function(set, uid, start, stop, callback) {
|
||||
db.getSortedSetRevRange(set, start, stop, function(err, pids) {
|
||||
db.getSortedSetRange('posts:flagged', 0, -1, function(err, pids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags']}, callback);
|
||||
async.eachLimit(pids, 50, Posts.dismissFlag, callback);
|
||||
});
|
||||
};
|
||||
|
||||
Posts.getFlags = function(set, uid, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange(set, start, stop, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
getFlaggedPostsWithReasons(pids, uid, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
function getFlaggedPostsWithReasons(pids, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
uidsReasons: function(next) {
|
||||
async.map(pids, function(pid, next) {
|
||||
db.getSortedSetRange('pid:' + pid + ':flag:uid:reason', 0, -1, next);
|
||||
}, next);
|
||||
},
|
||||
posts: function(next) {
|
||||
Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags']}, next);
|
||||
}
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
async.map(results.uidsReasons, function(uidReasons, next) {
|
||||
async.map(uidReasons, function(uidReason, next) {
|
||||
var uid = uidReason.split(':')[0];
|
||||
var reason = uidReason.substr(uidReason.indexOf(':') + 1);
|
||||
user.getUserFields(uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
||||
next(err, {user: userData, reason: reason});
|
||||
});
|
||||
}, next);
|
||||
}, function(err, reasons) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
results.posts.forEach(function(post, index) {
|
||||
if (post) {
|
||||
post.flagReasons = reasons[index];
|
||||
}
|
||||
});
|
||||
|
||||
next(null, results.posts);
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
Posts.getUserFlags = function(byUsername, sortBy, callerUID, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
@@ -112,7 +167,7 @@ module.exports = function(Posts) {
|
||||
db.getSortedSetRevRange('uid:' + uid + ':flag:pids', 0, -1, next);
|
||||
},
|
||||
function(pids, next) {
|
||||
Posts.getPostSummaryByPids(pids, callerUID, {stripTags: false, extraFields: ['flags']}, next);
|
||||
getFlaggedPostsWithReasons(pids, callerUID, next);
|
||||
},
|
||||
function(posts, next) {
|
||||
if (sortBy === 'count') {
|
||||
@@ -120,6 +175,7 @@ module.exports = function(Posts) {
|
||||
return b.flags - a.flags;
|
||||
});
|
||||
}
|
||||
|
||||
next(null, posts.slice(start, stop));
|
||||
}
|
||||
], callback);
|
||||
|
||||
@@ -179,7 +179,8 @@ module.exports = function(privileges) {
|
||||
'topics:create': results['topics:create'][0] || isAdminOrMod,
|
||||
editable: isAdminOrMod,
|
||||
view_deleted: isAdminOrMod,
|
||||
read: results.read[0] || isAdminOrMod
|
||||
read: results.read[0] || isAdminOrMod,
|
||||
isAdminOrMod: isAdminOrMod
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -17,8 +17,11 @@ module.exports = function (app, middleware, controllers) {
|
||||
setupPageRoute(app, '/user/:userslug/favourites', middleware, accountMiddlewares, controllers.accounts.posts.getFavourites);
|
||||
setupPageRoute(app, '/user/:userslug/watched', middleware, accountMiddlewares, controllers.accounts.posts.getWatchedTopics);
|
||||
setupPageRoute(app, '/user/:userslug/edit', middleware, accountMiddlewares, controllers.accounts.edit.get);
|
||||
setupPageRoute(app, '/user/:userslug/edit/username', middleware, accountMiddlewares, controllers.accounts.edit.username);
|
||||
setupPageRoute(app, '/user/:userslug/edit/email', middleware, accountMiddlewares, controllers.accounts.edit.email);
|
||||
setupPageRoute(app, '/user/:userslug/edit/password', middleware, accountMiddlewares, controllers.accounts.edit.password);
|
||||
setupPageRoute(app, '/user/:userslug/settings', middleware, accountMiddlewares, controllers.accounts.settings.get);
|
||||
|
||||
setupPageRoute(app, '/notifications', middleware, [middleware.authenticate], controllers.accounts.notifications.get);
|
||||
setupPageRoute(app, '/chats/:userslug?', middleware, [middleware.redirectToLoginIfGuest], controllers.accounts.chats.get);
|
||||
setupPageRoute(app, '/chats/:userslug?', middleware, [middleware.authenticate], controllers.accounts.chats.get);
|
||||
};
|
||||
|
||||
@@ -80,6 +80,7 @@ function addRoutes(router, middleware, controllers) {
|
||||
router.get('/advanced/post-cache', middlewares, controllers.admin.postCache.get);
|
||||
|
||||
router.get('/development/logger', middlewares, controllers.admin.logger.get);
|
||||
router.get('/development/info', middlewares, controllers.admin.info.get);
|
||||
}
|
||||
|
||||
module.exports = function(app, middleware, controllers) {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
var express = require('express'),
|
||||
|
||||
posts = require('../posts'),
|
||||
categories = require('../categories'),
|
||||
uploadsController = require('../controllers/uploads');
|
||||
|
||||
module.exports = function(app, middleware, controllers) {
|
||||
@@ -22,6 +20,7 @@ module.exports = function(app, middleware, controllers) {
|
||||
router.get('/categories/:cid/moderators', controllers.api.getModerators);
|
||||
router.get('/recent/posts/:term?', controllers.api.getRecentPosts);
|
||||
router.get('/unread/total', middleware.authenticate, controllers.unread.unreadTotal);
|
||||
router.get('/topic/teaser/:topic_id', controllers.topics.teaser);
|
||||
|
||||
var multipart = require('connect-multiparty');
|
||||
var multipartMiddleware = multipart();
|
||||
|
||||
@@ -4,7 +4,6 @@ var nconf = require('nconf'),
|
||||
path = require('path'),
|
||||
winston = require('winston'),
|
||||
controllers = require('../controllers'),
|
||||
meta = require('../meta'),
|
||||
plugins = require('../plugins'),
|
||||
express = require('express'),
|
||||
|
||||
@@ -30,14 +29,12 @@ function mainRoutes(app, middleware, controllers) {
|
||||
setupPageRoute(app, '/compose', middleware, [middleware.authenticate], controllers.compose);
|
||||
setupPageRoute(app, '/confirm/:code', middleware, [], controllers.confirmEmail);
|
||||
setupPageRoute(app, '/outgoing', middleware, [], controllers.outgoing);
|
||||
setupPageRoute(app, '/search/:term?', middleware, [middleware.guestSearchingAllowed], controllers.search.search);
|
||||
setupPageRoute(app, '/search/:term?', middleware, [], controllers.search.search);
|
||||
setupPageRoute(app, '/reset/:code?', middleware, [], controllers.reset);
|
||||
setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse);
|
||||
}
|
||||
|
||||
function topicRoutes(app, middleware, controllers) {
|
||||
app.get('/api/topic/teaser/:topic_id', controllers.topics.teaser);
|
||||
|
||||
setupPageRoute(app, '/topic/:topic_id/:slug/:post_index?', middleware, [], controllers.topics.get);
|
||||
setupPageRoute(app, '/topic/:topic_id/:slug?', middleware, [], controllers.topics.get);
|
||||
}
|
||||
@@ -120,15 +117,7 @@ module.exports = function(app, middleware) {
|
||||
require('./debug')(app, middleware, controllers);
|
||||
}
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) {
|
||||
return next();
|
||||
}
|
||||
if (req.path.startsWith('/uploads/files')) {
|
||||
return res.status(403).json('not-allowed');
|
||||
}
|
||||
next();
|
||||
});
|
||||
app.use(middleware.privateUploads);
|
||||
|
||||
app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), {
|
||||
maxAge: app.enabled('cache') ? 5184000000 : 0
|
||||
@@ -144,7 +133,11 @@ module.exports = function(app, middleware) {
|
||||
};
|
||||
|
||||
function handle404(app, middleware) {
|
||||
app.use(function(req, res, next) {
|
||||
var relativePath = nconf.get('relative_path');
|
||||
var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'),
|
||||
isClientScript = new RegExp('^' + relativePath + '\\/src\\/.+\\.js');
|
||||
|
||||
app.use(function(req, res) {
|
||||
if (plugins.hasListeners('action:meta.override404')) {
|
||||
return plugins.fireHook('action:meta.override404', {
|
||||
req: req,
|
||||
@@ -153,14 +146,14 @@ function handle404(app, middleware) {
|
||||
});
|
||||
}
|
||||
|
||||
var relativePath = nconf.get('relative_path');
|
||||
var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'),
|
||||
isClientScript = new RegExp('^' + relativePath + '\\/src\\/.+\\.js');
|
||||
|
||||
if (isClientScript.test(req.url)) {
|
||||
res.type('text/javascript').status(200).send('');
|
||||
} else if (isLanguage.test(req.url)) {
|
||||
res.status(200).json({});
|
||||
} else if (req.path.startsWith(relativePath + '/uploads')) {
|
||||
res.status(404).send('');
|
||||
} else if (req.path === '/favicon.ico') {
|
||||
res.status(404).send('');
|
||||
} else if (req.accepts('html')) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.warn('Route requested but not found: ' + req.url);
|
||||
@@ -182,7 +175,7 @@ function handle404(app, middleware) {
|
||||
}
|
||||
|
||||
function handleErrors(app, middleware) {
|
||||
app.use(function(err, req, res, next) {
|
||||
app.use(function(err, req, res) {
|
||||
if (err.code === 'EBADCSRFTOKEN') {
|
||||
winston.error(req.path + '\n', err.message);
|
||||
return res.sendStatus(403);
|
||||
|
||||
@@ -22,15 +22,13 @@ search.search = function(data, callback) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
result.search_query = validator.escape(query);
|
||||
data.search_query = validator.escape(query);
|
||||
if (searchIn === 'titles' || searchIn === 'titlesposts') {
|
||||
searchIn = 'posts';
|
||||
}
|
||||
result[searchIn] = data.matches;
|
||||
result.matchCount = data.matchCount;
|
||||
result.pageCount = data.pageCount;
|
||||
result.time = (process.elapsedTimeSince(start) / 1000).toFixed(2);
|
||||
callback(null, result);
|
||||
|
||||
data.time = (process.elapsedTimeSince(start) / 1000).toFixed(2);
|
||||
callback(null, data);
|
||||
}
|
||||
|
||||
var start = process.hrtime();
|
||||
@@ -38,18 +36,12 @@ search.search = function(data, callback) {
|
||||
var query = data.query;
|
||||
var searchIn = data.searchIn || 'titlesposts';
|
||||
|
||||
var result = {
|
||||
posts: [],
|
||||
users: [],
|
||||
tags: []
|
||||
};
|
||||
|
||||
if (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts') {
|
||||
searchInContent(data, done);
|
||||
} else if (searchIn === 'users') {
|
||||
searchInUsers(data, done);
|
||||
user.search(data, done);
|
||||
} else if (searchIn === 'tags') {
|
||||
searchInTags(query, done);
|
||||
topics.searchAndLoadTags(data, done);
|
||||
} else {
|
||||
callback(new Error('[[error:unknown-search-filter]]'));
|
||||
}
|
||||
@@ -91,7 +83,7 @@ function searchInContent(data, callback) {
|
||||
|
||||
var matchCount = 0;
|
||||
if (!results || (!results.pids.length && !results.tids.length)) {
|
||||
return callback(null, {matches: [], matchCount: matchCount, pageCount: 1});
|
||||
return callback(null, {posts: [], matchCount: matchCount, pageCount: 1});
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
@@ -118,7 +110,7 @@ function searchInContent(data, callback) {
|
||||
posts.getPostSummaryByPids(pids, data.uid, {}, next);
|
||||
},
|
||||
function(posts, next) {
|
||||
next(null, {matches: posts, matchCount: matchCount, pageCount: Math.max(1, Math.ceil(parseInt(matchCount, 10) / 10))});
|
||||
next(null, {posts: posts, matchCount: matchCount, pageCount: Math.max(1, Math.ceil(parseInt(matchCount, 10) / 10))});
|
||||
}
|
||||
], callback);
|
||||
});
|
||||
@@ -315,16 +307,12 @@ function sortPosts(posts, data) {
|
||||
}
|
||||
data.sortBy = data.sortBy || 'timestamp';
|
||||
data.sortDirection = data.sortDirection || 'desc';
|
||||
var direction = data.sortDirection === 'desc' ? 1 : -1;
|
||||
|
||||
if (data.sortBy === 'timestamp') {
|
||||
if (data.sortDirection === 'desc') {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p2.timestamp - p1.timestamp;
|
||||
});
|
||||
} else {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p1.timestamp - p2.timestamp;
|
||||
});
|
||||
}
|
||||
posts.sort(function(p1, p2) {
|
||||
return direction * (p2.timestamp - p1.timestamp);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -336,21 +324,13 @@ function sortPosts(posts, data) {
|
||||
return;
|
||||
}
|
||||
|
||||
var value = firstPost[fields[0]][fields[1]];
|
||||
var isNumeric = utils.isNumber(value);
|
||||
var isNumeric = utils.isNumber(firstPost[fields[0]][fields[1]]);
|
||||
|
||||
if (isNumeric) {
|
||||
if (data.sortDirection === 'desc') {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p2[fields[0]][fields[1]] - p1[fields[0]][fields[1]];
|
||||
});
|
||||
} else {
|
||||
posts.sort(function(p1, p2) {
|
||||
return p1[fields[0]][fields[1]] - p2[fields[0]][fields[1]];
|
||||
});
|
||||
}
|
||||
posts.sort(function(p1, p2) {
|
||||
return direction * (p2[fields[0]][fields[1]] - p1[fields[0]][fields[1]]);
|
||||
});
|
||||
} else {
|
||||
var direction = data.sortDirection === 'desc' ? 1 : -1;
|
||||
posts.sort(function(p1, p2) {
|
||||
if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) {
|
||||
return direction;
|
||||
@@ -435,25 +415,6 @@ function getSearchUids(data, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
function searchInUsers(data, callback) {
|
||||
user.search(data, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {matches: results.users, matchCount: results.matchCount, pageCount: results.pageCount});
|
||||
});
|
||||
}
|
||||
|
||||
function searchInTags(query, callback) {
|
||||
topics.searchAndLoadTags({query: query}, function(err, tags) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, {matches: tags, matchCount: tags.length, pageCount: 1});
|
||||
});
|
||||
}
|
||||
|
||||
search.searchQuery = function(index, content, cids, uids, callback) {
|
||||
plugins.fireHook('filter:search.query', {
|
||||
index: index,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
var async = require('async');
|
||||
var groups = require('../../groups'),
|
||||
Groups = {};
|
||||
|
||||
@@ -20,7 +21,17 @@ Groups.join = function(socket, data, callback) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
groups.join(data.groupName, data.uid, callback);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
groups.isMember(data.uid, data.groupName, next);
|
||||
},
|
||||
function (isMember, next) {
|
||||
if (isMember) {
|
||||
return next(new Error('[[error:group-already-member]]'));
|
||||
}
|
||||
groups.join(data.groupName, data.uid, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.leave = function(socket, data, callback) {
|
||||
@@ -32,7 +43,17 @@ Groups.leave = function(socket, data, callback) {
|
||||
return callback(new Error('[[error:cant-remove-self-as-admin]]'));
|
||||
}
|
||||
|
||||
groups.leave(data.groupName, data.uid, callback);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
groups.isMember(data.uid, data.groupName, next);
|
||||
},
|
||||
function (isMember, next) {
|
||||
if (!isMember) {
|
||||
return next(new Error('[[error:group-not-member]]'));
|
||||
}
|
||||
groups.leave(data.groupName, data.uid, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
Groups.update = function(socket, data, callback) {
|
||||
|
||||
@@ -15,7 +15,25 @@ SocketCategories.getRecentReplies = function(socket, cid, callback) {
|
||||
};
|
||||
|
||||
SocketCategories.get = function(socket, data, callback) {
|
||||
categories.getCategoriesByPrivilege('categories:cid', socket.uid, 'find', callback);
|
||||
async.parallel({
|
||||
isAdmin: async.apply(user.isAdministrator, socket.uid),
|
||||
categories: function(next) {
|
||||
async.waterfall([
|
||||
async.apply(db.getSortedSetRange, 'categories:cid', 0, -1),
|
||||
async.apply(categories.getCategoriesData),
|
||||
], next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
results.categories = results.categories.filter(function(category) {
|
||||
return category && (!category.disabled || results.isAdmin);
|
||||
});
|
||||
|
||||
callback(null, results.categories);
|
||||
});
|
||||
};
|
||||
|
||||
SocketCategories.getWatchedCategories = function(socket, data, callback) {
|
||||
|
||||
@@ -62,8 +62,11 @@ SocketGroups.leave = function(socket, data, callback) {
|
||||
|
||||
function isOwner(next) {
|
||||
return function (socket, data, callback) {
|
||||
groups.ownership.isOwner(socket.uid, data.groupName, function(err, isOwner) {
|
||||
if (err || !isOwner) {
|
||||
async.parallel({
|
||||
isAdmin: async.apply(user.isAdmin, socket.uid),
|
||||
isOwner: async.apply(groups.ownership.isOwner, socket.uid, data.groupName)
|
||||
}, function(err, results) {
|
||||
if (err || (!isOwner && !results.isAdmin)) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
next(socket, data, callback);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
var nconf = require('nconf');
|
||||
var validator = require('validator');
|
||||
|
||||
var websockets = require('./index');
|
||||
var user = require('../user');
|
||||
@@ -86,14 +87,14 @@ SocketHelpers.sendNotificationToTopicOwner = function(tid, fromuid, notification
|
||||
|
||||
async.parallel({
|
||||
username: async.apply(user.getUserField, fromuid, 'username'),
|
||||
topicData: async.apply(topics.getTopicFields, tid, ['uid', 'slug']),
|
||||
topicData: async.apply(topics.getTopicFields, tid, ['uid', 'slug', 'title']),
|
||||
}, function(err, results) {
|
||||
if (err || fromuid === parseInt(results.topicData.uid, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifications.create({
|
||||
bodyShort: '[[' + notification + ', ' + results.username + ']]',
|
||||
bodyShort: '[[' + notification + ', ' + results.username + ', ' + results.topicData.title + ']]',
|
||||
path: nconf.get('relative_path') + '/topic/' + results.topicData.slug,
|
||||
nid: 'tid:' + tid + ':uid:' + fromuid,
|
||||
from: fromuid
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
var SocketIO = require('socket.io'),
|
||||
socketioWildcard = require('socketio-wildcard')(),
|
||||
async = require('async'),
|
||||
fs = require('fs'),
|
||||
nconf = require('nconf'),
|
||||
cookieParser = require('cookie-parser')(nconf.get('secret')),
|
||||
winston = require('winston'),
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
validator = require('validator'),
|
||||
var validator = require('validator'),
|
||||
|
||||
db = require('../database'),
|
||||
meta = require('../meta'),
|
||||
user = require('../user'),
|
||||
topics = require('../topics'),
|
||||
logger = require('../logger'),
|
||||
plugins = require('../plugins'),
|
||||
emitter = require('../emitter'),
|
||||
rooms = require('./rooms'),
|
||||
|
||||
@@ -52,13 +47,7 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
|
||||
return callback(new Error('[[error:not-allowed]]'));
|
||||
}
|
||||
|
||||
if (socket.currentRoom) {
|
||||
rooms.leave(socket, socket.currentRoom);
|
||||
if (socket.currentRoom.indexOf('topic') !== -1) {
|
||||
websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid);
|
||||
}
|
||||
socket.currentRoom = '';
|
||||
}
|
||||
leaveCurrentRoom(socket);
|
||||
|
||||
if (data.enter) {
|
||||
rooms.enter(socket, data.enter);
|
||||
@@ -75,6 +64,24 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
|
||||
callback();
|
||||
};
|
||||
|
||||
SocketMeta.rooms.leaveCurrent = function(socket, data, callback) {
|
||||
if (!socket.uid || !socket.currentRoom) {
|
||||
return callback();
|
||||
}
|
||||
leaveCurrentRoom(socket);
|
||||
callback();
|
||||
};
|
||||
|
||||
function leaveCurrentRoom(socket) {
|
||||
if (socket.currentRoom) {
|
||||
rooms.leave(socket, socket.currentRoom);
|
||||
if (socket.currentRoom.indexOf('topic') !== -1) {
|
||||
websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid);
|
||||
}
|
||||
socket.currentRoom = '';
|
||||
}
|
||||
}
|
||||
|
||||
SocketMeta.rooms.getAll = function(socket, data, callback) {
|
||||
var roomClients = rooms.roomClients();
|
||||
var socketData = {
|
||||
|
||||
@@ -147,4 +147,11 @@ SocketModules.sounds.getMapping = function(socket, data, callback) {
|
||||
meta.sounds.getMapping(callback);
|
||||
};
|
||||
|
||||
SocketModules.sounds.getData = function(socket, data, callback) {
|
||||
async.parallel({
|
||||
mapping: async.apply(meta.sounds.getMapping),
|
||||
files: async.apply(meta.sounds.getFiles)
|
||||
}, callback);
|
||||
};
|
||||
|
||||
module.exports = SocketModules;
|
||||
|
||||
@@ -12,6 +12,23 @@ SocketNotifs.get = function(socket, data, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
SocketNotifs.loadMore = function(socket, data, callback) {
|
||||
if (!data || !parseInt(data.after, 10)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
if (!socket.uid) {
|
||||
return;
|
||||
}
|
||||
var start = parseInt(data.after, 10);
|
||||
var stop = start + 20;
|
||||
user.notifications.getAll(socket.uid, start, stop, function(err, notifications) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {notifications: notifications, nextStart: stop});
|
||||
});
|
||||
};
|
||||
|
||||
SocketNotifs.getCount = function(socket, data, callback) {
|
||||
user.notifications.getUnreadCount(socket.uid, callback);
|
||||
};
|
||||
|
||||
@@ -13,17 +13,21 @@ var meta = require('../../meta');
|
||||
|
||||
module.exports = function(SocketPosts) {
|
||||
|
||||
SocketPosts.flag = function(socket, pid, callback) {
|
||||
SocketPosts.flag = function(socket, data, callback) {
|
||||
if (!socket.uid) {
|
||||
return callback(new Error('[[error:not-logged-in]]'));
|
||||
}
|
||||
|
||||
if (!data || !data.pid || !data.reason) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
var flaggingUser = {},
|
||||
post;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next);
|
||||
posts.getPostFields(data.pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next);
|
||||
},
|
||||
function (postData, next) {
|
||||
if (parseInt(postData.deleted, 10) === 1) {
|
||||
@@ -55,7 +59,7 @@ module.exports = function(SocketPosts) {
|
||||
flaggingUser = user.userData;
|
||||
flaggingUser.uid = socket.uid;
|
||||
|
||||
posts.flag(post, socket.uid, next);
|
||||
posts.flag(post, socket.uid, data.reason, next);
|
||||
},
|
||||
function (next) {
|
||||
async.parallel({
|
||||
@@ -74,8 +78,8 @@ module.exports = function(SocketPosts) {
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + post.topic.title + ']]',
|
||||
bodyLong: post.content,
|
||||
pid: pid,
|
||||
nid: 'post_flag:' + pid + ':uid:' + socket.uid,
|
||||
pid: data.pid,
|
||||
nid: 'post_flag:' + data.pid + ':uid:' + socket.uid,
|
||||
from: socket.uid
|
||||
}, function(err, notification) {
|
||||
if (err || !notification) {
|
||||
|
||||
@@ -116,19 +116,6 @@ SocketUser.reset.commit = function(socket, data, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
SocketUser.isAdminOrSelf = function(socket, uid, callback) {
|
||||
if (socket.uid === parseInt(uid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
user.isAdministrator(socket.uid, function(err, isAdmin) {
|
||||
if (err || !isAdmin) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
SocketUser.follow = function(socket, data, callback) {
|
||||
if (!socket.uid || !data) {
|
||||
return;
|
||||
@@ -182,7 +169,7 @@ SocketUser.saveSettings = function(socket, data, callback) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
SocketUser.isAdminOrSelf(socket, data.uid, function(err) {
|
||||
user.isAdminOrSelf(socket.uid, data.uid, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -220,6 +207,17 @@ SocketUser.getUnreadChatCount = function(socket, data, callback) {
|
||||
messaging.getUnreadCount(socket.uid, callback);
|
||||
};
|
||||
|
||||
SocketUser.getUnreadCounts = function(socket, data, callback) {
|
||||
if (!socket.uid) {
|
||||
return callback(null, {});
|
||||
}
|
||||
async.parallel({
|
||||
unreadTopicCount: async.apply(topics.getTotalUnread, socket.uid),
|
||||
unreadChatCount: async.apply(messaging.getUnreadCount, socket.uid),
|
||||
unreadNotificationCount: async.apply(user.notifications.getUnreadCount, socket.uid)
|
||||
}, callback);
|
||||
};
|
||||
|
||||
SocketUser.loadMore = function(socket, data, callback) {
|
||||
if (!data || !data.set || parseInt(data.after, 10) < 0) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
|
||||
@@ -23,12 +23,12 @@ module.exports = function(SocketUser) {
|
||||
} else if (type === 'uploaded') {
|
||||
type = 'uploadedpicture';
|
||||
} else {
|
||||
return callback(new Error('[[error:invalid-image-type, ' + ['default', 'uploadedpicture'].join(', ') + ']]'));
|
||||
return callback(new Error('[[error:invalid-image-type, ' + ['default', 'uploadedpicture'].join(', ') + ']]'));
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
SocketUser.isAdminOrSelf(socket, data.uid, next);
|
||||
user.isAdminOrSelf(socket.uid, data.uid, next);
|
||||
},
|
||||
function (next) {
|
||||
if (!type) {
|
||||
@@ -47,7 +47,7 @@ module.exports = function(SocketUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
SocketUser.isAdminOrSelf(socket, data.uid, function(err) {
|
||||
user.isAdminOrSelf(socket.uid, data.uid, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -64,7 +64,7 @@ module.exports = function(SocketUser) {
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
SocketUser.isAdminOrSelf(socket, data.uid, next);
|
||||
user.isAdminOrSelf(socket.uid, data.uid, next);
|
||||
},
|
||||
function (next) {
|
||||
user.getUserField(data.uid, 'uploadedpicture', next);
|
||||
|
||||
@@ -8,6 +8,44 @@ var events = require('../../events');
|
||||
|
||||
module.exports = function(SocketUser) {
|
||||
|
||||
SocketUser.changeUsernameEmail = function(socket, data, callback) {
|
||||
if (!data || !data.uid || !socket.uid) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
isAdminOrSelfAndPasswordMatch(socket.uid, data, next);
|
||||
},
|
||||
function (next) {
|
||||
SocketUser.updateProfile(socket, data, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
function isAdminOrSelfAndPasswordMatch(uid, data, callback) {
|
||||
async.parallel({
|
||||
isAdmin: async.apply(user.isAdministrator, uid),
|
||||
hasPassword: async.apply(user.hasPassword, data.uid),
|
||||
passwordMatch: async.apply(user.isPasswordCorrect, data.uid, data.password)
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var self = parseInt(uid, 10) === parseInt(data.uid, 10);
|
||||
|
||||
if (!results.isAdmin && !self) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
if (self && results.hasPassword && !results.passwordMatch) {
|
||||
return callback(new Error('[[error:invalid-password]]'));
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
SocketUser.changePassword = function(socket, data, callback) {
|
||||
if (!data || !data.uid || data.newPassword.length < meta.config.minimumPasswordLength) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
@@ -31,7 +69,6 @@ module.exports = function(SocketUser) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
SocketUser.updateProfile = function(socket, data, callback) {
|
||||
if (!socket.uid) {
|
||||
return callback('[[error:invalid-uid]]');
|
||||
@@ -55,7 +92,7 @@ module.exports = function(SocketUser) {
|
||||
if (parseInt(meta.config['username:disableEdit'], 10) === 1) {
|
||||
data.username = oldUserData.username;
|
||||
}
|
||||
SocketUser.isAdminOrSelf(socket, data.uid, next);
|
||||
user.isAdminOrSelf(socket.uid, data.uid, next);
|
||||
},
|
||||
function (next) {
|
||||
user.updateProfile(data.uid, data, next);
|
||||
|
||||
@@ -101,6 +101,9 @@ module.exports = function(Topics) {
|
||||
check(data.tags, meta.config.minimumTagsPerTopic, meta.config.maximumTagsPerTopic, 'not-enough-tags', 'too-many-tags', next);
|
||||
},
|
||||
function(next) {
|
||||
if (data.content) {
|
||||
data.content = data.content.rtrim();
|
||||
}
|
||||
check(data.content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next);
|
||||
},
|
||||
function(next) {
|
||||
@@ -228,7 +231,7 @@ module.exports = function(Topics) {
|
||||
function(filteredData, next) {
|
||||
content = filteredData.content || data.content;
|
||||
if (content) {
|
||||
content = content.trim();
|
||||
content = content.rtrim();
|
||||
}
|
||||
|
||||
check(content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next);
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
winston = require('winston'),
|
||||
|
||||
db = require('../database'),
|
||||
meta = require('../meta'),
|
||||
_ = require('underscore'),
|
||||
plugins = require('../plugins'),
|
||||
utils = require('../../public/src/utils');
|
||||
plugins = require('../plugins');
|
||||
|
||||
|
||||
module.exports = function(Topics) {
|
||||
|
||||
@@ -248,7 +248,7 @@ module.exports = function(Topics) {
|
||||
};
|
||||
|
||||
Topics.searchTags = function(data, callback) {
|
||||
if (!data) {
|
||||
if (!data || !data.query) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
@@ -256,9 +256,7 @@ module.exports = function(Topics) {
|
||||
if (err) {
|
||||
return callback(null, []);
|
||||
}
|
||||
if (data.query === '') {
|
||||
return callback(null, tags);
|
||||
}
|
||||
|
||||
data.query = data.query.toLowerCase();
|
||||
|
||||
var matches = [];
|
||||
@@ -279,8 +277,14 @@ module.exports = function(Topics) {
|
||||
};
|
||||
|
||||
Topics.searchAndLoadTags = function(data, callback) {
|
||||
var searchResult = {
|
||||
tags: [],
|
||||
matchCount: 0,
|
||||
pageCount: 1
|
||||
};
|
||||
|
||||
if (!data.query || !data.query.length) {
|
||||
return callback(null, []);
|
||||
return callback(null, searchResult);
|
||||
}
|
||||
Topics.searchTags(data, function(err, tags) {
|
||||
if (err) {
|
||||
@@ -307,8 +311,10 @@ module.exports = function(Topics) {
|
||||
results.tagData.sort(function(a, b) {
|
||||
return b.score - a.score;
|
||||
});
|
||||
|
||||
callback(null, results.tagData);
|
||||
searchResult.tags = results.tagData;
|
||||
searchResult.matchCount = results.tagData.length;
|
||||
searchResult.pageCount = 1;
|
||||
callback(null, searchResult);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -142,7 +142,7 @@ module.exports = function(Topics) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
require('../socket.io').in('uid_' + uid).emit('event:unread.updateCount', null, count);
|
||||
require('../socket.io').in('uid_' + uid).emit('event:unread.updateCount', count);
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
18
src/user.js
18
src/user.js
@@ -3,14 +3,11 @@
|
||||
var async = require('async'),
|
||||
nconf = require('nconf'),
|
||||
gravatar = require('gravatar'),
|
||||
validator = require('validator'),
|
||||
|
||||
plugins = require('./plugins'),
|
||||
db = require('./database'),
|
||||
meta = require('./meta'),
|
||||
topics = require('./topics'),
|
||||
groups = require('./groups'),
|
||||
Password = require('./password'),
|
||||
privileges = require('./privileges'),
|
||||
utils = require('../public/src/utils');
|
||||
|
||||
@@ -37,6 +34,7 @@ var async = require('async'),
|
||||
require('./user/approval')(User);
|
||||
require('./user/invite')(User);
|
||||
require('./user/icon')(User);
|
||||
require('./user/password')(User);
|
||||
|
||||
User.updateLastOnlineTime = function(uid, callback) {
|
||||
callback = callback || function() {};
|
||||
@@ -158,7 +156,7 @@ var async = require('async'),
|
||||
User.getUidByUserslug(userslug, function(err, exists) {
|
||||
callback(err, !! exists);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
User.getUidByUsername = function(username, callback) {
|
||||
if (!username) {
|
||||
@@ -224,6 +222,18 @@ var async = require('async'),
|
||||
privileges.users.isAdministrator(uid, callback);
|
||||
};
|
||||
|
||||
User.isAdminOrSelf = function(callerUid, uid, callback) {
|
||||
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
User.isAdministrator(callerUid, function(err, isAdmin) {
|
||||
if (err || !isAdmin) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
}(exports));
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ var async = require('async'),
|
||||
db = require('../database'),
|
||||
posts = require('../posts'),
|
||||
topics = require('../topics'),
|
||||
favourites = require('../favourites'),
|
||||
groups = require('../groups'),
|
||||
plugins = require('../plugins'),
|
||||
batch = require('../batch');
|
||||
@@ -21,12 +22,35 @@ module.exports = function(User) {
|
||||
function(next) {
|
||||
deleteTopics(uid, next);
|
||||
},
|
||||
function(next) {
|
||||
deleteVotes(uid, next);
|
||||
},
|
||||
function(next) {
|
||||
User.deleteAccount(uid, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
function deleteVotes(uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
upvotedPids: async.apply(db.getSortedSetRange, 'uid:' + uid + ':upvote', 0, -1),
|
||||
downvotedPids: async.apply(db.getSortedSetRange, 'uid:' + uid + ':downvote', 0, -1)
|
||||
}, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
pids = pids.upvotedPids.concat(pids.downvotedPids).filter(function(pid, index, array) {
|
||||
return pid && array.indexOf(pid) === index;
|
||||
});
|
||||
|
||||
async.eachLimit(pids, 50, function(pid, next) {
|
||||
favourites.unvote(pid, uid, next);
|
||||
}, next);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
function deletePosts(uid, callback) {
|
||||
deleteSortedSetElements('uid:' + uid + ':posts', posts.purge, callback);
|
||||
}
|
||||
@@ -125,20 +149,20 @@ module.exports = function(User) {
|
||||
};
|
||||
|
||||
function deleteUserIps(uid, callback) {
|
||||
db.getSortedSetRange('uid:' + uid + ':ip', 0, -1, function(err, ips) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('uid:' + uid + ':ip', 0, -1, next);
|
||||
},
|
||||
function (ips, next) {
|
||||
var keys = ips.map(function(ip) {
|
||||
return 'ip:' + ip + ':uid';
|
||||
});
|
||||
db.sortedSetsRemove(keys, uid, next);
|
||||
},
|
||||
function (next) {
|
||||
db.delete('uid:' + uid + ':ip', next);
|
||||
}
|
||||
|
||||
async.each(ips, function(ip, next) {
|
||||
db.sortedSetRemove('ip:' + ip + ':uid', uid, next);
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
db.delete('uid:' + uid + ':ip', callback);
|
||||
});
|
||||
});
|
||||
], callback);
|
||||
}
|
||||
|
||||
function deleteUserFromFollowers(uid, callback) {
|
||||
|
||||
@@ -21,7 +21,7 @@ var async = require('async'),
|
||||
if (!parseInt(uid, 10)) {
|
||||
return callback(null , {read: [], unread: []});
|
||||
}
|
||||
getNotifications(uid, 10, function(err, notifications) {
|
||||
getNotifications(uid, 0, 9, function(err, notifications) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -38,8 +38,8 @@ var async = require('async'),
|
||||
});
|
||||
};
|
||||
|
||||
UserNotifications.getAll = function(uid, count, callback) {
|
||||
getNotifications(uid, count, function(err, notifs) {
|
||||
UserNotifications.getAll = function(uid, start, stop, callback) {
|
||||
getNotifications(uid, start, stop, function(err, notifs) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -52,13 +52,13 @@ var async = require('async'),
|
||||
});
|
||||
};
|
||||
|
||||
function getNotifications(uid, count, callback) {
|
||||
function getNotifications(uid, start, stop, callback) {
|
||||
async.parallel({
|
||||
unread: function(next) {
|
||||
getNotificationsFromSet('uid:' + uid + ':notifications:unread', false, uid, 0, count - 1, next);
|
||||
getNotificationsFromSet('uid:' + uid + ':notifications:unread', false, uid, start, stop, next);
|
||||
},
|
||||
read: function(next) {
|
||||
getNotificationsFromSet('uid:' + uid + ':notifications:read', true, uid, 0, count - 1, next);
|
||||
getNotificationsFromSet('uid:' + uid + ':notifications:read', true, uid, start, stop, next);
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user