Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7955a5d53a | ||
|
|
c261babf17 | ||
|
|
b90eef6d19 | ||
|
|
5c597ca218 | ||
|
|
3dbcf8112d | ||
|
|
5357ad61db | ||
|
|
ff50917c29 | ||
|
|
48835d8c44 | ||
|
|
e9c66bb35a | ||
|
|
23eb7824ac | ||
|
|
494b9d23ac | ||
|
|
64ae9ac033 | ||
|
|
a72fc69997 | ||
|
|
a16f93cbd5 | ||
|
|
81e5cf0cf3 | ||
|
|
01102d5982 | ||
|
|
2174aec0e1 | ||
|
|
46bad118de | ||
|
|
2d7228fa40 | ||
|
|
a1839d90fd | ||
|
|
0cc136c3f6 | ||
|
|
cd1e26418d | ||
|
|
dab4f07258 | ||
|
|
501dc56fd3 | ||
|
|
253271127d | ||
|
|
f2da892b38 | ||
|
|
6dd72f480c | ||
|
|
3caf8b4a67 | ||
|
|
39f2efbef8 | ||
|
|
288341945d | ||
|
|
d02bd72764 | ||
|
|
7d3adb9275 | ||
|
|
156950ac2f | ||
|
|
83f18c1915 | ||
|
|
332730575f | ||
|
|
08ef67e824 | ||
|
|
7e71fb218c | ||
|
|
a7216caa3b | ||
|
|
87309601ce | ||
|
|
53db9db50f | ||
|
|
23628668b7 | ||
|
|
6ac685b194 | ||
|
|
db8c43ca97 | ||
|
|
bff0c8fdaf | ||
|
|
6f2b809385 | ||
|
|
455479bd54 | ||
|
|
03b34a449d | ||
|
|
08e51c8942 | ||
|
|
4aef5bfb72 | ||
|
|
34c74770ce | ||
|
|
da8d198676 | ||
|
|
33868804fd | ||
|
|
22a3794c51 | ||
|
|
1058d54c52 | ||
|
|
90ce539683 | ||
|
|
99c2fbd947 | ||
|
|
866d813218 | ||
|
|
f1df8c2479 | ||
|
|
6f1523c279 | ||
|
|
163cdaf70c | ||
|
|
a9ce8393e4 | ||
|
|
f04e30c4d4 | ||
|
|
9d0f8b4543 | ||
|
|
d631a4b2e5 | ||
|
|
2cf55dcf9f | ||
|
|
9fbb139e67 | ||
|
|
11e3b0da7d | ||
|
|
0b922d3f60 | ||
|
|
7e4faa3270 | ||
|
|
635fba1e45 | ||
|
|
7c950cc350 | ||
|
|
cc0fe66e3e | ||
|
|
b2d6ce59cf | ||
|
|
586a181e0a | ||
|
|
33150943df | ||
|
|
28dab60232 | ||
|
|
71ef76b108 | ||
|
|
64008ef5d8 | ||
|
|
1859154370 | ||
|
|
fa4067e885 | ||
|
|
c8c355b319 | ||
|
|
51355a53d9 | ||
|
|
1f3f672d3f | ||
|
|
7c3fa30c13 | ||
|
|
cbbb7a7c8e | ||
|
|
6893bd8b04 | ||
|
|
22eabf6620 | ||
|
|
a827888ee3 | ||
|
|
54d94f5988 | ||
|
|
7c1b6d6ad2 | ||
|
|
8c4f776122 | ||
|
|
84fa704b25 | ||
|
|
535379d9d7 | ||
|
|
d2927e2be2 | ||
|
|
bd04b2f921 |
3
.gitignore
vendored
@@ -12,3 +12,6 @@ public/css/*.css
|
|||||||
Vagrantfile
|
Vagrantfile
|
||||||
.vagrant
|
.vagrant
|
||||||
provision.sh
|
provision.sh
|
||||||
|
*.komodoproject
|
||||||
|
|
||||||
|
feeds/recent.rss
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## How can I follow along/contribute?
|
## How can I follow along/contribute?
|
||||||
|
|
||||||
|
|||||||
5
app.js
@@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/');
|
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/');
|
||||||
nconf.set('upload_url', nconf.get('url') + 'uploads/');
|
nconf.set('upload_url', nconf.get('url') + 'uploads/');
|
||||||
|
nconf.set('base_dir', __dirname);
|
||||||
|
|
||||||
winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
|
winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
|
||||||
winston.info('NodeBB instance bound to: ' + ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? 'Any address (0.0.0.0)' : nconf.get('bind_address')));
|
winston.info('NodeBB instance bound to: ' + ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? 'Any address (0.0.0.0)' : nconf.get('bind_address')));
|
||||||
@@ -112,7 +113,9 @@
|
|||||||
], customTemplates);
|
], customTemplates);
|
||||||
|
|
||||||
|
|
||||||
templates.ready(webserver.init);
|
plugins.ready(function() {
|
||||||
|
templates.ready(webserver.init);
|
||||||
|
});
|
||||||
|
|
||||||
Notifications.init();
|
Notifications.init();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
2
nodebb
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
clear
|
clear
|
||||||
echo "Launching NodeBB in \"development\" mode."
|
echo "Launching NodeBB in \"development\" mode."
|
||||||
echo "To run the production build of NodeBB, please use \"forever\"."
|
echo "To run the production build of NodeBB, please use \"forever\"."
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "nodebb",
|
"name": "nodebb",
|
||||||
"license": "GPLv3 or later",
|
"license": "GPLv3 or later",
|
||||||
"description": "NodeBB Forum",
|
"description": "NodeBB Forum",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"homepage": "http://www.nodebb.org",
|
"homepage": "http://www.nodebb.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"nodebb-plugin-mentions": "~0.1.14",
|
"nodebb-plugin-mentions": "~0.1.14",
|
||||||
"nodebb-plugin-markdown": "~0.1.8",
|
"nodebb-plugin-markdown": "~0.1.8",
|
||||||
"nodebb-theme-vanilla": "designcreateplay/nodebb-theme-vanilla",
|
"nodebb-theme-vanilla": "designcreateplay/nodebb-theme-vanilla",
|
||||||
"nodebb-theme-cerulean": "0.0.6",
|
"nodebb-theme-cerulean": "0.0.7",
|
||||||
"cron": "~1.0.1"
|
"cron": "~1.0.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var ajaxify = {};
|
"use strict";
|
||||||
|
|
||||||
|
var ajaxify = {};
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
/*global app, templates, utils*/
|
/*global app, templates, utils*/
|
||||||
@@ -23,8 +24,9 @@ var ajaxify = {};
|
|||||||
|
|
||||||
window.onpopstate = function (event) {
|
window.onpopstate = function (event) {
|
||||||
// "quiet": If set to true, will not call pushState
|
// "quiet": If set to true, will not call pushState
|
||||||
if (event !== null && event.state && event.state.url !== undefined)
|
if (event !== null && event.state && event.state.url !== undefined) {
|
||||||
ajaxify.go(event.state.url, null, null, true);
|
ajaxify.go(event.state.url, null, null, true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var pagination;
|
var pagination;
|
||||||
@@ -32,10 +34,13 @@ var ajaxify = {};
|
|||||||
ajaxify.go = function (url, callback, template, quiet) {
|
ajaxify.go = function (url, callback, template, quiet) {
|
||||||
// start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs
|
// start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs
|
||||||
$(window).off('scroll');
|
$(window).off('scroll');
|
||||||
app.enter_room('global');
|
app.enterRoom('global');
|
||||||
|
|
||||||
pagination = pagination || document.getElementById('pagination');
|
pagination = pagination || document.getElementById('pagination');
|
||||||
if (pagination) pagination.parentNode.style.display = 'none';
|
if (pagination) {
|
||||||
|
pagination.parentNode.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
window.onscroll = null;
|
window.onscroll = null;
|
||||||
// end
|
// end
|
||||||
|
|
||||||
@@ -62,13 +67,26 @@ var ajaxify = {};
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
|
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
|
||||||
if (quiet !== true) {
|
//if (quiet !== true) {
|
||||||
if (window.history && window.history.pushState) {
|
if (window.history && window.history.pushState) {
|
||||||
window.history.pushState({
|
window.history.pushState({
|
||||||
"url": url
|
url: url
|
||||||
}, url, RELATIVE_PATH + "/" + url);
|
}, url, RELATIVE_PATH + '/' + url);
|
||||||
|
|
||||||
|
$.ajax(RELATIVE_PATH + '/plugins/fireHook', {
|
||||||
|
type: 'PUT',
|
||||||
|
data: {
|
||||||
|
_csrf: $('#csrf_token').val(),
|
||||||
|
hook: 'page.load',
|
||||||
|
args: {
|
||||||
|
template: tpl_url,
|
||||||
|
url: url,
|
||||||
|
uid: app.uid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
//}
|
||||||
|
|
||||||
translator.load(tpl_url);
|
translator.load(tpl_url);
|
||||||
|
|
||||||
@@ -78,20 +96,27 @@ var ajaxify = {};
|
|||||||
templates.load_template(function () {
|
templates.load_template(function () {
|
||||||
exec_body_scripts(content);
|
exec_body_scripts(content);
|
||||||
require(['forum/' + tpl_url], function(script) {
|
require(['forum/' + tpl_url], function(script) {
|
||||||
if (script && script.init) script.init();
|
if (script && script.init) {
|
||||||
|
script.init();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.process_page();
|
app.processPage();
|
||||||
|
|
||||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
||||||
if (window.location.hash)
|
if (window.location.hash) {
|
||||||
hash = window.location.hash;
|
hash = window.location.hash;
|
||||||
if (hash)
|
}
|
||||||
app.scrollToPost(hash.substr(1));
|
|
||||||
|
if (hash) {
|
||||||
|
require(['forum/topic'], function(topic) {
|
||||||
|
topic.scrollToPost(hash.substr(1))
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
utils.refreshTitle(url);
|
utils.refreshTitle(url);
|
||||||
@@ -105,23 +130,27 @@ var ajaxify = {};
|
|||||||
};
|
};
|
||||||
|
|
||||||
$('document').ready(function () {
|
$('document').ready(function () {
|
||||||
if (!window.history || !window.history.pushState) return; // no ajaxification for old browsers
|
if (!window.history || !window.history.pushState) {
|
||||||
|
return; // no ajaxification for old browsers
|
||||||
|
}
|
||||||
|
|
||||||
content = content || document.getElementById('content');
|
content = content || document.getElementById('content');
|
||||||
|
|
||||||
// Enhancing all anchors to ajaxify...
|
// Enhancing all anchors to ajaxify...
|
||||||
$(document.body).on('click', 'a', function (e) {
|
$(document.body).on('click', 'a', function (e) {
|
||||||
function hrefEmpty(href) {
|
function hrefEmpty(href) {
|
||||||
return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
|
return href === 'javascript:;' || href === window.location.href + "#" || href.slice(-1) === "#";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:')
|
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:') {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!window.location.pathname.match(/\/(403|404)$/g))
|
if(!window.location.pathname.match(/\/(403|404)$/g)) {
|
||||||
app.previousUrl = window.location.href;
|
app.previousUrl = window.location.href;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.getAttribute('data-ajaxify') == 'false') {
|
if (this.getAttribute('data-ajaxify') === 'false') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ var socket,
|
|||||||
setTimeout(app.logout, 1000);
|
setTimeout(app.logout, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.enter_room('global');
|
app.enterRoom('global');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async: false
|
async: false
|
||||||
@@ -186,43 +186,35 @@ var socket,
|
|||||||
clearTimeout(alert.attr('timeoutId'));
|
clearTimeout(alert.attr('timeoutId'));
|
||||||
startTimeout(alert, params.timeout);
|
startTimeout(alert, params.timeout);
|
||||||
} else {
|
} else {
|
||||||
var div = document.createElement('div'),
|
var div = $('<div id="' + alert_id + '" class="alert toaster-alert alert-' + params.type +'"></div>'),
|
||||||
button = document.createElement('button'),
|
button = $('<button class="close">×</button>'),
|
||||||
strong = document.createElement('strong'),
|
strong = $('<strong>' + title + '</strong>'),
|
||||||
p = document.createElement('p');
|
p = $('<p>' + params.message + '</p>');
|
||||||
|
|
||||||
p.innerHTML = params.message;
|
div.append(button)
|
||||||
strong.innerHTML = title;
|
.append(strong)
|
||||||
|
.append(p);
|
||||||
|
|
||||||
div.className = "alert toaster-alert " + "alert-" + params.type;
|
button.on('click', function () {
|
||||||
|
div.remove();
|
||||||
div.setAttribute('id', alert_id);
|
});
|
||||||
div.appendChild(button);
|
|
||||||
div.appendChild(strong);
|
|
||||||
div.appendChild(p);
|
|
||||||
|
|
||||||
button.className = 'close';
|
|
||||||
button.innerHTML = '×';
|
|
||||||
button.onclick = function (ev) {
|
|
||||||
div.parentNode.removeChild(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.location == null)
|
if (params.location == null)
|
||||||
params.location = 'alert_window';
|
params.location = 'alert_window';
|
||||||
|
|
||||||
jQuery('#' + params.location).prepend(jQuery(div).fadeIn('100'));
|
$('#' + params.location).prepend(div.fadeIn('100'));
|
||||||
|
|
||||||
if (params.timeout) {
|
if (params.timeout) {
|
||||||
startTimeout(div, params.timeout);
|
startTimeout(div, params.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.clickfn) {
|
if (params.clickfn) {
|
||||||
div.onclick = function () {
|
div.on('click', function () {
|
||||||
params.clickfn();
|
params.clickfn();
|
||||||
jQuery(div).fadeOut(500, function () {
|
div.fadeOut(500, function () {
|
||||||
this.remove();
|
$(this).remove();
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,22 +243,23 @@ var socket,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.current_room = null;
|
app.currentRoom = null;
|
||||||
app.enter_room = function (room) {
|
app.enterRoom = function (room) {
|
||||||
if (socket) {
|
if (socket) {
|
||||||
if (app.current_room === room)
|
if (app.currentRoom === room) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
socket.emit('event:enter_room', {
|
socket.emit('event:enter_room', {
|
||||||
'enter': room,
|
'enter': room,
|
||||||
'leave': app.current_room
|
'leave': app.currentRoom
|
||||||
});
|
});
|
||||||
|
|
||||||
app.current_room = room;
|
app.currentRoom = room;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
app.populate_online_users = function () {
|
app.populateOnlineUsers = function () {
|
||||||
var uids = [];
|
var uids = [];
|
||||||
|
|
||||||
jQuery('.post-row').each(function () {
|
jQuery('.post-row').each(function () {
|
||||||
@@ -276,9 +269,7 @@ var socket,
|
|||||||
socket.emit('api:user.get_online_users', uids);
|
socket.emit('api:user.get_online_users', uids);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.process_page = function () {
|
function highlightNavigationLink() {
|
||||||
app.populate_online_users();
|
|
||||||
|
|
||||||
var path = window.location.pathname,
|
var path = window.location.pathname,
|
||||||
parts = path.split('/'),
|
parts = path.split('/'),
|
||||||
active = parts[parts.length - 1];
|
active = parts[parts.length - 1];
|
||||||
@@ -295,10 +286,27 @@ var socket,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.createUserTooltips = function() {
|
||||||
|
$('img[title].teaser-pic,img[title].user-img').each(function() {
|
||||||
|
$(this).tooltip({
|
||||||
|
placement: 'top',
|
||||||
|
title: $(this).attr('title')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.processPage = function () {
|
||||||
|
app.populateOnlineUsers();
|
||||||
|
|
||||||
|
highlightNavigationLink();
|
||||||
|
|
||||||
$('span.timeago').timeago();
|
$('span.timeago').timeago();
|
||||||
$('.post-content img').addClass('img-responsive');
|
$('.post-content img').addClass('img-responsive');
|
||||||
|
|
||||||
|
app.createUserTooltips();
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
|
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -331,6 +339,28 @@ var socket,
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.openChat = function (username, touid) {
|
app.openChat = function (username, touid) {
|
||||||
|
if (username === app.username) {
|
||||||
|
app.alert({
|
||||||
|
type: 'warning',
|
||||||
|
title: 'Invalid Chat',
|
||||||
|
message: "You can't chat with yourself!",
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!app.username) {
|
||||||
|
app.alert({
|
||||||
|
type: 'danger',
|
||||||
|
title: 'Not Logged In',
|
||||||
|
message: 'Please log in to chat with <strong>' + username + '</strong>',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
require(['chat'], function (chat) {
|
require(['chat'], function (chat) {
|
||||||
var chatModal;
|
var chatModal;
|
||||||
if (!chat.modalExists(touid)) {
|
if (!chat.modalExists(touid)) {
|
||||||
@@ -343,61 +373,6 @@ var socket,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.createNewPosts = function (data) {
|
|
||||||
if (data.posts[0].uid !== app.uid) {
|
|
||||||
data.posts[0].display_moderator_tools = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
|
|
||||||
translator.translate(html, function(translatedHTML) {
|
|
||||||
var uniqueid = new Date().getTime(),
|
|
||||||
tempContainer = jQuery('<div id="' + uniqueid + '"></div>')
|
|
||||||
.appendTo("#post-container")
|
|
||||||
.hide()
|
|
||||||
.append(translatedHTML)
|
|
||||||
.fadeIn('slow');
|
|
||||||
|
|
||||||
for (var x = 0, numPosts = data.posts.length; x < numPosts; x++) {
|
|
||||||
socket.emit('api:post.privileges', data.posts[x].pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
tempContainer.replaceWith(tempContainer.contents());
|
|
||||||
infiniteLoaderActive = false;
|
|
||||||
|
|
||||||
app.populate_online_users();
|
|
||||||
app.addCommasToNumbers();
|
|
||||||
$('span.timeago').timeago();
|
|
||||||
$('.post-content img').addClass('img-responsive');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
app.infiniteLoaderActive = false;
|
|
||||||
|
|
||||||
app.loadMorePosts = function (tid, callback) {
|
|
||||||
if (app.infiniteLoaderActive)
|
|
||||||
return;
|
|
||||||
app.infiniteLoaderActive = true;
|
|
||||||
|
|
||||||
if ($('#loading-indicator').attr('done') === '0')
|
|
||||||
$('#loading-indicator').removeClass('hide');
|
|
||||||
|
|
||||||
socket.emit('api:topic.loadMore', {
|
|
||||||
tid: tid,
|
|
||||||
after: document.querySelectorAll('#post-container li[data-pid]').length
|
|
||||||
}, function (data) {
|
|
||||||
app.infiniteLoaderActive = false;
|
|
||||||
if (data.posts.length) {
|
|
||||||
$('#loading-indicator').attr('done', '0');
|
|
||||||
app.createNewPosts(data);
|
|
||||||
} else {
|
|
||||||
$('#loading-indicator').attr('done', '1');
|
|
||||||
}
|
|
||||||
$('#loading-indicator').addClass('hide');
|
|
||||||
if (callback)
|
|
||||||
callback(data.posts);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
app.scrollToTop = function () {
|
app.scrollToTop = function () {
|
||||||
$('body,html').animate({
|
$('body,html').animate({
|
||||||
scrollTop: 0
|
scrollTop: 0
|
||||||
@@ -410,43 +385,6 @@ var socket,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.scrollToPost = function (pid) {
|
|
||||||
|
|
||||||
if (!pid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var container = $(document.body),
|
|
||||||
scrollTo = $('#post_anchor_' + pid),
|
|
||||||
tid = $('#post-container').attr('data-tid');
|
|
||||||
|
|
||||||
function animateScroll() {
|
|
||||||
$('body,html').animate({
|
|
||||||
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height()
|
|
||||||
}, 400);
|
|
||||||
//$('body,html').scrollTop(scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scrollTo.length && tid) {
|
|
||||||
|
|
||||||
var intervalID = setInterval(function () {
|
|
||||||
app.loadMorePosts(tid, function (posts) {
|
|
||||||
scrollTo = $('#post_anchor_' + pid);
|
|
||||||
|
|
||||||
if (tid && scrollTo.length) {
|
|
||||||
animateScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!posts.length || scrollTo.length)
|
|
||||||
clearInterval(intervalID);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
} else if (tid) {
|
|
||||||
animateScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery('document').ready(function () {
|
jQuery('document').ready(function () {
|
||||||
$('#search-form').on('submit', function () {
|
$('#search-form').on('submit', function () {
|
||||||
var input = $(this).find('input');
|
var input = $(this).find('input');
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ define(['forum/accountheader'], function(header) {
|
|||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var username = $('.account-username a').html();
|
var username = $('.account-username a').html();
|
||||||
app.enter_room('user/' + theirid);
|
app.enterRoom('user/' + theirid);
|
||||||
|
|
||||||
app.addCommasToNumbers();
|
app.addCommasToNumbers();
|
||||||
|
$('.user-recent-posts img').addClass('img-responsive');
|
||||||
|
|
||||||
var followBtn = $('#follow-btn');
|
var followBtn = $('#follow-btn');
|
||||||
var unfollowBtn = $('#unfollow-btn');
|
var unfollowBtn = $('#unfollow-btn');
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ define(function() {
|
|||||||
update_blockclass(ev.target);
|
update_blockclass(ev.target);
|
||||||
});
|
});
|
||||||
|
|
||||||
jQuery('.category_name, .category_description, .blockclass').on('change', function(ev) {
|
jQuery('.category_name, .category_description, .blockclass .category_bgColor').on('change', function(ev) {
|
||||||
modified(ev.target);
|
modified(ev.target);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -156,6 +156,20 @@ define(function() {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Colour Picker
|
||||||
|
$('[data-name="bgColor"], [data-name="color"]').each(function(idx, inputEl) {
|
||||||
|
var jinputEl = $(this),
|
||||||
|
parentEl = jinputEl.parents('[data-cid]');
|
||||||
|
jinputEl.ColorPicker({
|
||||||
|
color: this.value || '#000',
|
||||||
|
onChange: function(hsb, hex) {
|
||||||
|
jinputEl.val('#' + hex);
|
||||||
|
if (inputEl.getAttribute('data-name') === 'bgColor') parentEl.css('background', '#' + hex);
|
||||||
|
else if (inputEl.getAttribute('data-name') === 'color') parentEl.css('color', '#' + hex);
|
||||||
|
modified(inputEl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,12 @@ define(function() {
|
|||||||
|
|
||||||
errorEl.html(errorText).removeClass('hide');
|
errorEl.html(errorText).removeClass('hide');
|
||||||
} else {
|
} else {
|
||||||
createModal.modal('hide');
|
|
||||||
errorEl.addClass('hide');
|
errorEl.addClass('hide');
|
||||||
createNameEl.val('');
|
createNameEl.val('');
|
||||||
ajaxify.go('admin/groups');
|
createModal.on('hidden.bs.modal', function() {
|
||||||
|
ajaxify.go('admin/groups');
|
||||||
|
});
|
||||||
|
createModal.modal('hide');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ define(function() {
|
|||||||
document.getElementById('connections').innerHTML = total;
|
document.getElementById('connections').innerHTML = total;
|
||||||
});
|
});
|
||||||
|
|
||||||
app.enter_room('admin');
|
app.enterRoom('admin');
|
||||||
socket.emit('api:get_all_rooms');
|
socket.emit('api:get_all_rooms');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,13 @@ define(function() {
|
|||||||
|
|
||||||
loadMoreEl.addEventListener('click', function() {
|
loadMoreEl.addEventListener('click', function() {
|
||||||
if (this.className.indexOf('disabled') === -1) {
|
if (this.className.indexOf('disabled') === -1) {
|
||||||
var topics = document.querySelectorAll('.topics li[data-tid]'),
|
var topics = document.querySelectorAll('.topics li[data-tid]');
|
||||||
lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
|
||||||
|
if(!topics.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
||||||
|
|
||||||
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics';
|
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics';
|
||||||
socket.emit('api:admin.topics.getMore', {
|
socket.emit('api:admin.topics.getMore', {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ define(function () {
|
|||||||
|
|
||||||
Category.init = function() {
|
Category.init = function() {
|
||||||
var cid = templates.get('category_id'),
|
var cid = templates.get('category_id'),
|
||||||
room = 'category_' + cid,
|
|
||||||
twitterEl = jQuery('#twitter-intent'),
|
twitterEl = jQuery('#twitter-intent'),
|
||||||
facebookEl = jQuery('#facebook-share'),
|
facebookEl = jQuery('#facebook-share'),
|
||||||
googleEl = jQuery('#google-share'),
|
googleEl = jQuery('#google-share'),
|
||||||
@@ -12,7 +11,7 @@ define(function () {
|
|||||||
google_url = templates.get('google-share-url'),
|
google_url = templates.get('google-share-url'),
|
||||||
loadingMoreTopics = false;
|
loadingMoreTopics = false;
|
||||||
|
|
||||||
app.enter_room(room);
|
app.enterRoom('category_' + cid);
|
||||||
|
|
||||||
twitterEl.on('click', function () {
|
twitterEl.on('click', function () {
|
||||||
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
||||||
@@ -56,7 +55,7 @@ define(function () {
|
|||||||
li.setAttribute('data-pid', posts[i].pid);
|
li.setAttribute('data-pid', posts[i].pid);
|
||||||
|
|
||||||
|
|
||||||
li.innerHTML = '<a href="/user/' + posts[i].userslug + '"><img title="' + posts[i].username + '" style="width: 48px; height: 48px; /*temporary*/" class="img-rounded" src="' + posts[i].picture + '" class="" /></a>' +
|
li.innerHTML = '<a href="/user/' + posts[i].userslug + '"><img title="' + posts[i].username + '" style="width: 48px; height: 48px; /*temporary*/" class="img-rounded user-img" src="' + posts[i].picture + '" class="" /></a>' +
|
||||||
'<a href="/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">' +
|
'<a href="/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">' +
|
||||||
'<strong><span>'+ posts[i].username + '</span></strong>' +
|
'<strong><span>'+ posts[i].username + '</span></strong>' +
|
||||||
'<p>' +
|
'<p>' +
|
||||||
@@ -69,6 +68,7 @@ define(function () {
|
|||||||
recent_replies.appendChild(frag);
|
recent_replies.appendChild(frag);
|
||||||
}
|
}
|
||||||
$('#category_recent_replies span.timeago').timeago();
|
$('#category_recent_replies span.timeago').timeago();
|
||||||
|
app.createUserTooltips();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).off('scroll').on('scroll', function (ev) {
|
$(window).off('scroll').on('scroll', function (ev) {
|
||||||
@@ -94,17 +94,20 @@ define(function () {
|
|||||||
|
|
||||||
if (numTopics > 0) {
|
if (numTopics > 0) {
|
||||||
for (var x = 0; x < numTopics; x++) {
|
for (var x = 0; x < numTopics; x++) {
|
||||||
if ($(topics[x]).find('.icon-pushpin').length)
|
if ($(topics[x]).find('.icon-pushpin').length) {
|
||||||
|
if(x === numTopics - 1) {
|
||||||
|
topic.insertAfter(topics[x]);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
topic.insertBefore(topics[x]);
|
topic.insertBefore(topics[x]);
|
||||||
topic.hide().fadeIn('slow');
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
container.append(topic);
|
container.append(topic);
|
||||||
topic.hide().fadeIn('slow');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topic.hide().fadeIn('slow');
|
||||||
socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
|
socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
|
||||||
|
|
||||||
addActiveUser(data);
|
addActiveUser(data);
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ define(function() {
|
|||||||
} else {
|
} else {
|
||||||
$('#login-error-notify').hide();
|
$('#login-error-notify').hide();
|
||||||
|
|
||||||
if(!app.previousUrl) { app.previousUrl = '/'; }
|
if(!app.previousUrl) {
|
||||||
|
app.previousUrl = '/';
|
||||||
|
}
|
||||||
|
|
||||||
if(app.previousUrl.indexOf('/reset/') != -1)
|
if(app.previousUrl.indexOf('/reset/') != -1)
|
||||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||||
@@ -63,6 +65,12 @@ define(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#content input').focus();
|
document.querySelector('#content input').focus();
|
||||||
|
|
||||||
|
if(!config.emailSetup)
|
||||||
|
$('#reset-link').addClass('hide');
|
||||||
|
else
|
||||||
|
$('#reset-link').removeClass('hide');
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Login;
|
return Login;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ define(function() {
|
|||||||
var active = '';
|
var active = '';
|
||||||
|
|
||||||
Recent.init = function() {
|
Recent.init = function() {
|
||||||
app.enter_room('recent_posts');
|
app.enterRoom('recent_posts');
|
||||||
|
|
||||||
ajaxify.register_events([
|
ajaxify.register_events([
|
||||||
'event:new_topic',
|
'event:new_topic',
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
define(function() {
|
define(function() {
|
||||||
var Topic = {};
|
var Topic = {},
|
||||||
|
infiniteLoaderActive = false;
|
||||||
|
|
||||||
|
|
||||||
Topic.init = function() {
|
Topic.init = function() {
|
||||||
var expose_tools = templates.get('expose_tools'),
|
var expose_tools = templates.get('expose_tools'),
|
||||||
@@ -17,16 +19,26 @@ define(function() {
|
|||||||
google_url = templates.get('google-share-url');
|
google_url = templates.get('google-share-url');
|
||||||
|
|
||||||
|
|
||||||
|
function fixDeleteStateForPosts() {
|
||||||
|
var postEls = document.querySelectorAll('#post-container li[data-deleted]');
|
||||||
|
for (var x = 0, numPosts = postEls.length; x < numPosts; x++) {
|
||||||
|
if (postEls[x].getAttribute('data-deleted') === '1') {
|
||||||
|
toggle_post_delete_state(postEls[x].getAttribute('data-pid'));
|
||||||
|
}
|
||||||
|
postEls[x].removeAttribute('data-deleted');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
jQuery('document').ready(function() {
|
jQuery('document').ready(function() {
|
||||||
|
|
||||||
app.addCommasToNumbers();
|
app.addCommasToNumbers();
|
||||||
|
|
||||||
|
app.enterRoom('topic_' + tid);
|
||||||
|
|
||||||
var room = 'topic_' + tid,
|
if($('#post-container .sub-posts').length) {
|
||||||
adminTools = document.getElementById('thread-tools');
|
$('.topic-main-buttons').removeClass('hide').parent().removeClass('hide');
|
||||||
|
}
|
||||||
app.enter_room(room);
|
|
||||||
|
|
||||||
|
|
||||||
$('.twitter-share').on('click', function () {
|
$('.twitter-share').on('click', function () {
|
||||||
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
||||||
@@ -50,11 +62,10 @@ define(function() {
|
|||||||
|
|
||||||
if (expose_tools === '1') {
|
if (expose_tools === '1') {
|
||||||
var moveThreadModal = $('#move_thread_modal');
|
var moveThreadModal = $('#move_thread_modal');
|
||||||
|
$('.thread-tools').removeClass('hide');
|
||||||
adminTools.style.visibility = 'inherit';
|
|
||||||
|
|
||||||
// Add events to the thread tools
|
// Add events to the thread tools
|
||||||
$('#delete_thread').on('click', function(e) {
|
$('.delete_thread').on('click', function(e) {
|
||||||
if (thread_state.deleted !== '1') {
|
if (thread_state.deleted !== '1') {
|
||||||
bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) {
|
bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
@@ -73,7 +84,7 @@ define(function() {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#lock_thread').on('click', function(e) {
|
$('.lock_thread').on('click', function(e) {
|
||||||
if (thread_state.locked !== '1') {
|
if (thread_state.locked !== '1') {
|
||||||
socket.emit('api:topic.lock', {
|
socket.emit('api:topic.lock', {
|
||||||
tid: tid
|
tid: tid
|
||||||
@@ -86,7 +97,7 @@ define(function() {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#pin_thread').on('click', function(e) {
|
$('.pin_thread').on('click', function(e) {
|
||||||
if (thread_state.pinned !== '1') {
|
if (thread_state.pinned !== '1') {
|
||||||
socket.emit('api:topic.pin', {
|
socket.emit('api:topic.pin', {
|
||||||
tid: tid
|
tid: tid
|
||||||
@@ -99,7 +110,7 @@ define(function() {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#move_thread').on('click', function(e) {
|
$('.move_thread').on('click', function(e) {
|
||||||
moveThreadModal.modal('show');
|
moveThreadModal.modal('show');
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@@ -109,7 +120,6 @@ define(function() {
|
|||||||
var loadingEl = document.getElementById('categories-loading');
|
var loadingEl = document.getElementById('categories-loading');
|
||||||
if (loadingEl) {
|
if (loadingEl) {
|
||||||
socket.once('api:categories.get', function(data) {
|
socket.once('api:categories.get', function(data) {
|
||||||
console.log(data);
|
|
||||||
// Render categories
|
// Render categories
|
||||||
var categoriesFrag = document.createDocumentFragment(),
|
var categoriesFrag = document.createDocumentFragment(),
|
||||||
categoryEl = document.createElement('li'),
|
categoryEl = document.createElement('li'),
|
||||||
@@ -182,12 +192,8 @@ define(function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix delete state for this thread's posts
|
fixDeleteStateForPosts();
|
||||||
var postEls = document.querySelectorAll('#post-container li[data-deleted]');
|
|
||||||
for (var x = 0, numPosts = postEls.length; x < numPosts; x++) {
|
|
||||||
if (postEls[x].getAttribute('data-deleted') === '1') toggle_post_delete_state(postEls[x].getAttribute('data-pid'));
|
|
||||||
postEls[x].removeAttribute('data-deleted');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Follow Thread State
|
// Follow Thread State
|
||||||
var followEl = $('.main-post .follow'),
|
var followEl = $('.main-post .follow'),
|
||||||
@@ -246,7 +252,7 @@ define(function() {
|
|||||||
var bookmark = localStorage.getItem('topic:' + tid + ':bookmark');
|
var bookmark = localStorage.getItem('topic:' + tid + ':bookmark');
|
||||||
|
|
||||||
if(bookmark) {
|
if(bookmark) {
|
||||||
app.scrollToPost(parseInt(bookmark, 10));
|
Topic.scrollToPost(parseInt(bookmark, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#post-container').on('click', '.deleted', function(ev) {
|
$('#post-container').on('click', '.deleted', function(ev) {
|
||||||
@@ -258,13 +264,15 @@ define(function() {
|
|||||||
$(window).off('scroll').on('scroll', function() {
|
$(window).off('scroll').on('scroll', function() {
|
||||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||||
|
|
||||||
if ($(window).scrollTop() > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
|
if ($(window).scrollTop() > bottom && !infiniteLoaderActive && $('#post-container').children().length) {
|
||||||
app.loadMorePosts(tid);
|
loadMorePosts(tid, function(posts) {
|
||||||
|
fixDeleteStateForPosts();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var reply_fn = function() {
|
$('.topic').on('click', '.post_reply', function() {
|
||||||
var selectionText = '',
|
var selectionText = '',
|
||||||
selection = window.getSelection() || document.getSelection();
|
selection = window.getSelection() || document.getSelection();
|
||||||
|
|
||||||
@@ -278,9 +286,7 @@ define(function() {
|
|||||||
cmp.push(tid, null, null, selectionText.length > 0 ? selectionText + '\n\n' : '');
|
cmp.push(tid, null, null, selectionText.length > 0 ? selectionText + '\n\n' : '');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
$('#post-container').on('click', '.post_reply', reply_fn);
|
|
||||||
$('#post_reply').on('click', reply_fn);
|
|
||||||
|
|
||||||
$('#post-container').on('click', '.quote', function() {
|
$('#post-container').on('click', '.quote', function() {
|
||||||
if (thread_state.locked !== '1') {
|
if (thread_state.locked !== '1') {
|
||||||
@@ -307,12 +313,12 @@ define(function() {
|
|||||||
if (element.attr('class') == 'icon-star-empty') {
|
if (element.attr('class') == 'icon-star-empty') {
|
||||||
socket.emit('api:posts.favourite', {
|
socket.emit('api:posts.favourite', {
|
||||||
pid: pid,
|
pid: pid,
|
||||||
room_id: app.current_room
|
room_id: app.currentRoom
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
socket.emit('api:posts.unfavourite', {
|
socket.emit('api:posts.unfavourite', {
|
||||||
pid: pid,
|
pid: pid,
|
||||||
room_id: app.current_room
|
room_id: app.currentRoom
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -341,13 +347,25 @@ define(function() {
|
|||||||
confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
|
confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
|
||||||
|
|
||||||
if (confirmDel) {
|
if (confirmDel) {
|
||||||
deleteAction ?
|
if(deleteAction) {
|
||||||
socket.emit('api:posts.delete', {
|
socket.emit('api:posts.delete', {
|
||||||
pid: pid
|
pid: pid,
|
||||||
}) :
|
tid: tid
|
||||||
socket.emit('api:posts.restore', {
|
}, function(err) {
|
||||||
pid: pid
|
if(err) {
|
||||||
|
return app.alertError('Can\'t delete post!');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
socket.emit('api:posts.restore', {
|
||||||
|
pid: pid,
|
||||||
|
tid: tid
|
||||||
|
}, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return app.alertError('Can\'t restore post!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -355,9 +373,6 @@ define(function() {
|
|||||||
var username = $(this).parents('li.row').attr('data-username');
|
var username = $(this).parents('li.row').attr('data-username');
|
||||||
var touid = $(this).parents('li.row').attr('data-uid');
|
var touid = $(this).parents('li.row').attr('data-uid');
|
||||||
|
|
||||||
if (username === app.username || !app.username)
|
|
||||||
return;
|
|
||||||
|
|
||||||
app.openChat(username, touid);
|
app.openChat(username, touid);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -381,12 +396,15 @@ define(function() {
|
|||||||
var userLink = $('<a href="/user/' + userslug + '"></a>').append(userIcon);
|
var userLink = $('<a href="/user/' + userslug + '"></a>').append(userIcon);
|
||||||
userLink.attr('data-uid', uid);
|
userLink.attr('data-uid', uid);
|
||||||
|
|
||||||
|
var div = $('<div class="inline-block"></div>');
|
||||||
|
div.append(userLink);
|
||||||
|
|
||||||
userLink.tooltip({
|
userLink.tooltip({
|
||||||
placement: 'left',
|
placement: 'top',
|
||||||
title: username
|
title: username
|
||||||
});
|
});
|
||||||
|
|
||||||
return userLink;
|
return div;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +446,7 @@ define(function() {
|
|||||||
activeEl.find('.anonymous-box').remove();
|
activeEl.find('.anonymous-box').remove();
|
||||||
if(anonymousCount || remainingUsers) {
|
if(anonymousCount || remainingUsers) {
|
||||||
|
|
||||||
var anonLink = $('<i class="icon-user anonymous-box"></i>');
|
var anonLink = $('<div class="anonymous-box inline-block"><i class="icon-user"></i></div>');
|
||||||
activeEl.append(anonLink);
|
activeEl.append(anonLink);
|
||||||
|
|
||||||
var title = '';
|
var title = '';
|
||||||
@@ -440,12 +458,12 @@ define(function() {
|
|||||||
title = anonymousCount + ' guest(s)';
|
title = anonymousCount + ' guest(s)';
|
||||||
|
|
||||||
anonLink.tooltip({
|
anonLink.tooltip({
|
||||||
placement: 'left',
|
placement: 'top',
|
||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.populate_online_users();
|
app.populateOnlineUsers();
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('event:rep_up', function(data) {
|
socket.on('event:rep_up', function(data) {
|
||||||
@@ -456,7 +474,7 @@ define(function() {
|
|||||||
adjust_rep(-1, data.pid, data.uid);
|
adjust_rep(-1, data.pid, data.uid);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('event:new_post', app.createNewPosts);
|
socket.on('event:new_post', createNewPosts);
|
||||||
|
|
||||||
socket.on('event:topic_deleted', function(data) {
|
socket.on('event:topic_deleted', function(data) {
|
||||||
if (data.tid === tid && data.status === 'ok') {
|
if (data.tid === tid && data.status === 'ok') {
|
||||||
@@ -540,11 +558,15 @@ define(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('event:post_deleted', function(data) {
|
socket.on('event:post_deleted', function(data) {
|
||||||
if (data.pid) toggle_post_delete_state(data.pid, true);
|
if (data.pid) {
|
||||||
|
toggle_post_delete_state(data.pid);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('event:post_restored', function(data) {
|
socket.on('event:post_restored', function(data) {
|
||||||
if (data.pid) toggle_post_delete_state(data.pid, true);
|
if (data.pid) {
|
||||||
|
toggle_post_delete_state(data.pid);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('api:post.privileges', function(privileges) {
|
socket.on('api:post.privileges', function(privileges) {
|
||||||
@@ -566,19 +588,19 @@ define(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function set_locked_state(locked, alert) {
|
function set_locked_state(locked, alert) {
|
||||||
var threadReplyBtn = document.getElementById('post_reply'),
|
var threadReplyBtn = $('.topic-main-buttons .post_reply'),
|
||||||
postReplyBtns = document.querySelectorAll('#post-container .post_reply'),
|
postReplyBtns = document.querySelectorAll('#post-container .post_reply'),
|
||||||
quoteBtns = document.querySelectorAll('#post-container .quote'),
|
quoteBtns = document.querySelectorAll('#post-container .quote'),
|
||||||
editBtns = document.querySelectorAll('#post-container .edit'),
|
editBtns = document.querySelectorAll('#post-container .edit'),
|
||||||
deleteBtns = document.querySelectorAll('#post-container .delete'),
|
deleteBtns = document.querySelectorAll('#post-container .delete'),
|
||||||
numPosts = document.querySelectorAll('#post_container li[data-pid]').length,
|
numPosts = document.querySelectorAll('#post_container li[data-pid]').length,
|
||||||
lockThreadEl = document.getElementById('lock_thread'),
|
lockThreadEl = $('.lock_thread'),
|
||||||
x;
|
x;
|
||||||
|
|
||||||
if (locked === true) {
|
if (locked === true) {
|
||||||
lockThreadEl.innerHTML = '<i class="icon-unlock"></i> Unlock Thread';
|
lockThreadEl.html('<i class="icon-unlock"></i> Unlock Thread');
|
||||||
threadReplyBtn.disabled = true;
|
threadReplyBtn.attr('disabled', true);
|
||||||
threadReplyBtn.innerHTML = 'Locked <i class="icon-lock"></i>';
|
threadReplyBtn.html('Locked <i class="icon-lock"></i>');
|
||||||
for (x = 0; x < numPosts; x++) {
|
for (x = 0; x < numPosts; x++) {
|
||||||
postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>';
|
postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>';
|
||||||
quoteBtns[x].style.display = 'none';
|
quoteBtns[x].style.display = 'none';
|
||||||
@@ -598,9 +620,9 @@ define(function() {
|
|||||||
|
|
||||||
thread_state.locked = '1';
|
thread_state.locked = '1';
|
||||||
} else {
|
} else {
|
||||||
lockThreadEl.innerHTML = '<i class="icon-lock"></i> Lock Thread';
|
lockThreadEl.html('<i class="icon-lock"></i> Lock Thread');
|
||||||
threadReplyBtn.disabled = false;
|
threadReplyBtn.attr('disabled', false);
|
||||||
threadReplyBtn.innerHTML = 'Reply';
|
threadReplyBtn.html('Reply');
|
||||||
for (x = 0; x < numPosts; x++) {
|
for (x = 0; x < numPosts; x++) {
|
||||||
postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>';
|
postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>';
|
||||||
quoteBtns[x].style.display = 'inline-block';
|
quoteBtns[x].style.display = 'inline-block';
|
||||||
@@ -623,13 +645,14 @@ define(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function set_delete_state(deleted) {
|
function set_delete_state(deleted) {
|
||||||
var deleteThreadEl = document.getElementById('delete_thread'),
|
var deleteThreadEl = $('.delete_thread'),
|
||||||
deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0],
|
deleteTextEl = $('.delete_thread span'),
|
||||||
|
//deleteThreadEl.getElementsByTagName('span')[0],
|
||||||
threadEl = $('#post-container'),
|
threadEl = $('#post-container'),
|
||||||
deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
|
deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
|
||||||
|
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
deleteTextEl.innerHTML = '<i class="icon-comment"></i> Restore Thread';
|
deleteTextEl.html('<i class="icon-comment"></i> Restore Thread');
|
||||||
threadEl.addClass('deleted');
|
threadEl.addClass('deleted');
|
||||||
|
|
||||||
// Spawn a 'deleted' notice at the top of the page
|
// Spawn a 'deleted' notice at the top of the page
|
||||||
@@ -640,7 +663,7 @@ define(function() {
|
|||||||
|
|
||||||
thread_state.deleted = '1';
|
thread_state.deleted = '1';
|
||||||
} else {
|
} else {
|
||||||
deleteTextEl.innerHTML = '<i class="icon-trash"></i> Delete Thread';
|
deleteTextEl.html('<i class="icon-trash"></i> Delete Thread');
|
||||||
threadEl.removeClass('deleted');
|
threadEl.removeClass('deleted');
|
||||||
deleteNotice.parentNode.removeChild(deleteNotice);
|
deleteNotice.parentNode.removeChild(deleteNotice);
|
||||||
|
|
||||||
@@ -649,10 +672,10 @@ define(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function set_pinned_state(pinned, alert) {
|
function set_pinned_state(pinned, alert) {
|
||||||
var pinEl = document.getElementById('pin_thread');
|
var pinEl = $('.pin_thread');
|
||||||
|
|
||||||
if (pinned) {
|
if (pinned) {
|
||||||
pinEl.innerHTML = '<i class="icon-pushpin"></i> Unpin Thread';
|
pinEl.html('<i class="icon-pushpin"></i> Unpin Thread');
|
||||||
if (alert) {
|
if (alert) {
|
||||||
app.alert({
|
app.alert({
|
||||||
'alert_id': 'thread_pin',
|
'alert_id': 'thread_pin',
|
||||||
@@ -665,7 +688,7 @@ define(function() {
|
|||||||
|
|
||||||
thread_state.pinned = '1';
|
thread_state.pinned = '1';
|
||||||
} else {
|
} else {
|
||||||
pinEl.innerHTML = '<i class="icon-pushpin"></i> Pin Thread';
|
pinEl.html('<i class="icon-pushpin"></i> Pin Thread');
|
||||||
if (alert) {
|
if (alert) {
|
||||||
app.alert({
|
app.alert({
|
||||||
'alert_id': 'thread_pin',
|
'alert_id': 'thread_pin',
|
||||||
@@ -702,6 +725,7 @@ define(function() {
|
|||||||
} else {
|
} else {
|
||||||
postEl.toggleClass('none');
|
postEl.toggleClass('none');
|
||||||
}
|
}
|
||||||
|
updatePostCount();
|
||||||
});
|
});
|
||||||
socket.emit('api:post.privileges', pid);
|
socket.emit('api:post.privileges', pid);
|
||||||
}
|
}
|
||||||
@@ -811,5 +835,138 @@ define(function() {
|
|||||||
window.onload = updateHeader;
|
window.onload = updateHeader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Topic.scrollToPost = function(pid) {
|
||||||
|
if (!pid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = $(document.body),
|
||||||
|
scrollTo = $('#post_anchor_' + pid),
|
||||||
|
tid = $('#post-container').attr('data-tid');
|
||||||
|
|
||||||
|
function animateScroll() {
|
||||||
|
$('body,html').animate({
|
||||||
|
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height()
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scrollTo.length && tid) {
|
||||||
|
|
||||||
|
var intervalID = setInterval(function () {
|
||||||
|
loadMorePosts(tid, function (posts) {
|
||||||
|
scrollTo = $('#post_anchor_' + pid);
|
||||||
|
|
||||||
|
if (tid && scrollTo.length) {
|
||||||
|
animateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!posts.length || scrollTo.length)
|
||||||
|
clearInterval(intervalID);
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
} else if (tid) {
|
||||||
|
animateScroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewPosts(data, infiniteLoaded) {
|
||||||
|
if(!data || (data.posts && !data.posts.length))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (data.posts[0].uid !== app.uid) {
|
||||||
|
data.posts[0].display_moderator_tools = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAlreadyAddedPosts() {
|
||||||
|
data.posts = data.posts.filter(function(post) {
|
||||||
|
return $('#post-container li[data-pid="' + post.pid +'"]').length === 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findInsertionPoint() {
|
||||||
|
var after = null,
|
||||||
|
firstPid = data.posts[0].pid;
|
||||||
|
$('#post-container li[data-pid]').each(function() {
|
||||||
|
if(parseInt(firstPid, 10) > parseInt($(this).attr('data-pid'), 10)) {
|
||||||
|
after = $(this);
|
||||||
|
if(after.hasClass('main-post')) {
|
||||||
|
after = after.next();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAlreadyAddedPosts();
|
||||||
|
if(!data.posts.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var insertAfter = findInsertionPoint();
|
||||||
|
|
||||||
|
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
|
||||||
|
|
||||||
|
translator.translate(html, function(translatedHTML) {
|
||||||
|
var translated = $(translatedHTML);
|
||||||
|
if(!infiniteLoaded) {
|
||||||
|
translated.removeClass('infiniteloaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
translated.insertAfter(insertAfter)
|
||||||
|
.hide()
|
||||||
|
.fadeIn('slow');
|
||||||
|
|
||||||
|
for (var x = 0, numPosts = data.posts.length; x < numPosts; x++) {
|
||||||
|
socket.emit('api:post.privileges', data.posts[x].pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
infiniteLoaderActive = false;
|
||||||
|
|
||||||
|
app.populateOnlineUsers();
|
||||||
|
app.addCommasToNumbers();
|
||||||
|
$('span.timeago').timeago();
|
||||||
|
$('.post-content img').addClass('img-responsive');
|
||||||
|
updatePostCount();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePostCount() {
|
||||||
|
$('#topic-post-count').html($('#post-container li[data-pid]:not(.deleted)').length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadMorePosts(tid, callback) {
|
||||||
|
var indicatorEl = $('.loading-indicator');
|
||||||
|
|
||||||
|
if (infiniteLoaderActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
infiniteLoaderActive = true;
|
||||||
|
|
||||||
|
if (indicatorEl.attr('done') === '0') {
|
||||||
|
indicatorEl.fadeIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('api:topic.loadMore', {
|
||||||
|
tid: tid,
|
||||||
|
after: $('#post-container .post-row.infiniteloaded').length
|
||||||
|
}, function (data) {
|
||||||
|
infiniteLoaderActive = false;
|
||||||
|
if (data.posts.length) {
|
||||||
|
indicatorEl.attr('done', '0');
|
||||||
|
createNewPosts(data, true);
|
||||||
|
} else {
|
||||||
|
indicatorEl.attr('done', '1');
|
||||||
|
}
|
||||||
|
indicatorEl.fadeOut();
|
||||||
|
if (callback) {
|
||||||
|
callback(data.posts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return Topic;
|
return Topic;
|
||||||
});
|
});
|
||||||
@@ -210,13 +210,17 @@ define(['taskbar'], function(taskbar) {
|
|||||||
selectionEnd = postContentEl.selectionEnd,
|
selectionEnd = postContentEl.selectionEnd,
|
||||||
selectionLength = selectionEnd - selectionStart;
|
selectionLength = selectionEnd - selectionStart;
|
||||||
|
|
||||||
|
function insertIntoInput(element, value) {
|
||||||
|
var start = postContentEl.selectionStart;
|
||||||
|
element.value = element.value.slice(0, start) + value + element.value.slice(start, element.value.length);
|
||||||
|
postContentEl.selectionStart = postContentEl.selectionEnd = start + value.length;
|
||||||
|
}
|
||||||
|
|
||||||
switch(iconClass) {
|
switch(iconClass) {
|
||||||
case 'icon-bold':
|
case 'icon-bold':
|
||||||
if (selectionStart === selectionEnd) {
|
if (selectionStart === selectionEnd) {
|
||||||
// Nothing selected
|
// Nothing selected
|
||||||
postContentEl.value = postContentEl.value + '**bolded text**';
|
insertIntoInput(postContentEl, "**bolded text**");
|
||||||
postContentEl.selectionStart = cursorEnd+2;
|
|
||||||
postContentEl.selectionEnd = postContentEl.value.length - 2;
|
|
||||||
} else {
|
} else {
|
||||||
// Text selected
|
// Text selected
|
||||||
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '**' + postContentEl.value.slice(selectionStart, selectionEnd) + '**' + postContentEl.value.slice(selectionEnd);
|
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '**' + postContentEl.value.slice(selectionStart, selectionEnd) + '**' + postContentEl.value.slice(selectionEnd);
|
||||||
@@ -227,9 +231,7 @@ define(['taskbar'], function(taskbar) {
|
|||||||
case 'icon-italic':
|
case 'icon-italic':
|
||||||
if (selectionStart === selectionEnd) {
|
if (selectionStart === selectionEnd) {
|
||||||
// Nothing selected
|
// Nothing selected
|
||||||
postContentEl.value = postContentEl.value + '*italicised text*';
|
insertIntoInput(postContentEl, "*italicised text*");
|
||||||
postContentEl.selectionStart = cursorEnd+1;
|
|
||||||
postContentEl.selectionEnd = postContentEl.value.length - 1;
|
|
||||||
} else {
|
} else {
|
||||||
// Text selected
|
// Text selected
|
||||||
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '*' + postContentEl.value.slice(selectionStart, selectionEnd) + '*' + postContentEl.value.slice(selectionEnd);
|
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '*' + postContentEl.value.slice(selectionStart, selectionEnd) + '*' + postContentEl.value.slice(selectionEnd);
|
||||||
@@ -239,16 +241,12 @@ define(['taskbar'], function(taskbar) {
|
|||||||
break;
|
break;
|
||||||
case 'icon-list':
|
case 'icon-list':
|
||||||
// Nothing selected
|
// Nothing selected
|
||||||
postContentEl.value = postContentEl.value + "\n\n* list item";
|
insertIntoInput(postContentEl, "\n\n* list item");
|
||||||
postContentEl.selectionStart = cursorEnd+4;
|
|
||||||
postContentEl.selectionEnd = postContentEl.value.length;
|
|
||||||
break;
|
break;
|
||||||
case 'icon-link':
|
case 'icon-link':
|
||||||
if (selectionStart === selectionEnd) {
|
if (selectionStart === selectionEnd) {
|
||||||
// Nothing selected
|
// Nothing selected
|
||||||
postContentEl.value = postContentEl.value + '[link text](link url)';
|
insertIntoInput(postContentEl, "[link text](link url)");
|
||||||
postContentEl.selectionStart = cursorEnd+12;
|
|
||||||
postContentEl.selectionEnd = postContentEl.value.length - 1;
|
|
||||||
} else {
|
} else {
|
||||||
// Text selected
|
// Text selected
|
||||||
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '[' + postContentEl.value.slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.value.slice(selectionEnd);
|
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '[' + postContentEl.value.slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.value.slice(selectionEnd);
|
||||||
@@ -258,6 +256,7 @@ define(['taskbar'], function(taskbar) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('resize', function() {
|
window.addEventListener('resize', function() {
|
||||||
if (composer.active !== undefined) composer.reposition(composer.active);
|
if (composer.active !== undefined) composer.reposition(composer.active);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -235,11 +235,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeRegex(block) {
|
function makeRegex(block) {
|
||||||
return new RegExp("<!-- BEGIN " + block + " -->[\\s\\S]*<!-- END " + block + " -->", 'g');
|
return new RegExp("<!--[\\s]*BEGIN " + block + "[\\s]*-->[\\s\\S]*<!--[\\s]*END " + block + "[\\s]*-->", 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeConditionalRegex(block) {
|
function makeConditionalRegex(block) {
|
||||||
return new RegExp("<!-- IF " + block + " -->[\\s\\S]*<!-- ENDIF " + block + " -->", 'g');
|
return new RegExp("<!--[\\s]*IF " + block + "[\\s]*-->[\\s\\S]*<!--[\\s]*ENDIF " + block + "[\\s]*-->", 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBlock(regex, block, template) {
|
function getBlock(regex, block, template) {
|
||||||
@@ -249,21 +249,8 @@
|
|||||||
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
||||||
|
|
||||||
data = data[0]
|
data = data[0]
|
||||||
.replace("<!-- BEGIN " + block + " -->", "")
|
.replace("<!--[\\s]*BEGIN " + block + "[\\s]*-->", "")
|
||||||
.replace("<!-- END " + block + " -->", "");
|
.replace("<!--[\\s]*END " + block + "[\\s]*-->", "");
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getConditionalBlock(regex, block, template) {
|
|
||||||
data = template.match(regex);
|
|
||||||
if (data == null) return;
|
|
||||||
|
|
||||||
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
|
||||||
|
|
||||||
data = data[0]
|
|
||||||
.replace("<!-- IF " + block + " -->", "")
|
|
||||||
.replace("<!-- ENDIF " + block + " -->", "");
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -317,14 +304,29 @@
|
|||||||
block = parse(data[d], namespace, block);
|
block = parse(data[d], namespace, block);
|
||||||
template = setBlock(regex, block, template);
|
template = setBlock(regex, block, template);
|
||||||
} else {
|
} else {
|
||||||
var conditional = makeConditionalRegex(d),
|
var conditional = makeConditionalRegex(namespace + d);
|
||||||
block = getConditionalBlock(conditional, namespace, template);
|
|
||||||
|
|
||||||
if (block && !data[d]) {
|
var conditionalBlock = conditional.exec(template);
|
||||||
template = template.replace(conditional, '');
|
|
||||||
|
if (conditionalBlock !== null) {
|
||||||
|
conditionalBlock = conditionalBlock[0].split(/<!-- ELSE -->/);
|
||||||
|
|
||||||
|
if (conditionalBlock[1]) {
|
||||||
|
// there is an else statement
|
||||||
|
if (!data[d]) {
|
||||||
|
template = template.replace(conditional, conditionalBlock[1]);
|
||||||
|
} else {
|
||||||
|
template = template.replace(conditional, conditionalBlock[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// regular if
|
||||||
|
if (!data[d]) {
|
||||||
|
template = template.replace(conditional, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template = replace(namespace + d, data[d], template);
|
template = replace(namespace + d, data[d], template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<span class="account-bio-label">website</span>
|
<span class="account-bio-label">website</span>
|
||||||
<span><a href="{website}">{website}</a></span>
|
<span><a href="{website}">{websiteName}</a></span>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<span class="account-bio-label">location</span>
|
<span class="account-bio-label">location</span>
|
||||||
|
|||||||
@@ -12,14 +12,14 @@
|
|||||||
<p>{groups.description}</p>
|
<p>{groups.description}</p>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-default" data-action="members">Members</button>
|
<button class="btn btn-default" data-action="members">Members</button>
|
||||||
|
<!-- IF groups.deletable -->
|
||||||
<button class="btn btn-danger" data-action="delete">Delete Group</button>
|
<button class="btn btn-danger" data-action="delete">Delete Group</button>
|
||||||
|
<!-- ENDIF groups.deletable -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<ul class="pull-right members">
|
<ul class="pull-right members">
|
||||||
<!-- BEGIN members -->
|
<!-- BEGIN members --><li data-uid="{groups.members.uid}" title="{groups.members.username}"><img src="{groups.members.picture}" /></li><!-- END members -->
|
||||||
<li data-uid="{groups.members.uid}" title="{groups.members.username}"><img src="{groups.members.picture}" /></li>
|
|
||||||
<!-- END members -->
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<script type="text/javascript" src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
|
<script type="text/javascript" src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||||
<script type="text/javascript" src="http://code.jquery.com/qunit/qunit-git.js"></script>
|
<script type="text/javascript" src="http://code.jquery.com/qunit/qunit-git.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/qunit/qunit-git.css">
|
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/qunit/qunit-git.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{relative_path}/vendor/colorpicker/colorpicker.css">
|
||||||
<script type="text/javascript" src="{relative_path}/socket.io/socket.io.js"></script>
|
<script type="text/javascript" src="{relative_path}/socket.io/socket.io.js"></script>
|
||||||
<script type="text/javascript" src="{relative_path}/src/app.js"></script>
|
<script type="text/javascript" src="{relative_path}/src/app.js"></script>
|
||||||
<script type="text/javascript" src="{relative_path}/src/templates.js"></script>
|
<script type="text/javascript" src="{relative_path}/src/templates.js"></script>
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
<script src="{relative_path}/vendor/jquery/js/jquery.form.js"></script>
|
<script src="{relative_path}/vendor/jquery/js/jquery.form.js"></script>
|
||||||
<script src="{relative_path}/vendor/requirejs/require.js"></script>
|
<script src="{relative_path}/vendor/requirejs/require.js"></script>
|
||||||
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
|
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
|
||||||
|
<script src="{relative_path}/vendor/colorpicker/colorpicker.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
require.config({
|
require.config({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<h1>Topics</h1>
|
<h1>Topics</h1>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
|
||||||
<ul class="topics">
|
<ul class="topics">
|
||||||
<!-- BEGIN topics -->
|
<!-- BEGIN topics -->
|
||||||
<li data-tid="{topics.tid}" data-locked="{topics.locked}" data-pinned="{topics.pinned}" data-deleted="{topics.deleted}">
|
<li data-tid="{topics.tid}" data-locked="{topics.locked}" data-pinned="{topics.pinned}" data-deleted="{topics.deleted}">
|
||||||
@@ -19,6 +20,14 @@
|
|||||||
<!-- END topics -->
|
<!-- END topics -->
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<!-- IF notopics -->
|
||||||
|
<div class="alert alert-warning" id="category-no-topics">
|
||||||
|
<strong>There are no topics.</strong>
|
||||||
|
</div>
|
||||||
|
<!-- ELSE -->
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button>
|
<button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- ENDIF notopics -->
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,9 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="pull-right hidden-xs">
|
<span class="pull-right hidden-xs">
|
||||||
|
<!-- IF topics.unreplied -->
|
||||||
|
No one has replied
|
||||||
|
<!-- ELSE -->
|
||||||
<a href="/user/{topics.teaser_userslug}">
|
<a href="/user/{topics.teaser_userslug}">
|
||||||
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||||
</a>
|
</a>
|
||||||
@@ -69,7 +72,7 @@
|
|||||||
replied
|
replied
|
||||||
</a>
|
</a>
|
||||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||||
|
<!-- ENDIF topics.unreplied -->
|
||||||
</span>
|
</span>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,7 +98,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="block-content active-users">
|
<div class="block-content active-users">
|
||||||
<!-- BEGIN active_users -->
|
<!-- BEGIN active_users -->
|
||||||
<a data-uid="{active_users.uid}" href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded" /></a>
|
<a data-uid="{active_users.uid}" href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded user-img" /></a>
|
||||||
<!-- END active_users -->
|
<!-- END active_users -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
<!-- BEGIN posts -->
|
<!-- BEGIN posts -->
|
||||||
<div class="topic-row img-thumbnail clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
|
<div class="topic-row img-thumbnail clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
|
||||||
<span><strong>{posts.username}</strong> : </span>
|
<span><strong>{posts.username}</strong> : </span>
|
||||||
<span>{posts.content}</span>
|
<span>{posts.category_name} >> {posts.title}</span>
|
||||||
|
<div>{posts.content}</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
|
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<footer id="footer" class="container footer">
|
<footer id="footer" class="container footer">
|
||||||
|
{footerHTML}
|
||||||
<div class="copyright">Copyright © 2013 <a target="_blank" href="http://www.nodebb.org">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></div>
|
<div class="copyright">Copyright © 2013 <a target="_blank" href="http://www.nodebb.org">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,13 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul id="logged-out-menu" class="nav navbar-nav navbar-right">
|
<ul id="logged-out-menu" class="nav navbar-nav navbar-right">
|
||||||
<li>
|
<li class="visible-lg visible-md visible-sm">
|
||||||
|
<a href="/register">Register</a>
|
||||||
|
</li>
|
||||||
|
<li class="visible-lg visible-md visible-sm">
|
||||||
|
<a href="/login">Login</a>
|
||||||
|
</li>
|
||||||
|
<li class="visible-xs">
|
||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="loggedout_dropdown"><i class="icon-signin"></i></a>
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="loggedout_dropdown"><i class="icon-signin"></i></a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="loggedout_dropdown">
|
<ul class="dropdown-menu" aria-labelledby="loggedout_dropdown">
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<a href="category/{categories.slug}" itemprop="url">
|
<a href="category/{categories.slug}" itemprop="url">
|
||||||
<meta itemprop="name" content="{categories.name}">
|
<meta itemprop="name" content="{categories.name}">
|
||||||
<h4><span class="badge {categories.badgeclass}">{categories.topic_count} </span> {categories.name}</h4>
|
<h4><span class="badge {categories.badgeclass}">{categories.topic_count} </span> {categories.name}</h4>
|
||||||
<div class="icon {categories.blockclass}">
|
<div class="icon" style="background: {categories.bgColor}; color: {categories.color};">
|
||||||
<div id="category-{categories.cid}" class="category-slider-{categories.post_count}">
|
<div id="category-{categories.cid}" class="category-slider-{categories.post_count}">
|
||||||
<div class="category-box"><i class="{categories.icon} icon-4x"></i></div>
|
<div class="category-box"><i class="{categories.icon} icon-4x"></i></div>
|
||||||
<div class="category-box" itemprop="description">{categories.description}</div>
|
<div class="category-box" itemprop="description">{categories.description}</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<h1>User Privilege Thresholds</h1>
|
<h1>User Privilege Thresholds</h1>
|
||||||
|
|
||||||
<form class="form-inline">
|
<form class="form-inline">
|
||||||
@@ -15,13 +14,13 @@
|
|||||||
<label>Manage Content</label> <input type="number" class="input-mini" value="1000" placeholder="1000" data-field="privileges:manage_content" />
|
<label>Manage Content</label> <input type="number" class="input-mini" value="1000" placeholder="1000" data-field="privileges:manage_content" />
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Users with reach the "Manage Content" threshold are able to edit/delete other users' posts.
|
Users who reach the "Manage Content" threshold are able to edit/delete other users' posts.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label>Manage Topics</label> <input type="number" class="input-mini" value="2000" placeholder="2000" data-field="privileges:manage_topic" />
|
<label>Manage Topics</label> <input type="number" class="input-mini" value="2000" placeholder="2000" data-field="privileges:manage_topic" />
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Users with reach the "Manage Topics" threshold are able to edit, lock, pin, close, and delete topics.
|
Users who reach the "Manage Topics" threshold are able to edit, lock, pin, close, and delete topics.
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-lg-offset-2 col-lg-10">
|
<div class="col-lg-offset-2 col-lg-10">
|
||||||
<button class="btn btn-primary" id="login" type="submit">[[login:login]]</button> <a href="/reset">[[login:forgot_password]]</a>
|
<button class="btn btn-primary" id="login" type="submit">[[login:login]]</button> <a id="reset-link" class="hide" href="/reset">[[login:forgot_password]]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" />
|
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" />
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<ul class="notifications-list">
|
<ul class="notifications-list">
|
||||||
<!-- BEGIN notifications -->
|
<!-- BEGIN notifications -->
|
||||||
<li data-nid="{notifications.nid}" class="{notifications.readClass}">
|
<li data-nid="{notifications.nid}" class="{notifications.readClass}">
|
||||||
<a href="..{notifications.path}">{notifications.text}</a>
|
<a href="{notifications.path}">{notifications.text}</a>
|
||||||
<p class="timestamp">
|
<p class="timestamp">
|
||||||
<span class="timeago" title="{notifications.datetimeISO}"></span>
|
<span class="timeago" title="{notifications.datetimeISO}"></span>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<h3>
|
<h3>
|
||||||
You are now leaving NodeBB.
|
You are now leaving {title}.
|
||||||
</h3>
|
</h3>
|
||||||
<p>
|
<p>
|
||||||
<a href="{url}" rel="nofollow" class="btn btn-primary btn-lg">Continue to {url}</a>
|
<a href="{url}" rel="nofollow" class="btn btn-primary btn-lg">Continue to {url}</a>
|
||||||
<a id="return-btn" href="#" class="btn btn-lg btn-warning">Return to NodeBB</a>
|
<a id="return-btn" href="#" class="btn btn-lg btn-warning">Return to {title}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li class="active">{category_name}</li>
|
<li class="active">Recent <a href="./recent.rss"><i class="icon-rss-sign"></i></a></li>
|
||||||
<div id="category_active_users"></div>
|
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
@@ -21,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="category row">
|
<div class="category row">
|
||||||
<div class="{topic_row_size}">
|
<div class="col-md-12">
|
||||||
<ul id="topics-container">
|
<ul id="topics-container">
|
||||||
<!-- BEGIN topics -->
|
<!-- BEGIN topics -->
|
||||||
<li class="category-item {topics.deleted-class}">
|
<li class="category-item {topics.deleted-class}">
|
||||||
@@ -55,6 +54,9 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="pull-right hidden-xs">
|
<span class="pull-right hidden-xs">
|
||||||
|
<!-- IF topics.unreplied -->
|
||||||
|
No one has replied
|
||||||
|
<!-- ELSE -->
|
||||||
<a href="/user/{topics.teaser_userslug}">
|
<a href="/user/{topics.teaser_userslug}">
|
||||||
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||||
</a>
|
</a>
|
||||||
@@ -62,7 +64,7 @@
|
|||||||
replied
|
replied
|
||||||
</a>
|
</a>
|
||||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||||
|
<!-- ENDIF topics.unreplied -->
|
||||||
</span>
|
</span>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,13 +20,14 @@
|
|||||||
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||||
<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="icon-rss-sign"></i></a></span>
|
<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="icon-rss-sign"></i></a></span>
|
||||||
</li>
|
</li>
|
||||||
<div class="thread_active_users active-users pull-right hidden-xs"></div>
|
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<ul id="post-container" class="container" data-tid="{topic_id}">
|
<ul id="post-container" class="container" data-tid="{topic_id}">
|
||||||
<!-- BEGIN main_posts -->
|
<!-- BEGIN main_posts -->
|
||||||
<a id="post_anchor_{main_posts.pid}" name="{main_posts.pid}"></a>
|
|
||||||
<li class="row post-row main-post" data-pid="{main_posts.pid}" data-uid="{main_posts.uid}" data-username="{main_posts.username}" data-deleted="{main_posts.deleted}" itemscope itemtype="http://schema.org/Article">
|
<li class="row post-row main-post infiniteloaded" data-pid="{main_posts.pid}" data-uid="{main_posts.uid}" data-username="{main_posts.username}" data-deleted="{main_posts.deleted}" itemscope itemtype="http://schema.org/Article">
|
||||||
|
<a id="post_anchor_{main_posts.pid}" name="{main_posts.pid}"></a>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="post-block">
|
<div class="post-block">
|
||||||
<meta itemprop="datePublished" content="{main_posts.relativeTime}">
|
<meta itemprop="datePublished" content="{main_posts.relativeTime}">
|
||||||
@@ -63,17 +64,17 @@
|
|||||||
<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="icon-reply"></i></button>
|
<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="icon-reply"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="pull-right">
|
||||||
<div class="btn-group pull-right post-tools">
|
<div class="btn-group post-tools">
|
||||||
<button class="btn btn-sm btn-default edit {main_posts.display_moderator_tools}" type="button" title="[[topic:edit]]"><i class="icon-pencil"></i></button>
|
<button class="btn btn-sm btn-default link" type="button" title="[[topic:link]]"><i class="icon-link"></i></button>
|
||||||
<button class="btn btn-sm btn-default delete {main_posts.display_moderator_tools}" type="button" title="[[topic:delete]]"><i class="icon-trash"></i></button>
|
<button class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="icon-facebook"></i></button>
|
||||||
</div>
|
<button class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="icon-twitter"></i></button>
|
||||||
|
<button class="btn btn-sm btn-default google-share" type="button" title=""><i class="icon-google-plus"></i></button>
|
||||||
<div class="btn-group pull-right post-tools">
|
</div>
|
||||||
<button class="btn btn-sm btn-default link" type="button" title="[[topic:link]]"><i class="icon-link"></i></button>
|
<div class="btn-group post-tools">
|
||||||
<button class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="icon-facebook"></i></button>
|
<button class="btn btn-sm btn-default edit {main_posts.display_moderator_tools}" type="button" title="[[topic:edit]]"><i class="icon-pencil"></i></button>
|
||||||
<button class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="icon-twitter"></i></button>
|
<button class="btn btn-sm btn-default delete {main_posts.display_moderator_tools}" type="button" title="[[topic:delete]]"><i class="icon-trash"></i></button>
|
||||||
<button class="btn btn-sm btn-default google-share" type="button" title=""><i class="icon-google-plus"></i></button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input id="post_{main_posts.pid}_link" value="" class="pull-right" style="display:none;"></input>
|
<input id="post_{main_posts.pid}_link" value="" class="pull-right" style="display:none;"></input>
|
||||||
@@ -98,9 +99,38 @@
|
|||||||
</li>
|
</li>
|
||||||
<!-- END main_posts -->
|
<!-- END main_posts -->
|
||||||
|
|
||||||
|
<li class="well">
|
||||||
|
<div class="inline-block">
|
||||||
|
<small class="topic-stats">
|
||||||
|
<span>posts</span>
|
||||||
|
<strong><span id="topic-post-count" class="formatted-number">{postcount}</span></strong> |
|
||||||
|
<span>views</span>
|
||||||
|
<strong><span class="formatted-number">{viewcount}</span></strong> |
|
||||||
|
<span>browsing</span>
|
||||||
|
</small>
|
||||||
|
<div class="thread_active_users active-users inline-block"></div>
|
||||||
|
</div>
|
||||||
|
<div class="topic-main-buttons pull-right inline-block">
|
||||||
|
<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
|
||||||
|
<div class="btn-group thread-tools hide">
|
||||||
|
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="#" class="pin_thread"><i class="icon-pushpin"></i> [[topic:thread_tools.pin]]</a></li>
|
||||||
|
<li><a href="#" class="lock_thread"><i class="icon-lock"></i> [[topic:thread_tools.lock]]</a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li><a href="#" class="move_thread"><i class="icon-move"></i> [[topic:thread_tools.move]]</a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li><a href="#" class="delete_thread"><span class="text-error"><i class="icon-trash"></i> [[topic:thread_tools.delete]]</span></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<!-- BEGIN posts -->
|
<!-- BEGIN posts -->
|
||||||
<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
|
|
||||||
<li class="row post-row sub-posts" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
|
<li class="row post-row sub-posts infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
|
||||||
|
<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
|
||||||
<meta itemprop="datePublished" content="{posts.relativeTime}">
|
<meta itemprop="datePublished" content="{posts.relativeTime}">
|
||||||
<meta itemprop="dateModified" content="{posts.relativeEditTime}">
|
<meta itemprop="dateModified" content="{posts.relativeEditTime}">
|
||||||
<div class="col-md-1 profile-image-block hidden-xs hidden-sm">
|
<div class="col-md-1 profile-image-block hidden-xs hidden-sm">
|
||||||
@@ -163,25 +193,25 @@
|
|||||||
<!-- END posts -->
|
<!-- END posts -->
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="loading-indicator" style="text-align:center;" class="hide" done="0">
|
<div class="well col-md-11 col-xs-12 pull-right hide">
|
||||||
<i class="icon-spinner icon-spin icon-large"></i>
|
<div class="topic-main-buttons pull-right inline-block hide">
|
||||||
</div>
|
<div class="loading-indicator" done="0" style="display:none;">
|
||||||
|
Loading More Posts <i class="icon-refresh icon-spin"></i>
|
||||||
<hr />
|
</div>
|
||||||
|
<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
|
||||||
<div class="topic-main-buttons">
|
<div class="btn-group thread-tools hide">
|
||||||
<button id="post_reply" class="btn btn-primary btn-lg post_reply" type="button">[[topic:reply]]</button>
|
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
|
||||||
<div class="btn-group pull-right" id="thread-tools" style="visibility: hidden;">
|
<ul class="dropdown-menu">
|
||||||
<button class="btn btn-default btn-lg dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
|
<li><a href="#" class="pin_thread"><i class="icon-pushpin"></i> [[topic:thread_tools.pin]]</a></li>
|
||||||
<ul class="dropdown-menu">
|
<li><a href="#" class="lock_thread"><i class="icon-lock"></i> [[topic:thread_tools.lock]]</a></li>
|
||||||
<li><a href="#" id="pin_thread"><i class="icon-pushpin"></i> [[topic:thread_tools.pin]]</a></li>
|
<li class="divider"></li>
|
||||||
<li><a href="#" id="lock_thread"><i class="icon-lock"></i> [[topic:thread_tools.lock]]</a></li>
|
<li><a href="#" class="move_thread"><i class="icon-move"></i> [[topic:thread_tools.move]]</a></li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li><a href="#" id="move_thread"><i class="icon-move"></i> [[topic:thread_tools.move]]</a></li>
|
<li><a href="#" class="delete_thread"><span class="text-error"><i class="icon-trash"></i> [[topic:thread_tools.delete]]</span></a></li>
|
||||||
<li class="divider"></li>
|
</ul>
|
||||||
<li><a href="#" id="delete_thread"><span class="text-error"><i class="icon-trash"></i> [[topic:thread_tools.delete]]</span></a></li>
|
</div>
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mobile-author-overlay">
|
<div class="mobile-author-overlay">
|
||||||
|
|||||||
@@ -49,14 +49,17 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="pull-right hidden-xs">
|
<span class="pull-right hidden-xs">
|
||||||
|
<!-- IF topics.unreplied -->
|
||||||
|
No one has replied
|
||||||
|
<!-- ELSE -->
|
||||||
<a href="/user/{topics.teaser_userslug}">
|
<a href="/user/{topics.teaser_userslug}">
|
||||||
<img class="img-rounded teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||||
replied
|
replied
|
||||||
</a>
|
</a>
|
||||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||||
|
<!-- ENDIF topics.unreplied -->
|
||||||
</span>
|
</span>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input class="form-control" id="search-user" type="text" placeholder="[[users:enter_username]]"/>
|
<input class="form-control" id="search-user" type="text" placeholder="[[users:enter_username]]"/>
|
||||||
<span class="input-group-addon">
|
<span class="input-group-addon">
|
||||||
<span id="user-notfound-notify"><i class="icon icon-circle-blank"></i></span>
|
<span id="user-notfound-notify"><i class="icon icon-search"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
162
public/vendor/colorpicker/colorpicker.css
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
.colorpicker {
|
||||||
|
width: 356px;
|
||||||
|
height: 176px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
background: url(./images/custom_background.png);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.colorpicker_color {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
left: 14px;
|
||||||
|
top: 13px;
|
||||||
|
position: absolute;
|
||||||
|
background: #f00;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.colorpicker_color div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
background: url(./images/colorpicker_overlay.png);
|
||||||
|
}
|
||||||
|
.colorpicker_color div div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: url(./images/colorpicker_select.gif);
|
||||||
|
margin: -5px 0 0 -5px;
|
||||||
|
}
|
||||||
|
.colorpicker_hue {
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
left: 171px;
|
||||||
|
width: 35px;
|
||||||
|
height: 150px;
|
||||||
|
cursor: n-resize;
|
||||||
|
}
|
||||||
|
.colorpicker_hue div {
|
||||||
|
position: absolute;
|
||||||
|
width: 35px;
|
||||||
|
height: 9px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: url(./images/custom_indic.gif) left top;
|
||||||
|
margin: -4px 0 0 0;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
.colorpicker_new_color {
|
||||||
|
position: absolute;
|
||||||
|
width: 60px;
|
||||||
|
height: 30px;
|
||||||
|
left: 213px;
|
||||||
|
top: 13px;
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
.colorpicker_current_color {
|
||||||
|
position: absolute;
|
||||||
|
width: 60px;
|
||||||
|
height: 30px;
|
||||||
|
left: 283px;
|
||||||
|
top: 13px;
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
.colorpicker input {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 10px;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
color: #898989;
|
||||||
|
top: 4px;
|
||||||
|
right: 11px;
|
||||||
|
text-align: right;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 11px;
|
||||||
|
}
|
||||||
|
.colorpicker_hex {
|
||||||
|
position: absolute;
|
||||||
|
width: 72px;
|
||||||
|
height: 22px;
|
||||||
|
background: url(./images/custom_hex.png) top;
|
||||||
|
left: 212px;
|
||||||
|
top: 142px;
|
||||||
|
}
|
||||||
|
.colorpicker_hex input {
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
.colorpicker_field {
|
||||||
|
height: 22px;
|
||||||
|
width: 62px;
|
||||||
|
background-position: top;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.colorpicker_field span {
|
||||||
|
position: absolute;
|
||||||
|
width: 12px;
|
||||||
|
height: 22px;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
cursor: n-resize;
|
||||||
|
}
|
||||||
|
.colorpicker_rgb_r {
|
||||||
|
background-image: url(./images/custom_rgb_r.png);
|
||||||
|
top: 52px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
.colorpicker_rgb_g {
|
||||||
|
background-image: url(./images/custom_rgb_g.png);
|
||||||
|
top: 82px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
.colorpicker_rgb_b {
|
||||||
|
background-image: url(./images/custom_rgb_b.png);
|
||||||
|
top: 112px;
|
||||||
|
left: 212px;
|
||||||
|
}
|
||||||
|
.colorpicker_hsb_h {
|
||||||
|
background-image: url(./images/custom_hsb_h.png);
|
||||||
|
top: 52px;
|
||||||
|
left: 282px;
|
||||||
|
}
|
||||||
|
.colorpicker_hsb_s {
|
||||||
|
background-image: url(./images/custom_hsb_s.png);
|
||||||
|
top: 82px;
|
||||||
|
left: 282px;
|
||||||
|
}
|
||||||
|
.colorpicker_hsb_b {
|
||||||
|
background-image: url(./images/custom_hsb_b.png);
|
||||||
|
top: 112px;
|
||||||
|
left: 282px;
|
||||||
|
}
|
||||||
|
.colorpicker_submit {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background: url(./images/custom_submit.png) top;
|
||||||
|
left: 322px;
|
||||||
|
top: 142px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.colorpicker_focus {
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
.colorpicker_hex.colorpicker_focus {
|
||||||
|
background-position: bottom;
|
||||||
|
}
|
||||||
|
.colorpicker_submit.colorpicker_focus {
|
||||||
|
background-position: bottom;
|
||||||
|
}
|
||||||
|
.colorpicker_slider {
|
||||||
|
background-position: bottom;
|
||||||
|
}
|
||||||
484
public/vendor/colorpicker/colorpicker.js
vendored
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Color picker
|
||||||
|
* Author: Stefan Petre www.eyecon.ro
|
||||||
|
*
|
||||||
|
* Dual licensed under the MIT and GPL licenses
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
var ColorPicker = function () {
|
||||||
|
var
|
||||||
|
ids = {},
|
||||||
|
inAction,
|
||||||
|
charMin = 65,
|
||||||
|
visible,
|
||||||
|
tpl = '<div class="colorpicker"><div class="colorpicker_color"><div><div></div></div></div><div class="colorpicker_hue"><div></div></div><div class="colorpicker_new_color"></div><div class="colorpicker_current_color"></div><div class="colorpicker_hex"><input type="text" maxlength="6" size="6" /></div><div class="colorpicker_rgb_r colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_g colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_h colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_s colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_submit"></div></div>',
|
||||||
|
defaults = {
|
||||||
|
eventName: 'click',
|
||||||
|
onShow: function () {},
|
||||||
|
onBeforeShow: function(){},
|
||||||
|
onHide: function () {},
|
||||||
|
onChange: function () {},
|
||||||
|
onSubmit: function () {},
|
||||||
|
color: 'ff0000',
|
||||||
|
livePreview: true,
|
||||||
|
flat: false
|
||||||
|
},
|
||||||
|
fillRGBFields = function (hsb, cal) {
|
||||||
|
var rgb = HSBToRGB(hsb);
|
||||||
|
$(cal).data('colorpicker').fields
|
||||||
|
.eq(1).val(rgb.r).end()
|
||||||
|
.eq(2).val(rgb.g).end()
|
||||||
|
.eq(3).val(rgb.b).end();
|
||||||
|
},
|
||||||
|
fillHSBFields = function (hsb, cal) {
|
||||||
|
$(cal).data('colorpicker').fields
|
||||||
|
.eq(4).val(hsb.h).end()
|
||||||
|
.eq(5).val(hsb.s).end()
|
||||||
|
.eq(6).val(hsb.b).end();
|
||||||
|
},
|
||||||
|
fillHexFields = function (hsb, cal) {
|
||||||
|
$(cal).data('colorpicker').fields
|
||||||
|
.eq(0).val(HSBToHex(hsb)).end();
|
||||||
|
},
|
||||||
|
setSelector = function (hsb, cal) {
|
||||||
|
$(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100}));
|
||||||
|
$(cal).data('colorpicker').selectorIndic.css({
|
||||||
|
left: parseInt(150 * hsb.s/100, 10),
|
||||||
|
top: parseInt(150 * (100-hsb.b)/100, 10)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setHue = function (hsb, cal) {
|
||||||
|
$(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10));
|
||||||
|
},
|
||||||
|
setCurrentColor = function (hsb, cal) {
|
||||||
|
$(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb));
|
||||||
|
},
|
||||||
|
setNewColor = function (hsb, cal) {
|
||||||
|
$(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb));
|
||||||
|
},
|
||||||
|
keyDown = function (ev) {
|
||||||
|
var pressedKey = ev.charCode || ev.keyCode || -1;
|
||||||
|
if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var cal = $(this).parent().parent();
|
||||||
|
if (cal.data('colorpicker').livePreview === true) {
|
||||||
|
change.apply(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
change = function (ev) {
|
||||||
|
var cal = $(this).parent().parent(), col;
|
||||||
|
if (this.parentNode.className.indexOf('_hex') > 0) {
|
||||||
|
cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value));
|
||||||
|
} else if (this.parentNode.className.indexOf('_hsb') > 0) {
|
||||||
|
cal.data('colorpicker').color = col = fixHSB({
|
||||||
|
h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10),
|
||||||
|
s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10),
|
||||||
|
b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cal.data('colorpicker').color = col = RGBToHSB(fixRGB({
|
||||||
|
r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10),
|
||||||
|
g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10),
|
||||||
|
b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (ev) {
|
||||||
|
fillRGBFields(col, cal.get(0));
|
||||||
|
fillHexFields(col, cal.get(0));
|
||||||
|
fillHSBFields(col, cal.get(0));
|
||||||
|
}
|
||||||
|
setSelector(col, cal.get(0));
|
||||||
|
setHue(col, cal.get(0));
|
||||||
|
setNewColor(col, cal.get(0));
|
||||||
|
cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]);
|
||||||
|
},
|
||||||
|
blur = function (ev) {
|
||||||
|
var cal = $(this).parent().parent();
|
||||||
|
cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus');
|
||||||
|
},
|
||||||
|
focus = function () {
|
||||||
|
charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65;
|
||||||
|
$(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus');
|
||||||
|
$(this).parent().addClass('colorpicker_focus');
|
||||||
|
},
|
||||||
|
downIncrement = function (ev) {
|
||||||
|
var field = $(this).parent().find('input').focus();
|
||||||
|
var current = {
|
||||||
|
el: $(this).parent().addClass('colorpicker_slider'),
|
||||||
|
max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255),
|
||||||
|
y: ev.pageY,
|
||||||
|
field: field,
|
||||||
|
val: parseInt(field.val(), 10),
|
||||||
|
preview: $(this).parent().parent().data('colorpicker').livePreview
|
||||||
|
};
|
||||||
|
$(document).bind('mouseup', current, upIncrement);
|
||||||
|
$(document).bind('mousemove', current, moveIncrement);
|
||||||
|
},
|
||||||
|
moveIncrement = function (ev) {
|
||||||
|
ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10))));
|
||||||
|
if (ev.data.preview) {
|
||||||
|
change.apply(ev.data.field.get(0), [true]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
upIncrement = function (ev) {
|
||||||
|
change.apply(ev.data.field.get(0), [true]);
|
||||||
|
ev.data.el.removeClass('colorpicker_slider').find('input').focus();
|
||||||
|
$(document).unbind('mouseup', upIncrement);
|
||||||
|
$(document).unbind('mousemove', moveIncrement);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
downHue = function (ev) {
|
||||||
|
var current = {
|
||||||
|
cal: $(this).parent(),
|
||||||
|
y: $(this).offset().top
|
||||||
|
};
|
||||||
|
current.preview = current.cal.data('colorpicker').livePreview;
|
||||||
|
$(document).bind('mouseup', current, upHue);
|
||||||
|
$(document).bind('mousemove', current, moveHue);
|
||||||
|
},
|
||||||
|
moveHue = function (ev) {
|
||||||
|
change.apply(
|
||||||
|
ev.data.cal.data('colorpicker')
|
||||||
|
.fields
|
||||||
|
.eq(4)
|
||||||
|
.val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10))
|
||||||
|
.get(0),
|
||||||
|
[ev.data.preview]
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
upHue = function (ev) {
|
||||||
|
fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
|
||||||
|
fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
|
||||||
|
$(document).unbind('mouseup', upHue);
|
||||||
|
$(document).unbind('mousemove', moveHue);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
downSelector = function (ev) {
|
||||||
|
var current = {
|
||||||
|
cal: $(this).parent(),
|
||||||
|
pos: $(this).offset()
|
||||||
|
};
|
||||||
|
current.preview = current.cal.data('colorpicker').livePreview;
|
||||||
|
$(document).bind('mouseup', current, upSelector);
|
||||||
|
$(document).bind('mousemove', current, moveSelector);
|
||||||
|
},
|
||||||
|
moveSelector = function (ev) {
|
||||||
|
change.apply(
|
||||||
|
ev.data.cal.data('colorpicker')
|
||||||
|
.fields
|
||||||
|
.eq(6)
|
||||||
|
.val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10))
|
||||||
|
.end()
|
||||||
|
.eq(5)
|
||||||
|
.val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10))
|
||||||
|
.get(0),
|
||||||
|
[ev.data.preview]
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
upSelector = function (ev) {
|
||||||
|
fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
|
||||||
|
fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
|
||||||
|
$(document).unbind('mouseup', upSelector);
|
||||||
|
$(document).unbind('mousemove', moveSelector);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
enterSubmit = function (ev) {
|
||||||
|
$(this).addClass('colorpicker_focus');
|
||||||
|
},
|
||||||
|
leaveSubmit = function (ev) {
|
||||||
|
$(this).removeClass('colorpicker_focus');
|
||||||
|
},
|
||||||
|
clickSubmit = function (ev) {
|
||||||
|
var cal = $(this).parent();
|
||||||
|
var col = cal.data('colorpicker').color;
|
||||||
|
cal.data('colorpicker').origColor = col;
|
||||||
|
setCurrentColor(col, cal.get(0));
|
||||||
|
cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el);
|
||||||
|
},
|
||||||
|
show = function (ev) {
|
||||||
|
var cal = $('#' + $(this).data('colorpickerId'));
|
||||||
|
cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]);
|
||||||
|
var pos = $(this).offset();
|
||||||
|
var viewPort = getViewport();
|
||||||
|
var top = pos.top + this.offsetHeight;
|
||||||
|
var left = pos.left;
|
||||||
|
if (top + 176 > viewPort.t + viewPort.h) {
|
||||||
|
top -= this.offsetHeight + 176;
|
||||||
|
}
|
||||||
|
if (left + 356 > viewPort.l + viewPort.w) {
|
||||||
|
left -= 356;
|
||||||
|
}
|
||||||
|
cal.css({left: left + 'px', top: top + 'px'});
|
||||||
|
if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) {
|
||||||
|
cal.show();
|
||||||
|
}
|
||||||
|
$(document).bind('mousedown', {cal: cal}, hide);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
hide = function (ev) {
|
||||||
|
if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) {
|
||||||
|
if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) {
|
||||||
|
ev.data.cal.hide();
|
||||||
|
}
|
||||||
|
$(document).unbind('mousedown', hide);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChildOf = function(parentEl, el, container) {
|
||||||
|
if (parentEl == el) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (parentEl.contains) {
|
||||||
|
return parentEl.contains(el);
|
||||||
|
}
|
||||||
|
if ( parentEl.compareDocumentPosition ) {
|
||||||
|
return !!(parentEl.compareDocumentPosition(el) & 16);
|
||||||
|
}
|
||||||
|
var prEl = el.parentNode;
|
||||||
|
while(prEl && prEl != container) {
|
||||||
|
if (prEl == parentEl)
|
||||||
|
return true;
|
||||||
|
prEl = prEl.parentNode;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
getViewport = function () {
|
||||||
|
var m = document.compatMode == 'CSS1Compat';
|
||||||
|
return {
|
||||||
|
l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
|
||||||
|
t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop),
|
||||||
|
w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth),
|
||||||
|
h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
fixHSB = function (hsb) {
|
||||||
|
return {
|
||||||
|
h: Math.min(360, Math.max(0, hsb.h)),
|
||||||
|
s: Math.min(100, Math.max(0, hsb.s)),
|
||||||
|
b: Math.min(100, Math.max(0, hsb.b))
|
||||||
|
};
|
||||||
|
},
|
||||||
|
fixRGB = function (rgb) {
|
||||||
|
return {
|
||||||
|
r: Math.min(255, Math.max(0, rgb.r)),
|
||||||
|
g: Math.min(255, Math.max(0, rgb.g)),
|
||||||
|
b: Math.min(255, Math.max(0, rgb.b))
|
||||||
|
};
|
||||||
|
},
|
||||||
|
fixHex = function (hex) {
|
||||||
|
var len = 6 - hex.length;
|
||||||
|
if (len > 0) {
|
||||||
|
var o = [];
|
||||||
|
for (var i=0; i<len; i++) {
|
||||||
|
o.push('0');
|
||||||
|
}
|
||||||
|
o.push(hex);
|
||||||
|
hex = o.join('');
|
||||||
|
}
|
||||||
|
return hex;
|
||||||
|
},
|
||||||
|
HexToRGB = function (hex) {
|
||||||
|
var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
|
||||||
|
return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)};
|
||||||
|
},
|
||||||
|
HexToHSB = function (hex) {
|
||||||
|
return RGBToHSB(HexToRGB(hex));
|
||||||
|
},
|
||||||
|
RGBToHSB = function (rgb) {
|
||||||
|
var hsb = {
|
||||||
|
h: 0,
|
||||||
|
s: 0,
|
||||||
|
b: 0
|
||||||
|
};
|
||||||
|
var min = Math.min(rgb.r, rgb.g, rgb.b);
|
||||||
|
var max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||||
|
var delta = max - min;
|
||||||
|
hsb.b = max;
|
||||||
|
if (max != 0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
hsb.s = max != 0 ? 255 * delta / max : 0;
|
||||||
|
if (hsb.s != 0) {
|
||||||
|
if (rgb.r == max) {
|
||||||
|
hsb.h = (rgb.g - rgb.b) / delta;
|
||||||
|
} else if (rgb.g == max) {
|
||||||
|
hsb.h = 2 + (rgb.b - rgb.r) / delta;
|
||||||
|
} else {
|
||||||
|
hsb.h = 4 + (rgb.r - rgb.g) / delta;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hsb.h = -1;
|
||||||
|
}
|
||||||
|
hsb.h *= 60;
|
||||||
|
if (hsb.h < 0) {
|
||||||
|
hsb.h += 360;
|
||||||
|
}
|
||||||
|
hsb.s *= 100/255;
|
||||||
|
hsb.b *= 100/255;
|
||||||
|
return hsb;
|
||||||
|
},
|
||||||
|
HSBToRGB = function (hsb) {
|
||||||
|
var rgb = {};
|
||||||
|
var h = Math.round(hsb.h);
|
||||||
|
var s = Math.round(hsb.s*255/100);
|
||||||
|
var v = Math.round(hsb.b*255/100);
|
||||||
|
if(s == 0) {
|
||||||
|
rgb.r = rgb.g = rgb.b = v;
|
||||||
|
} else {
|
||||||
|
var t1 = v;
|
||||||
|
var t2 = (255-s)*v/255;
|
||||||
|
var t3 = (t1-t2)*(h%60)/60;
|
||||||
|
if(h==360) h = 0;
|
||||||
|
if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3}
|
||||||
|
else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3}
|
||||||
|
else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3}
|
||||||
|
else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3}
|
||||||
|
else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3}
|
||||||
|
else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3}
|
||||||
|
else {rgb.r=0; rgb.g=0; rgb.b=0}
|
||||||
|
}
|
||||||
|
return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)};
|
||||||
|
},
|
||||||
|
RGBToHex = function (rgb) {
|
||||||
|
var hex = [
|
||||||
|
rgb.r.toString(16),
|
||||||
|
rgb.g.toString(16),
|
||||||
|
rgb.b.toString(16)
|
||||||
|
];
|
||||||
|
$.each(hex, function (nr, val) {
|
||||||
|
if (val.length == 1) {
|
||||||
|
hex[nr] = '0' + val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hex.join('');
|
||||||
|
},
|
||||||
|
HSBToHex = function (hsb) {
|
||||||
|
return RGBToHex(HSBToRGB(hsb));
|
||||||
|
},
|
||||||
|
restoreOriginal = function () {
|
||||||
|
var cal = $(this).parent();
|
||||||
|
var col = cal.data('colorpicker').origColor;
|
||||||
|
cal.data('colorpicker').color = col;
|
||||||
|
fillRGBFields(col, cal.get(0));
|
||||||
|
fillHexFields(col, cal.get(0));
|
||||||
|
fillHSBFields(col, cal.get(0));
|
||||||
|
setSelector(col, cal.get(0));
|
||||||
|
setHue(col, cal.get(0));
|
||||||
|
setNewColor(col, cal.get(0));
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
init: function (opt) {
|
||||||
|
opt = $.extend({}, defaults, opt||{});
|
||||||
|
if (typeof opt.color == 'string') {
|
||||||
|
opt.color = HexToHSB(opt.color);
|
||||||
|
} else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) {
|
||||||
|
opt.color = RGBToHSB(opt.color);
|
||||||
|
} else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) {
|
||||||
|
opt.color = fixHSB(opt.color);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.each(function () {
|
||||||
|
if (!$(this).data('colorpickerId')) {
|
||||||
|
var options = $.extend({}, opt);
|
||||||
|
options.origColor = opt.color;
|
||||||
|
var id = 'collorpicker_' + parseInt(Math.random() * 1000);
|
||||||
|
$(this).data('colorpickerId', id);
|
||||||
|
var cal = $(tpl).attr('id', id);
|
||||||
|
if (options.flat) {
|
||||||
|
cal.appendTo(this).show();
|
||||||
|
} else {
|
||||||
|
cal.appendTo(document.body);
|
||||||
|
}
|
||||||
|
options.fields = cal
|
||||||
|
.find('input')
|
||||||
|
.bind('keyup', keyDown)
|
||||||
|
.bind('change', change)
|
||||||
|
.bind('blur', blur)
|
||||||
|
.bind('focus', focus);
|
||||||
|
cal
|
||||||
|
.find('span').bind('mousedown', downIncrement).end()
|
||||||
|
.find('>div.colorpicker_current_color').bind('click', restoreOriginal);
|
||||||
|
options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector);
|
||||||
|
options.selectorIndic = options.selector.find('div div');
|
||||||
|
options.el = this;
|
||||||
|
options.hue = cal.find('div.colorpicker_hue div');
|
||||||
|
cal.find('div.colorpicker_hue').bind('mousedown', downHue);
|
||||||
|
options.newColor = cal.find('div.colorpicker_new_color');
|
||||||
|
options.currentColor = cal.find('div.colorpicker_current_color');
|
||||||
|
cal.data('colorpicker', options);
|
||||||
|
cal.find('div.colorpicker_submit')
|
||||||
|
.bind('mouseenter', enterSubmit)
|
||||||
|
.bind('mouseleave', leaveSubmit)
|
||||||
|
.bind('click', clickSubmit);
|
||||||
|
fillRGBFields(options.color, cal.get(0));
|
||||||
|
fillHSBFields(options.color, cal.get(0));
|
||||||
|
fillHexFields(options.color, cal.get(0));
|
||||||
|
setHue(options.color, cal.get(0));
|
||||||
|
setSelector(options.color, cal.get(0));
|
||||||
|
setCurrentColor(options.color, cal.get(0));
|
||||||
|
setNewColor(options.color, cal.get(0));
|
||||||
|
if (options.flat) {
|
||||||
|
cal.css({
|
||||||
|
position: 'relative',
|
||||||
|
display: 'block'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$(this).bind(options.eventName, show);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showPicker: function() {
|
||||||
|
return this.each( function () {
|
||||||
|
if ($(this).data('colorpickerId')) {
|
||||||
|
show.apply(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hidePicker: function() {
|
||||||
|
return this.each( function () {
|
||||||
|
if ($(this).data('colorpickerId')) {
|
||||||
|
$('#' + $(this).data('colorpickerId')).hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setColor: function(col) {
|
||||||
|
if (typeof col == 'string') {
|
||||||
|
col = HexToHSB(col);
|
||||||
|
} else if (col.r != undefined && col.g != undefined && col.b != undefined) {
|
||||||
|
col = RGBToHSB(col);
|
||||||
|
} else if (col.h != undefined && col.s != undefined && col.b != undefined) {
|
||||||
|
col = fixHSB(col);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.each(function(){
|
||||||
|
if ($(this).data('colorpickerId')) {
|
||||||
|
var cal = $('#' + $(this).data('colorpickerId'));
|
||||||
|
cal.data('colorpicker').color = col;
|
||||||
|
cal.data('colorpicker').origColor = col;
|
||||||
|
fillRGBFields(col, cal.get(0));
|
||||||
|
fillHSBFields(col, cal.get(0));
|
||||||
|
fillHexFields(col, cal.get(0));
|
||||||
|
setHue(col, cal.get(0));
|
||||||
|
setSelector(col, cal.get(0));
|
||||||
|
setCurrentColor(col, cal.get(0));
|
||||||
|
setNewColor(col, cal.get(0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
$.fn.extend({
|
||||||
|
ColorPicker: ColorPicker.init,
|
||||||
|
ColorPickerHide: ColorPicker.hidePicker,
|
||||||
|
ColorPickerShow: ColorPicker.showPicker,
|
||||||
|
ColorPickerSetColor: ColorPicker.setColor
|
||||||
|
});
|
||||||
|
})(jQuery)
|
||||||
BIN
public/vendor/colorpicker/images/Thumbs.db
vendored
Normal file
BIN
public/vendor/colorpicker/images/blank.gif
vendored
Normal file
|
After Width: | Height: | Size: 49 B |
BIN
public/vendor/colorpicker/images/colorpicker_background.png
vendored
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/vendor/colorpicker/images/colorpicker_hex.png
vendored
Normal file
|
After Width: | Height: | Size: 532 B |
BIN
public/vendor/colorpicker/images/colorpicker_hsb_b.png
vendored
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
public/vendor/colorpicker/images/colorpicker_hsb_h.png
vendored
Normal file
|
After Width: | Height: | Size: 1012 B |
BIN
public/vendor/colorpicker/images/colorpicker_hsb_s.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/vendor/colorpicker/images/colorpicker_indic.gif
vendored
Normal file
|
After Width: | Height: | Size: 86 B |
BIN
public/vendor/colorpicker/images/colorpicker_overlay.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/vendor/colorpicker/images/colorpicker_rgb_b.png
vendored
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
public/vendor/colorpicker/images/colorpicker_rgb_g.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/vendor/colorpicker/images/colorpicker_rgb_r.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/vendor/colorpicker/images/colorpicker_select.gif
vendored
Normal file
|
After Width: | Height: | Size: 78 B |
BIN
public/vendor/colorpicker/images/colorpicker_submit.png
vendored
Normal file
|
After Width: | Height: | Size: 984 B |
BIN
public/vendor/colorpicker/images/custom_background.png
vendored
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/vendor/colorpicker/images/custom_hex.png
vendored
Normal file
|
After Width: | Height: | Size: 562 B |
BIN
public/vendor/colorpicker/images/custom_hsb_b.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/vendor/colorpicker/images/custom_hsb_h.png
vendored
Normal file
|
After Width: | Height: | Size: 970 B |
BIN
public/vendor/colorpicker/images/custom_hsb_s.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/vendor/colorpicker/images/custom_indic.gif
vendored
Normal file
|
After Width: | Height: | Size: 86 B |
BIN
public/vendor/colorpicker/images/custom_rgb_b.png
vendored
Normal file
|
After Width: | Height: | Size: 1008 B |
BIN
public/vendor/colorpicker/images/custom_rgb_g.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/vendor/colorpicker/images/custom_rgb_r.png
vendored
Normal file
|
After Width: | Height: | Size: 1018 B |
BIN
public/vendor/colorpicker/images/custom_submit.png
vendored
Normal file
|
After Width: | Height: | Size: 997 B |
BIN
public/vendor/colorpicker/images/select.png
vendored
Normal file
|
After Width: | Height: | Size: 506 B |
BIN
public/vendor/colorpicker/images/select2.png
vendored
Normal file
|
After Width: | Height: | Size: 518 B |
BIN
public/vendor/colorpicker/images/slider.png
vendored
Normal file
|
After Width: | Height: | Size: 315 B |
@@ -252,26 +252,28 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
Categories.moveRecentReplies = function(tid, oldCid, cid, callback) {
|
Categories.moveRecentReplies = function(tid, oldCid, cid, callback) {
|
||||||
function movePost(pid, callback) {
|
function movePost(pid, callback) {
|
||||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
posts.getPostField(pid, 'timestamp', function(err, timestamp) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
RDB.zrem('categories:recent_posts:cid:' + oldCid, pid);
|
RDB.zrem('categories:recent_posts:cid:' + oldCid, pid);
|
||||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||||
|
callback(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
topics.getPids(tid, function(err, pids) {
|
topics.getPids(tid, function(err, pids) {
|
||||||
if (!err) {
|
if(err) {
|
||||||
async.each(pids, movePost, function(err) {
|
return callback(err, null);
|
||||||
if (!err) {
|
|
||||||
callback(null, 1);
|
|
||||||
} else {
|
|
||||||
winston.err(err);
|
|
||||||
callback(err, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
winston.err(err);
|
|
||||||
callback(err, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async.each(pids, movePost, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
callback(null, 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -372,19 +374,6 @@ var RDB = require('./redis.js'),
|
|||||||
return callback(err, null);
|
return callback(err, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPostCategory(pid, callback) {
|
|
||||||
posts.getPostField(pid, 'tid', function(tid) {
|
|
||||||
|
|
||||||
topics.getTopicField(tid, 'cid', function(err, postCid) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, postCid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = 0,
|
var index = 0,
|
||||||
active = false;
|
active = false;
|
||||||
|
|
||||||
@@ -393,7 +382,7 @@ var RDB = require('./redis.js'),
|
|||||||
return active === false && index < pids.length;
|
return active === false && index < pids.length;
|
||||||
},
|
},
|
||||||
function(callback) {
|
function(callback) {
|
||||||
getPostCategory(pids[index], function(err, postCid) {
|
posts.getCidByPid(pids[index], function(err, postCid) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@@ -411,7 +400,6 @@ var RDB = require('./redis.js'),
|
|||||||
return callback(err, null);
|
return callback(err, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
callback(null, active);
|
callback(null, active);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ var RDB = require('./redis.js'),
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
posts.getPostFields(pid, ['uid', 'timestamp'], function (postData) {
|
posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) {
|
||||||
|
|
||||||
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
||||||
if (hasFavourited === 0) {
|
if (hasFavourited === 0) {
|
||||||
@@ -57,7 +57,7 @@ var RDB = require('./redis.js'),
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
posts.getPostField(pid, 'uid', function (uid_of_poster) {
|
posts.getPostField(pid, 'uid', function (err, uid_of_poster) {
|
||||||
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
||||||
if (hasFavourited === 1) {
|
if (hasFavourited === 1) {
|
||||||
RDB.srem('pid:' + pid + ':users_favourited', uid);
|
RDB.srem('pid:' + pid + ':users_favourited', uid);
|
||||||
|
|||||||
113
src/feed.js
@@ -2,12 +2,14 @@
|
|||||||
var RDB = require('./redis.js'),
|
var RDB = require('./redis.js'),
|
||||||
posts = require('./posts.js'),
|
posts = require('./posts.js'),
|
||||||
topics = require('./topics.js'),
|
topics = require('./topics.js'),
|
||||||
|
categories = require('./categories'),
|
||||||
|
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
rss = require('rss'),
|
rss = require('rss'),
|
||||||
winston = require('winston'),
|
winston = require('winston'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
categories = require('./categories');
|
async = require('async');
|
||||||
|
|
||||||
Feed.defaults = {
|
Feed.defaults = {
|
||||||
ttl: 60,
|
ttl: 60,
|
||||||
@@ -26,43 +28,43 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
Feed.updateTopic = function (tid, callback) {
|
Feed.updateTopic = function (tid, callback) {
|
||||||
if (process.env.NODE_ENV === 'development') winston.info('[rss] Updating RSS feeds for topic ' + tid);
|
|
||||||
|
|
||||||
topics.getTopicWithPosts(tid, 0, 0, -1, function (err, topicData) {
|
topics.getTopicWithPosts(tid, 0, 0, -1, function (err, topicData) {
|
||||||
if (err) return callback(new Error('topic-invalid'));
|
if (err) return callback(new Error('topic-invalid'));
|
||||||
|
|
||||||
var feed = new rss({
|
var feed = new rss({
|
||||||
title: topicData.topic_name,
|
title: topicData.topic_name,
|
||||||
description: topicData.main_posts[0].content,
|
description: topicData.main_posts[0].content,
|
||||||
feed_url: Feed.defaults.baseUrl + '/topics/' + tid + '.rss',
|
feed_url: Feed.defaults.baseUrl + '/topics/' + tid + '.rss',
|
||||||
site_url: nconf.get('url') + 'topic/' + topicData.slug,
|
site_url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||||
image_url: topicData.main_posts[0].picture,
|
image_url: topicData.main_posts[0].picture,
|
||||||
author: topicData.main_posts[0].username,
|
author: topicData.main_posts[0].username,
|
||||||
ttl: Feed.defaults.ttl
|
ttl: Feed.defaults.ttl
|
||||||
}),
|
}),
|
||||||
topic_posts = topicData.main_posts.concat(topicData.posts),
|
topic_posts = topicData.main_posts.concat(topicData.posts),
|
||||||
title, postData, dateStamp;
|
dateStamp;
|
||||||
|
|
||||||
// Add pubDate if topic contains posts
|
// Add pubDate if topic contains posts
|
||||||
if (topicData.main_posts.length > 0) feed.pubDate = new Date(parseInt(topicData.main_posts[0].timestamp, 10)).toUTCString();
|
if (topicData.main_posts.length > 0) feed.pubDate = new Date(parseInt(topicData.main_posts[0].timestamp, 10)).toUTCString();
|
||||||
|
|
||||||
for (var i = 0, ii = topic_posts.length; i < ii; i++) {
|
async.each(topic_posts, function(postData, next) {
|
||||||
if (topic_posts[i].deleted === '0') {
|
if (postData.deleted === '0') {
|
||||||
postData = topic_posts[i];
|
|
||||||
dateStamp = new Date(parseInt(postData.edited === '0' ? postData.timestamp : postData.edited, 10)).toUTCString();
|
dateStamp = new Date(parseInt(postData.edited === '0' ? postData.timestamp : postData.edited, 10)).toUTCString();
|
||||||
title = 'Reply to ' + topicData.topic_name + ' on ' + dateStamp;
|
|
||||||
|
|
||||||
feed.item({
|
feed.item({
|
||||||
title: title,
|
title: 'Reply to ' + topicData.topic_name + ' on ' + dateStamp,
|
||||||
description: postData.content,
|
description: postData.content,
|
||||||
url: nconf.get('url') + 'topic/' + topicData.slug + '#' + postData.pid,
|
url: nconf.get('url') + 'topic/' + topicData.slug + '#' + postData.pid,
|
||||||
author: postData.username,
|
author: postData.username,
|
||||||
date: dateStamp
|
date: dateStamp
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Feed.saveFeed('feeds/topics/' + tid + '.rss', feed, function (err) {
|
next();
|
||||||
|
}, function() {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
winston.info('[rss] Re-generated RSS Feed for tid ' + tid + '.');
|
||||||
|
}
|
||||||
|
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -70,40 +72,75 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
Feed.updateCategory = function (cid, callback) {
|
Feed.updateCategory = function (cid, callback) {
|
||||||
if (process.env.NODE_ENV === 'development') winston.info('[rss] Updating RSS feeds for category ' + cid);
|
|
||||||
categories.getCategoryById(cid, 0, function (err, categoryData) {
|
categories.getCategoryById(cid, 0, function (err, categoryData) {
|
||||||
if (err) return callback(new Error('category-invalid'));
|
if (err) return callback(new Error('category-invalid'));
|
||||||
|
|
||||||
var feed = new rss({
|
var feed = new rss({
|
||||||
title: categoryData.category_name,
|
title: categoryData.category_name,
|
||||||
description: categoryData.category_description,
|
description: categoryData.category_description,
|
||||||
feed_url: Feed.defaults.baseUrl + '/categories/' + cid + '.rss',
|
feed_url: Feed.defaults.baseUrl + '/categories/' + cid + '.rss',
|
||||||
site_url: nconf.get('url') + 'category/' + categoryData.category_id,
|
site_url: nconf.get('url') + 'category/' + categoryData.category_id,
|
||||||
ttl: Feed.defaults.ttl
|
ttl: Feed.defaults.ttl
|
||||||
}),
|
});
|
||||||
topics = categoryData.topics,
|
|
||||||
title, topicData, dateStamp;
|
|
||||||
|
|
||||||
// Add pubDate if category has topics
|
// Add pubDate if category has topics
|
||||||
if (categoryData.topics.length > 0) feed.pubDate = new Date(parseInt(categoryData.topics[0].lastposttime, 10)).toUTCString();
|
if (categoryData.topics.length > 0) feed.pubDate = new Date(parseInt(categoryData.topics[0].lastposttime, 10)).toUTCString();
|
||||||
|
|
||||||
for (var i = 0, ii = topics.length; i < ii; i++) {
|
async.eachSeries(categoryData.topics, function(topicData, next) {
|
||||||
topicData = topics[i];
|
|
||||||
dateStamp = new Date(parseInt(topicData.lastposttime, 10)).toUTCString();
|
|
||||||
title = topics[i].title;
|
|
||||||
|
|
||||||
feed.item({
|
feed.item({
|
||||||
title: title,
|
title: topicData.title,
|
||||||
url: nconf.get('url') + 'topic/' + topicData.slug,
|
url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||||
author: topicData.username,
|
author: topicData.username,
|
||||||
date: dateStamp
|
date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString()
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
Feed.saveFeed('feeds/categories/' + cid + '.rss', feed, function (err) {
|
next();
|
||||||
if (callback) callback();
|
}, function() {
|
||||||
|
Feed.saveFeed('feeds/categories/' + cid + '.rss', feed, function (err) {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
winston.info('[rss] Re-generated RSS Feed for cid ' + cid + '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) callback();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Feed.updateRecent = function(callback) {
|
||||||
|
console.log('entered');
|
||||||
|
if (process.env.NODE_ENV === 'development') winston.info('[rss] Updating Recent Posts RSS feed');
|
||||||
|
topics.getLatestTopics(0, 0, 19, undefined, function (err, recentData) {
|
||||||
|
var feed = new rss({
|
||||||
|
title: 'Recently Active Topics',
|
||||||
|
description: 'A list of topics that have been active within the past 24 hours',
|
||||||
|
feed_url: Feed.defaults.baseUrl + '/recent.rss',
|
||||||
|
site_url: nconf.get('url') + 'recent',
|
||||||
|
ttl: Feed.defaults.ttl
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add pubDate if recent topics list contains topics
|
||||||
|
if (recentData.topics.length > 0) {
|
||||||
|
feed.pubDate = new Date(parseInt(recentData.topics[0].lastposttime, 10)).toUTCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachSeries(recentData.topics, function(topicData, next) {
|
||||||
|
feed.item({
|
||||||
|
title: topicData.title,
|
||||||
|
url: nconf.get('url') + 'topic/' + topicData.slug,
|
||||||
|
author: topicData.username,
|
||||||
|
date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString()
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
}, function() {
|
||||||
|
Feed.saveFeed('feeds/recent.rss', feed, function (err) {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
winston.info('[rss] Re-generated "recent posts" RSS Feed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}(exports));
|
}(exports));
|
||||||
@@ -56,6 +56,8 @@
|
|||||||
results.base.count = results.users.length;
|
results.base.count = results.users.length;
|
||||||
results.base.members = results.users;
|
results.base.members = results.users;
|
||||||
|
|
||||||
|
results.base.deletable = (results.base.gid !== '1');
|
||||||
|
|
||||||
callback(err, results.base);
|
callback(err, results.base);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -103,7 +105,9 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroy: function (gid, callback) {
|
destroy: function (gid, callback) {
|
||||||
RDB.hset('gid:' + gid, 'deleted', '1', callback);
|
if (gid !== 1) {
|
||||||
|
RDB.hset('gid:' + gid, 'deleted', '1', callback);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
join: function (gid, uid, callback) {
|
join: function (gid, uid, callback) {
|
||||||
RDB.sadd('gid:' + gid + ':members', uid, callback);
|
RDB.sadd('gid:' + gid + ':members', uid, callback);
|
||||||
|
|||||||
29
src/imgur.js
@@ -1,12 +1,11 @@
|
|||||||
var request = require('request');
|
var request = require('request'),
|
||||||
|
winston = require('winston');
|
||||||
|
|
||||||
|
|
||||||
(function (imgur) {
|
(function (imgur) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var clientID = '';
|
imgur.upload = function (clientID, image, type, callback) {
|
||||||
|
|
||||||
imgur.upload = function (image, type, callback) {
|
|
||||||
var options = {
|
var options = {
|
||||||
url: 'https://api.imgur.com/3/upload.json',
|
url: 'https://api.imgur.com/3/upload.json',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -15,21 +14,27 @@ var request = require('request');
|
|||||||
};
|
};
|
||||||
|
|
||||||
var post = request.post(options, function (err, req, body) {
|
var post = request.post(options, function (err, req, body) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
callback(err, JSON.parse(body));
|
var response = JSON.parse(body);
|
||||||
} catch (e) {
|
if(response.success) {
|
||||||
callback(err, body);
|
callback(null, response.data);
|
||||||
|
} else {
|
||||||
|
callback(new Error(response.data.error.message), null);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
winston.error('Unable to parse Imgur json response. [' + body +']');
|
||||||
|
callback(e, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var upload = post.form({
|
post.form({
|
||||||
type: type,
|
type: type,
|
||||||
image: image
|
image: image
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
imgur.setClientID = function (id) {
|
|
||||||
clientID = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
}(exports));
|
}(exports));
|
||||||
@@ -63,13 +63,14 @@ var async = require('async'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (setupVal && setupVal instanceof Object) {
|
if (setupVal && setupVal instanceof Object) {
|
||||||
if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:email']) {
|
if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:password:confirm'] && setupVal['admin:email']) {
|
||||||
install.values = setupVal;
|
install.values = setupVal;
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
winston.error('Required values are missing for automated setup:');
|
winston.error('Required values are missing for automated setup:');
|
||||||
if (!setupVal['admin:username']) winston.error(' admin:username');
|
if (!setupVal['admin:username']) winston.error(' admin:username');
|
||||||
if (!setupVal['admin:password']) winston.error(' admin:password');
|
if (!setupVal['admin:password']) winston.error(' admin:password');
|
||||||
|
if (!setupVal['admin:password:confirm']) winston.error(' admin:password:confirm');
|
||||||
if (!setupVal['admin:email']) winston.error(' admin:email');
|
if (!setupVal['admin:email']) winston.error(' admin:email');
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
@@ -252,6 +253,11 @@ var async = require('async'),
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, next);
|
}, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
// Upgrading schema
|
||||||
|
var Upgrade = require('./upgrade');
|
||||||
|
Upgrade.upgrade(next);
|
||||||
}
|
}
|
||||||
], function (err) {
|
], function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -277,18 +283,32 @@ var async = require('async'),
|
|||||||
description: 'Administrator email address',
|
description: 'Administrator email address',
|
||||||
pattern: /.+@.+/,
|
pattern: /.+@.+/,
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}],
|
||||||
|
passwordQuestions = [{
|
||||||
name: 'password',
|
name: 'password',
|
||||||
description: 'Password',
|
description: 'Password',
|
||||||
required: true,
|
required: true,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
type: 'string'
|
type: 'string'
|
||||||
|
}, {
|
||||||
|
name: 'password:confirm',
|
||||||
|
description: 'Confirm Password',
|
||||||
|
required: true,
|
||||||
|
hidden: true,
|
||||||
|
type: 'string'
|
||||||
}],
|
}],
|
||||||
success = function(err, results) {
|
success = function(err, results) {
|
||||||
if (!results) {
|
if (!results) {
|
||||||
return callback(new Error('aborted'));
|
return callback(new Error('aborted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the passwords match
|
||||||
|
if (results['password:confirm'] !== results.password) {
|
||||||
|
winston.warn("Passwords did not match, please try again");
|
||||||
|
// Re-prompt password questions.
|
||||||
|
return retryPassword(results);
|
||||||
|
}
|
||||||
|
|
||||||
nconf.set('bcrypt_rounds', 12);
|
nconf.set('bcrypt_rounds', 12);
|
||||||
User.create(results.username, results.password, results.email, function (err, uid) {
|
User.create(results.username, results.password, results.email, function (err, uid) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -306,14 +326,33 @@ var async = require('async'),
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
retryPassword = function (originalResults) {
|
||||||
|
// Ask only the password questions
|
||||||
|
prompt.get(passwordQuestions, function (err, results) {
|
||||||
|
if (!results) {
|
||||||
|
return callback(new Error('aborted'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the original data with newly collected password
|
||||||
|
originalResults.password = results.password;
|
||||||
|
originalResults['password:confirm'] = results['password:confirm'];
|
||||||
|
|
||||||
|
// Send back to success to handle
|
||||||
|
success(err, originalResults);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add the password questions
|
||||||
|
questions = questions.concat(passwordQuestions);
|
||||||
|
|
||||||
if (!install.values) prompt.get(questions, success);
|
if (!install.values) prompt.get(questions, success);
|
||||||
else {
|
else {
|
||||||
var results = {
|
var results = {
|
||||||
username: install.values['admin:username'],
|
username: install.values['admin:username'],
|
||||||
email: install.values['admin:email'],
|
email: install.values['admin:email'],
|
||||||
password: install.values['admin:password']
|
password: install.values['admin:password'],
|
||||||
|
'password:confirm': install.values['admin:password:confirm']
|
||||||
};
|
};
|
||||||
|
|
||||||
success(null, results);
|
success(null, results);
|
||||||
|
|||||||
77
src/meta.js
@@ -1,5 +1,6 @@
|
|||||||
var utils = require('./../public/src/utils.js'),
|
var utils = require('./../public/src/utils.js'),
|
||||||
RDB = require('./redis.js'),
|
RDB = require('./redis.js'),
|
||||||
|
plugins = require('./plugins'),
|
||||||
async = require('async'),
|
async = require('async'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
@@ -102,7 +103,9 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
var themeData = {
|
var themeData = {
|
||||||
'theme:type': data.type,
|
'theme:type': data.type,
|
||||||
'theme:id': data.id,
|
'theme:id': data.id,
|
||||||
'theme:staticDir': ''
|
'theme:staticDir': '',
|
||||||
|
'theme:templates': '',
|
||||||
|
'theme:src': ''
|
||||||
};
|
};
|
||||||
|
|
||||||
switch(data.type) {
|
switch(data.type) {
|
||||||
@@ -205,44 +208,46 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
],
|
],
|
||||||
minFile: path.join(__dirname, '..', 'public/src/nodebb.min.js'),
|
minFile: path.join(__dirname, '..', 'public/src/nodebb.min.js'),
|
||||||
get: function (callback) {
|
get: function (callback) {
|
||||||
var mtime,
|
plugins.fireHook('filter:scripts.get', this.scripts, function(err, scripts) {
|
||||||
jsPaths = this.scripts.map(function (jsPath) {
|
var mtime,
|
||||||
return path.join(__dirname, '..', '/public', jsPath);
|
jsPaths = scripts.map(function (jsPath) {
|
||||||
});
|
return path.join(__dirname, '..', '/public', jsPath);
|
||||||
|
});
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'development') {
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
mtime: function (next) {
|
mtime: function (next) {
|
||||||
async.map(jsPaths, fs.stat, function (err, stats) {
|
async.map(jsPaths, fs.stat, function (err, stats) {
|
||||||
async.reduce(stats, 0, function (memo, item, callback) {
|
async.reduce(stats, 0, function (memo, item, callback) {
|
||||||
mtime = +new Date(item.mtime);
|
mtime = +new Date(item.mtime);
|
||||||
callback(null, mtime > memo ? mtime : memo);
|
callback(null, mtime > memo ? mtime : memo);
|
||||||
}, next);
|
}, next);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
minFile: function (next) {
|
minFile: function (next) {
|
||||||
if (!fs.existsSync(Meta.js.minFile)) {
|
if (!fs.existsSync(Meta.js.minFile)) {
|
||||||
if (process.env.NODE_ENV === 'development') winston.warn('No minified client-side library found');
|
if (process.env.NODE_ENV === 'development') winston.warn('No minified client-side library found');
|
||||||
return next(null, 0);
|
return next(null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.stat(Meta.js.minFile, function (err, stat) {
|
||||||
|
next(err, +new Date(stat.mtime));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}, function (err, results) {
|
||||||
fs.stat(Meta.js.minFile, function (err, stat) {
|
if (results.minFile > results.mtime) {
|
||||||
next(err, +new Date(stat.mtime));
|
if (process.env.NODE_ENV === 'development') winston.info('No changes to client-side libraries -- skipping minification');
|
||||||
});
|
|
||||||
}
|
|
||||||
}, function (err, results) {
|
|
||||||
if (results.minFile > results.mtime) {
|
|
||||||
if (process.env.NODE_ENV === 'development') winston.info('No changes to client-side libraries -- skipping minification');
|
|
||||||
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
|
||||||
} else {
|
|
||||||
Meta.js.minify(function () {
|
|
||||||
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
||||||
});
|
} else {
|
||||||
}
|
Meta.js.minify(function () {
|
||||||
});
|
callback(null, [path.relative(path.join(__dirname, '../public'), Meta.js.minFile)]);
|
||||||
} else {
|
});
|
||||||
callback(null, this.scripts);
|
}
|
||||||
}
|
});
|
||||||
|
} else {
|
||||||
|
callback(null, scripts);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
minify: function (callback) {
|
minify: function (callback) {
|
||||||
var uglifyjs = require('uglify-js'),
|
var uglifyjs = require('uglify-js'),
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ var RDB = require('./redis.js'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getThreadPrivileges(next) {
|
function getThreadPrivileges(next) {
|
||||||
posts.getPostField(pid, 'tid', function(tid) {
|
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||||
threadTools.privileges(tid, uid, function(privileges) {
|
threadTools.privileges(tid, uid, function(privileges) {
|
||||||
next(null, privileges);
|
next(null, privileges);
|
||||||
});
|
});
|
||||||
@@ -42,7 +42,7 @@ var RDB = require('./redis.js'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isOwnPost(next) {
|
function isOwnPost(next) {
|
||||||
posts.getPostField(pid, 'uid', function(author) {
|
posts.getPostField(pid, 'uid', function(err, author) {
|
||||||
next(null, parseInt(author, 10) === parseInt(uid, 10));
|
next(null, parseInt(author, 10) === parseInt(uid, 10));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
posts.getPostField(pid, 'tid', function(tid) {
|
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||||
PostTools.isMain(pid, tid, function(isMainPost) {
|
PostTools.isMain(pid, tid, function(isMainPost) {
|
||||||
if (isMainPost) {
|
if (isMainPost) {
|
||||||
topics.setTopicField(tid, 'title', title);
|
topics.setTopicField(tid, 'title', title);
|
||||||
@@ -132,41 +132,47 @@ var RDB = require('./redis.js'),
|
|||||||
RDB.decr('totalpostcount');
|
RDB.decr('totalpostcount');
|
||||||
postSearch.remove(pid);
|
postSearch.remove(pid);
|
||||||
|
|
||||||
posts.getPostFields(pid, ['tid', 'uid'], function(postData) {
|
posts.getPostFields(pid, ['tid', 'uid'], function(err, postData) {
|
||||||
RDB.hincrby('topic:' + postData.tid, 'postcount', -1);
|
RDB.hincrby('topic:' + postData.tid, 'postcount', -1);
|
||||||
|
|
||||||
user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) {
|
user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) {
|
||||||
RDB.zadd('users:postcount', postcount, postData.uid);
|
RDB.zadd('users:postcount', postcount, postData.uid);
|
||||||
});
|
});
|
||||||
|
|
||||||
io.sockets. in ('topic_' + postData.tid).emit('event:post_deleted', {
|
|
||||||
pid: pid
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete the thread if it is the last undeleted post
|
// Delete the thread if it is the last undeleted post
|
||||||
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
|
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
|
||||||
if (err && err.message === 'no-undeleted-pids-found') {
|
if (err && err.message === 'no-undeleted-pids-found') {
|
||||||
threadTools.delete(postData.tid, -1, function(err) {
|
threadTools.delete(postData.tid, -1, function(err) {
|
||||||
if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
|
if (err) {
|
||||||
|
winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
posts.getPostField(pid, 'timestamp', function(err, timestamp) {
|
||||||
topics.updateTimestamp(postData.tid, timestamp);
|
topics.updateTimestamp(postData.tid, timestamp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Feed.updateTopic(postData.tid);
|
Feed.updateTopic(postData.tid);
|
||||||
|
Feed.updateRecent();
|
||||||
|
|
||||||
callback();
|
callback(null);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
PostTools.privileges(pid, uid, function(privileges) {
|
posts.getPostField(pid, 'deleted', function(err, deleted) {
|
||||||
if (privileges.editable) {
|
if(deleted === '1') {
|
||||||
success();
|
return callback(new Error('Post already deleted!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostTools.privileges(pid, uid, function(privileges) {
|
||||||
|
if (privileges.editable) {
|
||||||
|
success();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PostTools.restore = function(uid, pid, callback) {
|
PostTools.restore = function(uid, pid, callback) {
|
||||||
@@ -174,17 +180,13 @@ var RDB = require('./redis.js'),
|
|||||||
posts.setPostField(pid, 'deleted', 0);
|
posts.setPostField(pid, 'deleted', 0);
|
||||||
RDB.incr('totalpostcount');
|
RDB.incr('totalpostcount');
|
||||||
|
|
||||||
posts.getPostFields(pid, ['tid', 'uid', 'content'], function(postData) {
|
posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) {
|
||||||
RDB.hincrby('topic:' + postData.tid, 'postcount', 1);
|
RDB.hincrby('topic:' + postData.tid, 'postcount', 1);
|
||||||
|
|
||||||
user.incrementUserFieldBy(postData.uid, 'postcount', 1);
|
user.incrementUserFieldBy(postData.uid, 'postcount', 1);
|
||||||
|
|
||||||
io.sockets. in ('topic_' + postData.tid).emit('event:post_restored', {
|
|
||||||
pid: pid
|
|
||||||
});
|
|
||||||
|
|
||||||
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
|
threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) {
|
||||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
posts.getPostField(pid, 'timestamp', function(err, timestamp) {
|
||||||
topics.updateTimestamp(postData.tid, timestamp);
|
topics.updateTimestamp(postData.tid, timestamp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -197,6 +199,7 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
|
|
||||||
Feed.updateTopic(postData.tid);
|
Feed.updateTopic(postData.tid);
|
||||||
|
Feed.updateRecent();
|
||||||
|
|
||||||
postSearch.index(postData.content, pid);
|
postSearch.index(postData.content, pid);
|
||||||
|
|
||||||
@@ -204,10 +207,16 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
PostTools.privileges(pid, uid, function(privileges) {
|
posts.getPostField(pid, 'deleted', function(err, deleted) {
|
||||||
if (privileges.editable) {
|
if(deleted === '0') {
|
||||||
success();
|
return callback(new Error('Post already restored'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostTools.privileges(pid, uid, function(privileges) {
|
||||||
|
if (privileges.editable) {
|
||||||
|
success();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
430
src/posts.js
@@ -2,6 +2,7 @@ var RDB = require('./redis.js'),
|
|||||||
utils = require('./../public/src/utils.js'),
|
utils = require('./../public/src/utils.js'),
|
||||||
user = require('./user.js'),
|
user = require('./user.js'),
|
||||||
topics = require('./topics.js'),
|
topics = require('./topics.js'),
|
||||||
|
categories = require('./categories.js'),
|
||||||
favourites = require('./favourites.js'),
|
favourites = require('./favourites.js'),
|
||||||
threadTools = require('./threadTools.js'),
|
threadTools = require('./threadTools.js'),
|
||||||
postTools = require('./postTools'),
|
postTools = require('./postTools'),
|
||||||
@@ -13,11 +14,183 @@ var RDB = require('./redis.js'),
|
|||||||
postSearch = reds.createSearch('nodebbpostsearch'),
|
postSearch = reds.createSearch('nodebbpostsearch'),
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
meta = require('./meta.js'),
|
meta = require('./meta.js'),
|
||||||
|
validator = require('validator'),
|
||||||
winston = require('winston');
|
winston = require('winston');
|
||||||
|
|
||||||
(function(Posts) {
|
(function(Posts) {
|
||||||
var customUserInfo = {};
|
var customUserInfo = {};
|
||||||
|
|
||||||
|
Posts.create = function(uid, tid, content, callback) {
|
||||||
|
if (uid === null) {
|
||||||
|
callback(new Error('invalid-user'), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
topics.isLocked(tid, function(err, locked) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
} else if(locked) {
|
||||||
|
callback(new Error('topic-locked'), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
RDB.incr('global:next_post_id', function(err, pid) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.fireHook('filter:post.save', content, function(err, newContent) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
content = newContent;
|
||||||
|
|
||||||
|
var timestamp = Date.now(),
|
||||||
|
postData = {
|
||||||
|
'pid': pid,
|
||||||
|
'uid': uid,
|
||||||
|
'tid': tid,
|
||||||
|
'content': content,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'reputation': 0,
|
||||||
|
'editor': '',
|
||||||
|
'edited': 0,
|
||||||
|
'deleted': 0,
|
||||||
|
'fav_button_class': '',
|
||||||
|
'fav_star_class': 'icon-star-empty',
|
||||||
|
'show_banned': 'hide',
|
||||||
|
'relativeTime': new Date(timestamp).toISOString(),
|
||||||
|
'post_rep': '0',
|
||||||
|
'edited-class': 'none',
|
||||||
|
'relativeEditTime': ''
|
||||||
|
};
|
||||||
|
|
||||||
|
RDB.hmset('post:' + pid, postData);
|
||||||
|
|
||||||
|
topics.addPostToTopic(tid, pid);
|
||||||
|
topics.increasePostCount(tid);
|
||||||
|
topics.updateTimestamp(tid, timestamp);
|
||||||
|
|
||||||
|
RDB.incr('totalpostcount');
|
||||||
|
|
||||||
|
topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) {
|
||||||
|
|
||||||
|
RDB.handle(err);
|
||||||
|
|
||||||
|
var cid = topicData.cid;
|
||||||
|
|
||||||
|
feed.updateTopic(tid);
|
||||||
|
feed.updateRecent();
|
||||||
|
|
||||||
|
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||||
|
|
||||||
|
if(topicData.pinned === '0')
|
||||||
|
RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
|
||||||
|
|
||||||
|
RDB.scard('cid:' + cid + ':active_users', function(err, amount) {
|
||||||
|
if (amount > 16) {
|
||||||
|
RDB.spop('cid:' + cid + ':active_users');
|
||||||
|
}
|
||||||
|
|
||||||
|
categories.addActiveUser(cid, uid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
user.onNewPostMade(uid, tid, pid, timestamp);
|
||||||
|
|
||||||
|
plugins.fireHook('filter:post.get', postData, function(err, newPostData) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
postData = newPostData;
|
||||||
|
|
||||||
|
postTools.parse(postData.content, function(err, content) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
postData.content = content;
|
||||||
|
|
||||||
|
plugins.fireHook('action:post.save', postData);
|
||||||
|
|
||||||
|
postSearch.index(content, pid);
|
||||||
|
|
||||||
|
callback(null, postData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.reply = function(tid, uid, content, callback) {
|
||||||
|
if(content) {
|
||||||
|
content = content.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content || content.length < meta.config.minimumPostLength) {
|
||||||
|
callback(new Error('content-too-short'), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Posts.create(uid, tid, content, function(err, postData) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
} else if(!postData) {
|
||||||
|
callback(new Error('reply-error'), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.parallel([
|
||||||
|
function(next) {
|
||||||
|
topics.markUnRead(tid, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
topics.markAsRead(tid, uid);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Posts.getCidByPid(postData.pid, function(err, cid) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
RDB.del('cid:' + cid + ':read_by_uid');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
threadTools.notifyFollowers(tid, uid);
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
Posts.addUserInfoToPost(postData, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var socketData = {
|
||||||
|
posts: [postData]
|
||||||
|
};
|
||||||
|
|
||||||
|
io.sockets.in('topic_' + tid).emit('event:new_post', socketData);
|
||||||
|
io.sockets.in('recent_posts').emit('event:new_post', socketData);
|
||||||
|
io.sockets.in('user/' + uid).emit('event:new_post', socketData);
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err, results) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, 'Reply successful');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Posts.getPostsByTid = function(tid, start, end, callback) {
|
Posts.getPostsByTid = function(tid, start, end, callback) {
|
||||||
RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
|
RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
|
||||||
RDB.handle(err);
|
RDB.handle(err);
|
||||||
@@ -86,7 +259,7 @@ var RDB = require('./redis.js'),
|
|||||||
function getPostSummary(pid, callback) {
|
function getPostSummary(pid, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(next) {
|
function(next) {
|
||||||
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) {
|
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(err, postData) {
|
||||||
if (postData.deleted === '1') return callback(null);
|
if (postData.deleted === '1') return callback(null);
|
||||||
else {
|
else {
|
||||||
postData.relativeTime = new Date(parseInt(postData.timestamp || 0, 10)).toISOString();
|
postData.relativeTime = new Date(parseInt(postData.timestamp || 0, 10)).toISOString();
|
||||||
@@ -100,12 +273,15 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(postData, next) {
|
function(postData, next) {
|
||||||
topics.getTopicFields(postData.tid, ['slug', 'deleted'], function(err, topicData) {
|
topics.getTopicFields(postData.tid, ['title', 'cid', 'slug', 'deleted'], function(err, topicData) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
else if (topicData.deleted === '1') return callback(null);
|
else if (topicData.deleted === '1') return callback(null);
|
||||||
|
categories.getCategoryField(topicData.cid, 'name', function(err, categoryData) {
|
||||||
postData.topicSlug = topicData.slug;
|
postData.category_name = categoryData;
|
||||||
next(null, postData);
|
postData.title = validator.sanitize(topicData.title).escape();
|
||||||
|
postData.topicSlug = topicData.slug;
|
||||||
|
next(null, postData);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(postData, next) {
|
function(postData, next) {
|
||||||
@@ -131,51 +307,48 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this function is never called except from some debug route. clean up?
|
|
||||||
Posts.getPostData = function(pid, callback) {
|
Posts.getPostData = function(pid, callback) {
|
||||||
RDB.hgetall('post:' + pid, function(err, data) {
|
RDB.hgetall('post:' + pid, function(err, data) {
|
||||||
if (err === null) {
|
if(err) {
|
||||||
plugins.fireHook('filter:post.get', data, function(err, newData) {
|
return callback(err, null);
|
||||||
if (!err) callback(newData);
|
|
||||||
else callback(data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
winston.error(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins.fireHook('filter:post.get', data, function(err, newData) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
callback(null, newData);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Posts.getPostFields = function(pid, fields, callback) {
|
Posts.getPostFields = function(pid, fields, callback) {
|
||||||
RDB.hmgetObject('post:' + pid, fields, function(err, data) {
|
RDB.hmgetObject('post:' + pid, fields, function(err, data) {
|
||||||
if (err === null) {
|
if(err) {
|
||||||
// TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this:
|
return callback(err, null);
|
||||||
data = data || {};
|
|
||||||
data.pid = pid;
|
|
||||||
data.fields = fields;
|
|
||||||
|
|
||||||
plugins.fireHook('filter:post.getFields', data, function(err, data) {
|
|
||||||
callback(data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this:
|
||||||
|
data = data || {};
|
||||||
|
data.pid = pid;
|
||||||
|
data.fields = fields;
|
||||||
|
|
||||||
|
plugins.fireHook('filter:post.getFields', data, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
callback(null, data);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Posts.getPostField = function(pid, field, callback) {
|
Posts.getPostField = function(pid, field, callback) {
|
||||||
RDB.hget('post:' + pid, field, function(err, data) {
|
Posts.getPostFields(pid, [field], function(err, data) {
|
||||||
if (err === null) {
|
if(err) {
|
||||||
// TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this:
|
return callback(err, null);
|
||||||
data = data || {};
|
|
||||||
data.pid = pid;
|
|
||||||
data.field = field;
|
|
||||||
|
|
||||||
plugins.fireHook('filter:post.getField', data, function(err, data) {
|
|
||||||
callback(data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback(null, data[field]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,8 +365,8 @@ var RDB = require('./redis.js'),
|
|||||||
var posts = [],
|
var posts = [],
|
||||||
multi = RDB.multi();
|
multi = RDB.multi();
|
||||||
|
|
||||||
for(var x=0,numPids=pids.length;x<numPids;x++) {
|
for(var x=0, numPids=pids.length; x<numPids; x++) {
|
||||||
multi.hgetall("post:"+pids[x]);
|
multi.hgetall("post:" + pids[x]);
|
||||||
}
|
}
|
||||||
|
|
||||||
multi.exec(function (err, replies) {
|
multi.exec(function (err, replies) {
|
||||||
@@ -204,12 +377,11 @@ var RDB = require('./redis.js'),
|
|||||||
postData['edited-class'] = postData.editor !== '' ? '' : 'none';
|
postData['edited-class'] = postData.editor !== '' ? '' : 'none';
|
||||||
try {
|
try {
|
||||||
postData.relativeTime = new Date(parseInt(postData.timestamp,10)).toISOString();
|
postData.relativeTime = new Date(parseInt(postData.timestamp,10)).toISOString();
|
||||||
postData['relativeEditTime'] = postData.edited !== '0' ? (new Date(parseInt(postData.edited,10)).toISOString()) : '';
|
postData.relativeEditTime = postData.edited !== '0' ? (new Date(parseInt(postData.edited,10)).toISOString()) : '';
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
winston.err('invalid time value');
|
winston.err('invalid time value');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (postData.uploadedImages) {
|
if (postData.uploadedImages) {
|
||||||
try {
|
try {
|
||||||
postData.uploadedImages = JSON.parse(postData.uploadedImages);
|
postData.uploadedImages = JSON.parse(postData.uploadedImages);
|
||||||
@@ -238,17 +410,23 @@ var RDB = require('./redis.js'),
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Posts.get_cid_by_pid = function(pid, callback) {
|
Posts.getCidByPid = function(pid, callback) {
|
||||||
Posts.getPostField(pid, 'tid', function(tid) {
|
Posts.getPostField(pid, 'tid', function(err, tid) {
|
||||||
if (tid) {
|
if(err) {
|
||||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
return callback(err, null);
|
||||||
if (cid) {
|
|
||||||
callback(cid);
|
|
||||||
} else {
|
|
||||||
callback(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cid) {
|
||||||
|
callback(null, cid);
|
||||||
|
} else {
|
||||||
|
callback(new Error('invalid-category-id'), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,157 +449,19 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Posts.reply = function(tid, uid, content, callback) {
|
|
||||||
if(content) {
|
|
||||||
content = content.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!content || content.length < meta.config.minimumPostLength) {
|
|
||||||
callback(new Error('content-too-short'), null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Posts.create(uid, tid, content, function(postData) {
|
|
||||||
if (postData) {
|
|
||||||
|
|
||||||
topics.markUnRead(tid);
|
|
||||||
|
|
||||||
Posts.get_cid_by_pid(postData.pid, function(cid) {
|
|
||||||
RDB.del('cid:' + cid + ':read_by_uid', function(err, data) {
|
|
||||||
topics.markAsRead(tid, uid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
threadTools.notifyFollowers(tid, uid);
|
|
||||||
|
|
||||||
Posts.addUserInfoToPost(postData, function() {
|
|
||||||
var socketData = {
|
|
||||||
posts: [postData]
|
|
||||||
};
|
|
||||||
|
|
||||||
io.sockets.in('topic_' + tid).emit('event:new_post', socketData);
|
|
||||||
io.sockets.in('recent_posts').emit('event:new_post', socketData);
|
|
||||||
io.sockets.in('user/' + uid).emit('event:new_post', socketData);
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, 'Reply successful');
|
|
||||||
} else {
|
|
||||||
callback(new Error('reply-error'), null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Posts.create = function(uid, tid, content, callback) {
|
|
||||||
if (uid === null) {
|
|
||||||
callback(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
topics.isLocked(tid, function(locked) {
|
|
||||||
if (!locked || locked === '0') {
|
|
||||||
RDB.incr('global:next_post_id', function(err, pid) {
|
|
||||||
RDB.handle(err);
|
|
||||||
|
|
||||||
plugins.fireHook('filter:post.save', content, function(err, newContent) {
|
|
||||||
if (!err) content = newContent;
|
|
||||||
|
|
||||||
var timestamp = Date.now(),
|
|
||||||
postData = {
|
|
||||||
'pid': pid,
|
|
||||||
'uid': uid,
|
|
||||||
'tid': tid,
|
|
||||||
'content': content,
|
|
||||||
'timestamp': timestamp,
|
|
||||||
'reputation': 0,
|
|
||||||
'editor': '',
|
|
||||||
'edited': 0,
|
|
||||||
'deleted': 0,
|
|
||||||
'fav_button_class': '',
|
|
||||||
'fav_star_class': 'icon-star-empty',
|
|
||||||
'show_banned': 'hide',
|
|
||||||
'relativeTime': new Date(timestamp).toISOString(),
|
|
||||||
'post_rep': '0',
|
|
||||||
'edited-class': 'none',
|
|
||||||
'relativeEditTime': ''
|
|
||||||
};
|
|
||||||
|
|
||||||
RDB.hmset('post:' + pid, postData);
|
|
||||||
|
|
||||||
topics.addPostToTopic(tid, pid);
|
|
||||||
topics.increasePostCount(tid);
|
|
||||||
topics.updateTimestamp(tid, timestamp);
|
|
||||||
|
|
||||||
RDB.incr('totalpostcount');
|
|
||||||
|
|
||||||
topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) {
|
|
||||||
|
|
||||||
RDB.handle(err);
|
|
||||||
|
|
||||||
var cid = topicData.cid;
|
|
||||||
|
|
||||||
feed.updateTopic(tid);
|
|
||||||
|
|
||||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
|
||||||
|
|
||||||
if(topicData.pinned === '0')
|
|
||||||
RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
|
|
||||||
|
|
||||||
RDB.scard('cid:' + cid + ':active_users', function(err, amount) {
|
|
||||||
if (amount > 10) {
|
|
||||||
RDB.spop('cid:' + cid + ':active_users');
|
|
||||||
}
|
|
||||||
|
|
||||||
categories.addActiveUser(cid, uid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
user.onNewPostMade(uid, tid, pid, timestamp);
|
|
||||||
|
|
||||||
async.parallel({
|
|
||||||
content: function(next) {
|
|
||||||
plugins.fireHook('filter:post.get', postData, function(err, newPostData) {
|
|
||||||
if (!err) postData = newPostData;
|
|
||||||
|
|
||||||
postTools.parse(postData.content, function(err, content) {
|
|
||||||
next(null, content);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, function(err, results) {
|
|
||||||
postData.content = results.content;
|
|
||||||
callback(postData);
|
|
||||||
});
|
|
||||||
|
|
||||||
plugins.fireHook('action:post.save', postData);
|
|
||||||
|
|
||||||
postSearch.index(content, pid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Posts.uploadPostImage = function(image, callback) {
|
Posts.uploadPostImage = function(image, callback) {
|
||||||
var imgur = require('./imgur');
|
|
||||||
imgur.setClientID(meta.config.imgurClientID);
|
|
||||||
|
|
||||||
if(!image)
|
if(!image)
|
||||||
return callback('invalid image', null);
|
return callback('invalid image', null);
|
||||||
|
|
||||||
imgur.upload(image.data, 'base64', function(err, data) {
|
require('./imgur').upload(meta.config.imgurClientID, image.data, 'base64', function(err, data) {
|
||||||
if(err) {
|
if(err) {
|
||||||
callback('Can\'t upload image!', null);
|
callback(err.message, null);
|
||||||
} else {
|
} else {
|
||||||
if(data.success) {
|
callback(null, {
|
||||||
var img= {url:data.data.link, name:image.name};
|
url: data.link,
|
||||||
|
name: image.name
|
||||||
callback(null, img);
|
});
|
||||||
} else {
|
|
||||||
winston.error('Can\'t upload image, did you set imgurClientID?');
|
|
||||||
callback("upload error", null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -462,7 +502,7 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
function reIndex(pid, callback) {
|
function reIndex(pid, callback) {
|
||||||
|
|
||||||
Posts.getPostField(pid, 'content', function(content) {
|
Posts.getPostField(pid, 'content', function(err, content) {
|
||||||
postSearch.remove(pid, function() {
|
postSearch.remove(pid, function() {
|
||||||
|
|
||||||
if (content && content.length) {
|
if (content && content.length) {
|
||||||
|
|||||||
20
src/redis.js
@@ -58,18 +58,18 @@
|
|||||||
*/
|
*/
|
||||||
RedisDB.hmgetObject = function(key, fields, callback) {
|
RedisDB.hmgetObject = function(key, fields, callback) {
|
||||||
RedisDB.hmget(key, fields, function(err, data) {
|
RedisDB.hmget(key, fields, function(err, data) {
|
||||||
if (err === null) {
|
|
||||||
var returnData = {};
|
|
||||||
|
|
||||||
for (var i = 0, ii = fields.length; i < ii; ++i) {
|
if(err) {
|
||||||
returnData[fields[i]] = data[i];
|
return callback(err, null);
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, returnData);
|
|
||||||
} else {
|
|
||||||
console.log(err);
|
|
||||||
callback(err, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var returnData = {};
|
||||||
|
|
||||||
|
for (var i = 0, ii = fields.length; i < ii; ++i) {
|
||||||
|
returnData[fields[i]] = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, returnData);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ var user = require('./../user.js'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
var filename = 'site-logo' + extension;
|
var filename = 'site-logo' + extension;
|
||||||
var uploadPath = path.join(process.cwd(), nconf.get('upload_path'), filename);
|
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
|
||||||
|
|
||||||
winston.info('Attempting upload to: ' + uploadPath);
|
winston.info('Attempting upload to: ' + uploadPath);
|
||||||
|
|
||||||
@@ -246,7 +246,8 @@ var user = require('./../user.js'),
|
|||||||
app.get('/topics', function (req, res) {
|
app.get('/topics', function (req, res) {
|
||||||
topics.getAllTopics(10, null, function (topics) {
|
topics.getAllTopics(10, null, function (topics) {
|
||||||
res.json({
|
res.json({
|
||||||
topics: topics
|
topics: topics,
|
||||||
|
notopics: topics.length === 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ var user = require('./../user.js'),
|
|||||||
config.maximumUsernameLength = meta.config.maximumUsernameLength;
|
config.maximumUsernameLength = meta.config.maximumUsernameLength;
|
||||||
config.minimumPasswordLength = meta.config.minimumPasswordLength;
|
config.minimumPasswordLength = meta.config.minimumPasswordLength;
|
||||||
config.useOutgoingLinksPage = meta.config.useOutgoingLinksPage;
|
config.useOutgoingLinksPage = meta.config.useOutgoingLinksPage;
|
||||||
|
config.emailSetup = !!meta.config['email:from'];
|
||||||
|
|
||||||
res.json(200, config);
|
res.json(200, config);
|
||||||
});
|
});
|
||||||
@@ -137,8 +138,12 @@ var user = require('./../user.js'),
|
|||||||
|
|
||||||
app.get('/recent/:term?', function (req, res) {
|
app.get('/recent/:term?', function (req, res) {
|
||||||
var uid = (req.user) ? req.user.uid : 0;
|
var uid = (req.user) ? req.user.uid : 0;
|
||||||
topics.getLatestTopics(uid, 0, 19, req.params.term, function (data) {
|
topics.getLatestTopics(uid, 0, 19, req.params.term, function (err, data) {
|
||||||
res.json(data);
|
if (!err) {
|
||||||
|
res.json(data);
|
||||||
|
} else {
|
||||||
|
res.send(500);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -189,7 +194,8 @@ var user = require('./../user.js'),
|
|||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
res.json({
|
res.json({
|
||||||
url: url
|
url: url,
|
||||||
|
title: meta.config.title
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
|
|||||||
@@ -1,50 +1,75 @@
|
|||||||
|
|
||||||
|
var user = require('./../user'),
|
||||||
|
categories = require('./../categories'),
|
||||||
|
topics = require('./../topics'),
|
||||||
|
posts = require('./../posts');
|
||||||
|
|
||||||
var DebugRoute = function(app) {
|
var DebugRoute = function(app) {
|
||||||
app.namespace('/debug', function() {
|
|
||||||
app.get('/cid/:cid', function (req, res) {
|
|
||||||
categories.getCategoryData(req.params.cid, function (err, data) {
|
|
||||||
if (data) {
|
|
||||||
res.send(data);
|
|
||||||
} else {
|
|
||||||
res.send(404, "Category doesn't exist!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/tid/:tid', function (req, res) {
|
app.namespace('/debug', function() {
|
||||||
topics.getTopicData(req.params.tid, function (data) {
|
|
||||||
if (data) {
|
|
||||||
res.send(data);
|
|
||||||
} else {
|
|
||||||
res.send(404, "Topic doesn't exist!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/pid/:pid', function (req, res) {
|
app.get('/uid/:uid', function (req, res) {
|
||||||
posts.getPostData(req.params.pid, function (data) {
|
|
||||||
if (data) {
|
|
||||||
res.send(data);
|
|
||||||
} else {
|
|
||||||
res.send(404, "Post doesn't exist!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/prune', function(req, res) {
|
if (!req.params.uid)
|
||||||
var Notifications = require('../notifications');
|
return res.redirect('/404');
|
||||||
|
|
||||||
Notifications.prune(new Date(), function() {
|
user.getUserData(req.params.uid, function (err, data) {
|
||||||
console.log('done');
|
if (data) {
|
||||||
});
|
res.send(data);
|
||||||
res.send();
|
} else {
|
||||||
});
|
res.json(404, {
|
||||||
|
error: "User doesn't exist!"
|
||||||
app.get('/uuidtest', function(req, res) {
|
});
|
||||||
var Utils = require('../../public/src/utils.js');
|
}
|
||||||
|
|
||||||
res.send(Utils.generateUUID());
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
|
app.get('/cid/:cid', function (req, res) {
|
||||||
|
categories.getCategoryData(req.params.cid, function (err, data) {
|
||||||
|
if (data) {
|
||||||
|
res.send(data);
|
||||||
|
} else {
|
||||||
|
res.send(404, "Category doesn't exist!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/tid/:tid', function (req, res) {
|
||||||
|
topics.getTopicData(req.params.tid, function (err, data) {
|
||||||
|
if (data) {
|
||||||
|
res.send(data);
|
||||||
|
} else {
|
||||||
|
res.send(404, "Topic doesn't exist!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/pid/:pid', function (req, res) {
|
||||||
|
posts.getPostData(req.params.pid, function (err, data) {
|
||||||
|
if (data) {
|
||||||
|
res.send(data);
|
||||||
|
} else {
|
||||||
|
res.send(404, "Post doesn't exist!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/prune', function(req, res) {
|
||||||
|
var Notifications = require('../notifications');
|
||||||
|
|
||||||
|
Notifications.prune(new Date(), function() {
|
||||||
|
console.log('done');
|
||||||
|
});
|
||||||
|
res.send();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/uuidtest', function(req, res) {
|
||||||
|
var Utils = require('../../public/src/utils.js');
|
||||||
|
|
||||||
|
res.send(Utils.generateUUID());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = DebugRoute;
|
module.exports = DebugRoute;
|
||||||
@@ -1,9 +1,29 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
var nconf = require('nconf'),
|
var nconf = require('nconf'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
|
validator = require('validator'),
|
||||||
Plugins = require('../plugins'),
|
Plugins = require('../plugins'),
|
||||||
|
|
||||||
PluginRoutes = function(app) {
|
PluginRoutes = function(app) {
|
||||||
|
app.get('/plugins/fireHook', function(req, res) {
|
||||||
|
// GET = filter
|
||||||
|
Plugins.fireHook('filter:' + req.query.hook, req.query.args, function(err, returnData) {
|
||||||
|
if (typeof returnData === 'object') {
|
||||||
|
res.json(200, returnData);
|
||||||
|
} else {
|
||||||
|
res.send(200, validator.sanitize(returnData).escape());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/plugins/fireHook', function(req, res) {
|
||||||
|
// PUT = action
|
||||||
|
Plugins.fireHook('action:' + req.body.hook, req.body.args);
|
||||||
|
res.send(200);
|
||||||
|
});
|
||||||
|
|
||||||
// Static Assets
|
// Static Assets
|
||||||
app.get('/plugins/:id/*', function(req, res) {
|
app.get('/plugins/:id/*', function(req, res) {
|
||||||
var relPath = req.url.replace('/plugins/' + req.params.id, '');
|
var relPath = req.url.replace('/plugins/' + req.params.id, '');
|
||||||
@@ -15,7 +35,7 @@ var nconf = require('nconf'),
|
|||||||
} else {
|
} else {
|
||||||
res.redirect('/404');
|
res.redirect('/404');
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
res.redirect('/404');
|
res.redirect('/404');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,22 +14,6 @@ var user = require('./../user.js'),
|
|||||||
(function (User) {
|
(function (User) {
|
||||||
User.createRoutes = function (app) {
|
User.createRoutes = function (app) {
|
||||||
|
|
||||||
app.get('/uid/:uid', function (req, res) {
|
|
||||||
|
|
||||||
if (!req.params.uid)
|
|
||||||
return res.redirect('/404');
|
|
||||||
|
|
||||||
user.getUserData(req.params.uid, function (err, data) {
|
|
||||||
if (data) {
|
|
||||||
res.send(data);
|
|
||||||
} else {
|
|
||||||
res.json(404, {
|
|
||||||
error: "User doesn't exist!"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.namespace('/users', function () {
|
app.namespace('/users', function () {
|
||||||
app.get('', function (req, res) {
|
app.get('', function (req, res) {
|
||||||
app.build_header({
|
app.build_header({
|
||||||
@@ -175,7 +159,7 @@ var user = require('./../user.js'),
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var absolutePath = path.join(process.cwd(), nconf.get('upload_path'), path.basename(oldpicture));
|
var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), path.basename(oldpicture));
|
||||||
|
|
||||||
fs.unlink(absolutePath, function (err) {
|
fs.unlink(absolutePath, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -197,7 +181,7 @@ var user = require('./../user.js'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
var filename = uid + '-profileimg' + extension;
|
var filename = uid + '-profileimg' + extension;
|
||||||
var uploadPath = path.join(process.cwd(), nconf.get('upload_path'), filename);
|
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
|
||||||
|
|
||||||
winston.info('Attempting upload to: ' + uploadPath);
|
winston.info('Attempting upload to: ' + uploadPath);
|
||||||
|
|
||||||
@@ -559,6 +543,8 @@ var user = require('./../user.js'),
|
|||||||
else
|
else
|
||||||
data.emailClass = "hide";
|
data.emailClass = "hide";
|
||||||
|
|
||||||
|
data.websiteName = data.website.replace('http://', '').replace('https://', '');
|
||||||
|
|
||||||
data.show_banned = data.banned === '1' ? '' : 'hide';
|
data.show_banned = data.banned === '1' ? '' : 'hide';
|
||||||
|
|
||||||
data.uid = uid;
|
data.uid = uid;
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
pids.reverse();
|
pids.reverse();
|
||||||
async.detectSeries(pids, function(pid, next) {
|
async.detectSeries(pids, function(pid, next) {
|
||||||
posts.getPostField(pid, 'deleted', function(deleted) {
|
posts.getPostField(pid, 'deleted', function(err, deleted) {
|
||||||
if (deleted === '0') next(true);
|
if (deleted === '0') next(true);
|
||||||
else next(false);
|
else next(false);
|
||||||
});
|
});
|
||||||
|
|||||||
323
src/topics.js
@@ -18,30 +18,132 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
(function(Topics) {
|
(function(Topics) {
|
||||||
|
|
||||||
Topics.getTopicData = function(tid, callback) {
|
Topics.post = function(uid, title, content, category_id, callback) {
|
||||||
RDB.hgetall('topic:' + tid, function(err, data) {
|
if (!category_id)
|
||||||
if (err === null) {
|
throw new Error('Attempted to post without a category_id');
|
||||||
if(data) {
|
|
||||||
data.title = validator.sanitize(data.title).escape();
|
if (content)
|
||||||
if(data.timestamp) {
|
content = content.trim();
|
||||||
data.relativeTime = new Date(parseInt(data.timestamp, 10)).toISOString();
|
if (title)
|
||||||
}
|
title = title.trim();
|
||||||
|
|
||||||
|
if (!uid) {
|
||||||
|
callback(new Error('not-logged-in'), null);
|
||||||
|
return;
|
||||||
|
} else if (!title || title.length < meta.config.minimumTitleLength) {
|
||||||
|
callback(new Error('title-too-short'), null);
|
||||||
|
return;
|
||||||
|
} else if (!content || content.length < meta.config.miminumPostLength) {
|
||||||
|
callback(new Error('content-too-short'), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.getUserField(uid, 'lastposttime', function(err, lastposttime) {
|
||||||
|
if (err) lastposttime = 0;
|
||||||
|
if (Date.now() - lastposttime < meta.config.postDelay * 1000) {
|
||||||
|
callback(new Error('too-many-posts'), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RDB.incr('next_topic_id', function(err, tid) {
|
||||||
|
RDB.handle(err);
|
||||||
|
|
||||||
|
// Global Topics
|
||||||
|
if (uid == null) uid = 0;
|
||||||
|
if (uid !== null) {
|
||||||
|
RDB.sadd('topics:tid', tid);
|
||||||
|
} else {
|
||||||
|
// need to add some unique key sent by client so we can update this with the real uid later
|
||||||
|
RDB.lpush('topics:queued:tid', tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(data);
|
var slug = tid + '/' + utils.slugify(title);
|
||||||
} else {
|
var timestamp = Date.now();
|
||||||
console.log(err);
|
RDB.hmset('topic:' + tid, {
|
||||||
|
'tid': tid,
|
||||||
|
'uid': uid,
|
||||||
|
'cid': category_id,
|
||||||
|
'title': title,
|
||||||
|
'slug': slug,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'lastposttime': 0,
|
||||||
|
'postcount': 0,
|
||||||
|
'viewcount': 0,
|
||||||
|
'locked': 0,
|
||||||
|
'deleted': 0,
|
||||||
|
'pinned': 0
|
||||||
|
});
|
||||||
|
|
||||||
|
topicSearch.index(title, tid);
|
||||||
|
|
||||||
|
user.addTopicIdToUser(uid, tid);
|
||||||
|
|
||||||
|
// let everyone know that there is an unread topic in this category
|
||||||
|
RDB.del('cid:' + category_id + ':read_by_uid', function(err, data) {
|
||||||
|
Topics.markAsRead(tid, uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// in future it may be possible to add topics to several categories, so leaving the door open here.
|
||||||
|
RDB.zadd('categories:' + category_id + ':tid', timestamp, tid);
|
||||||
|
RDB.hincrby('category:' + category_id, 'topic_count', 1);
|
||||||
|
RDB.incr('totaltopiccount');
|
||||||
|
|
||||||
|
feed.updateCategory(category_id);
|
||||||
|
|
||||||
|
posts.create(uid, tid, content, function(err, postData) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
} else if(!postData) {
|
||||||
|
return callback(new Error('invalid-post'), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-subscribe the post creator to the newly created topic
|
||||||
|
threadTools.toggleFollow(tid, uid);
|
||||||
|
|
||||||
|
Topics.getTopicForCategoryView(tid, uid, function(topicData) {
|
||||||
|
callback(null, {
|
||||||
|
topicData: topicData,
|
||||||
|
postData: postData
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Topics.getTopicData = function(tid, callback) {
|
||||||
|
RDB.hgetall('topic:' + tid, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(data) {
|
||||||
|
data.title = validator.sanitize(data.title).escape();
|
||||||
|
if(data.timestamp) {
|
||||||
|
data.relativeTime = new Date(parseInt(data.timestamp, 10)).toISOString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Topics.getTopicDataWithUser = function(tid, callback) {
|
Topics.getTopicDataWithUser = function(tid, callback) {
|
||||||
Topics.getTopicData(tid, function(topic) {
|
Topics.getTopicData(tid, function(err, topic) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
user.getUserFields(topic.uid, ['username', 'userslug', 'picture'] , function(err, userData) {
|
user.getUserFields(topic.uid, ['username', 'userslug', 'picture'] , function(err, userData) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
topic.username = userData.username;
|
topic.username = userData.username;
|
||||||
topic.userslug = userData.userslug
|
topic.userslug = userData.userslug
|
||||||
topic.picture = userData.picture;
|
topic.picture = userData.picture;
|
||||||
callback(topic);
|
callback(null, topic);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -119,26 +221,24 @@ var RDB = require('./redis.js'),
|
|||||||
var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
|
var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
|
||||||
|
|
||||||
RDB.zrevrangebyscore(args, function(err, tids) {
|
RDB.zrevrangebyscore(args, function(err, tids) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
var latestTopics = {
|
var latestTopics = {
|
||||||
'category_name': 'Recent',
|
|
||||||
'show_sidebar': 'hidden',
|
|
||||||
'show_topic_button': 'hidden',
|
|
||||||
'no_topics_message': 'hidden',
|
'no_topics_message': 'hidden',
|
||||||
'topic_row_size': 'col-md-12',
|
|
||||||
'category_id': false,
|
|
||||||
'topics': []
|
'topics': []
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!tids || !tids.length) {
|
if (!tids || !tids.length) {
|
||||||
latestTopics.no_topics_message = 'show';
|
latestTopics.no_topics_message = 'show';
|
||||||
callback(latestTopics);
|
callback(err, latestTopics);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Topics.getTopicsByTids(tids, current_user, function(topicData) {
|
Topics.getTopicsByTids(tids, current_user, function(topicData) {
|
||||||
latestTopics.topics = topicData;
|
latestTopics.topics = topicData;
|
||||||
callback(latestTopics);
|
callback(err, latestTopics);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -331,7 +431,7 @@ var RDB = require('./redis.js'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadTopic(tid, callback) {
|
function loadTopic(tid, callback) {
|
||||||
Topics.getTopicData(tid, function(topicData) {
|
Topics.getTopicData(tid, function(err, topicData) {
|
||||||
if (!topicData) {
|
if (!topicData) {
|
||||||
return callback(null);
|
return callback(null);
|
||||||
}
|
}
|
||||||
@@ -342,6 +442,7 @@ var RDB = require('./redis.js'),
|
|||||||
topicData['lock-icon'] = topicData.locked === '1' ? 'icon-lock' : 'none';
|
topicData['lock-icon'] = topicData.locked === '1' ? 'icon-lock' : 'none';
|
||||||
topicData['deleted-class'] = topicData.deleted === '1' ? 'deleted' : '';
|
topicData['deleted-class'] = topicData.deleted === '1' ? 'deleted' : '';
|
||||||
|
|
||||||
|
topicData.unreplied = topicData.postcount === '1';
|
||||||
topicData.username = topicInfo.username;
|
topicData.username = topicInfo.username;
|
||||||
topicData.userslug = topicInfo.userslug;
|
topicData.userslug = topicInfo.userslug;
|
||||||
topicData.picture = topicInfo.picture;
|
topicData.picture = topicInfo.picture;
|
||||||
@@ -381,9 +482,7 @@ var RDB = require('./redis.js'),
|
|||||||
Topics.increaseViewCount(tid);
|
Topics.increaseViewCount(tid);
|
||||||
|
|
||||||
function getTopicData(next) {
|
function getTopicData(next) {
|
||||||
Topics.getTopicData(tid, function(topicData) {
|
Topics.getTopicData(tid, next);
|
||||||
next(null, topicData);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTopicPosts(next) {
|
function getTopicPosts(next) {
|
||||||
@@ -426,6 +525,7 @@ var RDB = require('./redis.js'),
|
|||||||
'slug': topicData.slug,
|
'slug': topicData.slug,
|
||||||
'postcount': topicData.postcount,
|
'postcount': topicData.postcount,
|
||||||
'viewcount': topicData.viewcount,
|
'viewcount': topicData.viewcount,
|
||||||
|
'unreplied': topicData.postcount > 1,
|
||||||
'topic_id': tid,
|
'topic_id': tid,
|
||||||
'expose_tools': privileges.editable ? 1 : 0,
|
'expose_tools': privileges.editable ? 1 : 0,
|
||||||
'posts': topicPosts,
|
'posts': topicPosts,
|
||||||
@@ -442,9 +542,7 @@ var RDB = require('./redis.js'),
|
|||||||
Topics.getTopicForCategoryView = function(tid, uid, callback) {
|
Topics.getTopicForCategoryView = function(tid, uid, callback) {
|
||||||
|
|
||||||
function getTopicData(next) {
|
function getTopicData(next) {
|
||||||
Topics.getTopicDataWithUser(tid, function(topic) {
|
Topics.getTopicDataWithUser(tid, next);
|
||||||
next(null, topic);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReadStatus(next) {
|
function getReadStatus(next) {
|
||||||
@@ -473,6 +571,9 @@ var RDB = require('./redis.js'),
|
|||||||
hasRead = results[1],
|
hasRead = results[1],
|
||||||
teaser = results[2];
|
teaser = results[2];
|
||||||
|
|
||||||
|
topicData['pin-icon'] = topicData.pinned === '1' ? 'icon-pushpin' : 'none';
|
||||||
|
topicData['lock-icon'] = topicData.locked === '1' ? 'icon-lock' : 'none';
|
||||||
|
|
||||||
topicData.badgeclass = hasRead ? '' : 'badge-important';
|
topicData.badgeclass = hasRead ? '' : 'badge-important';
|
||||||
topicData.teaser_text = teaser.text || '';
|
topicData.teaser_text = teaser.text || '';
|
||||||
topicData.teaser_username = teaser.username || '';
|
topicData.teaser_username = teaser.username || '';
|
||||||
@@ -517,7 +618,7 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
|
|
||||||
async.each(tids, function(tid, next) {
|
async.each(tids, function(tid, next) {
|
||||||
Topics.getTopicDataWithUser(tid, function(topicData) {
|
Topics.getTopicDataWithUser(tid, function(err, topicData) {
|
||||||
topics.push(topicData);
|
topics.push(topicData);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
@@ -546,15 +647,15 @@ var RDB = require('./redis.js'),
|
|||||||
}
|
}
|
||||||
|
|
||||||
Topics.getTitleByPid = function(pid, callback) {
|
Topics.getTitleByPid = function(pid, callback) {
|
||||||
posts.getPostField(pid, 'tid', function(tid) {
|
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||||
Topics.getTopicField(tid, 'title', function(err, title) {
|
Topics.getTopicField(tid, 'title', function(err, title) {
|
||||||
callback(title);
|
callback(title);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Topics.markUnRead = function(tid) {
|
Topics.markUnRead = function(tid, callback) {
|
||||||
RDB.del('tid:' + tid + ':read_by_uid');
|
RDB.del('tid:' + tid + ':read_by_uid', callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
Topics.markAsRead = function(tid, uid) {
|
Topics.markAsRead = function(tid, uid) {
|
||||||
@@ -620,36 +721,44 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
Topics.getTeaser = function(tid, callback) {
|
Topics.getTeaser = function(tid, callback) {
|
||||||
threadTools.getLatestUndeletedPid(tid, function(err, pid) {
|
threadTools.getLatestUndeletedPid(tid, function(err, pid) {
|
||||||
if (!err) {
|
if (err) {
|
||||||
posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(postData) {
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(err, postData) {
|
||||||
if (err)
|
if (err) {
|
||||||
return callback(err, null);
|
return callback(err, null);
|
||||||
|
} else if(!postData) {
|
||||||
|
return callback(new Error('no-teaser-found'));
|
||||||
|
}
|
||||||
|
|
||||||
var stripped = postData.content,
|
user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
||||||
timestamp = postData.timestamp,
|
if (err) {
|
||||||
returnObj = {
|
return callback(err, null);
|
||||||
"pid": postData.pid,
|
}
|
||||||
"username": userData.username,
|
|
||||||
"userslug": userData.userslug,
|
|
||||||
"picture": userData.picture,
|
|
||||||
"timestamp": timestamp
|
|
||||||
};
|
|
||||||
|
|
||||||
if (postData.content) {
|
var stripped = postData.content,
|
||||||
stripped = postData.content.replace(/>.+\n\n/, '');
|
timestamp = postData.timestamp,
|
||||||
postTools.parse(stripped, function(err, stripped) {
|
returnObj = {
|
||||||
returnObj.text = utils.strip_tags(stripped);
|
"pid": postData.pid,
|
||||||
callback(null, returnObj);
|
"username": userData.username,
|
||||||
});
|
"userslug": userData.userslug,
|
||||||
} else {
|
"picture": userData.picture,
|
||||||
returnObj.text = '';
|
"timestamp": timestamp
|
||||||
|
};
|
||||||
|
|
||||||
|
if (postData.content) {
|
||||||
|
stripped = postData.content.replace(/>.+\n\n/, '');
|
||||||
|
postTools.parse(stripped, function(err, stripped) {
|
||||||
|
returnObj.text = utils.strip_tags(stripped);
|
||||||
callback(null, returnObj);
|
callback(null, returnObj);
|
||||||
}
|
});
|
||||||
});
|
} else {
|
||||||
|
returnObj.text = '';
|
||||||
|
callback(null, returnObj);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else callback(new Error('no-teaser-found'));
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,101 +772,6 @@ var RDB = require('./redis.js'),
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Topics.post = function(uid, title, content, category_id, callback) {
|
|
||||||
if (!category_id)
|
|
||||||
throw new Error('Attempted to post without a category_id');
|
|
||||||
|
|
||||||
if (content)
|
|
||||||
content = content.trim();
|
|
||||||
if (title)
|
|
||||||
title = title.trim();
|
|
||||||
|
|
||||||
if (uid === 0) {
|
|
||||||
callback(new Error('not-logged-in'), null);
|
|
||||||
return;
|
|
||||||
} else if (!title || title.length < meta.config.minimumTitleLength) {
|
|
||||||
callback(new Error('title-too-short'), null);
|
|
||||||
return;
|
|
||||||
} else if (!content || content.length < meta.config.miminumPostLength) {
|
|
||||||
callback(new Error('content-too-short'), null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.getUserField(uid, 'lastposttime', function(err, lastposttime) {
|
|
||||||
if (err) lastposttime = 0;
|
|
||||||
if (Date.now() - lastposttime < meta.config.postDelay * 1000) {
|
|
||||||
callback(new Error('too-many-posts'), null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RDB.incr('next_topic_id', function(err, tid) {
|
|
||||||
RDB.handle(err);
|
|
||||||
|
|
||||||
// Global Topics
|
|
||||||
if (uid == null) uid = 0;
|
|
||||||
if (uid !== null) {
|
|
||||||
RDB.sadd('topics:tid', tid);
|
|
||||||
} else {
|
|
||||||
// need to add some unique key sent by client so we can update this with the real uid later
|
|
||||||
RDB.lpush('topics:queued:tid', tid);
|
|
||||||
}
|
|
||||||
|
|
||||||
var slug = tid + '/' + utils.slugify(title);
|
|
||||||
var timestamp = Date.now();
|
|
||||||
RDB.hmset('topic:' + tid, {
|
|
||||||
'tid': tid,
|
|
||||||
'uid': uid,
|
|
||||||
'cid': category_id,
|
|
||||||
'title': title,
|
|
||||||
'slug': slug,
|
|
||||||
'timestamp': timestamp,
|
|
||||||
'lastposttime': 0,
|
|
||||||
'postcount': 0,
|
|
||||||
'viewcount': 0,
|
|
||||||
'locked': 0,
|
|
||||||
'deleted': 0,
|
|
||||||
'pinned': 0
|
|
||||||
});
|
|
||||||
|
|
||||||
topicSearch.index(title, tid);
|
|
||||||
|
|
||||||
user.addTopicIdToUser(uid, tid);
|
|
||||||
|
|
||||||
// let everyone know that there is an unread topic in this category
|
|
||||||
RDB.del('cid:' + category_id + ':read_by_uid', function(err, data) {
|
|
||||||
Topics.markAsRead(tid, uid);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// in future it may be possible to add topics to several categories, so leaving the door open here.
|
|
||||||
RDB.zadd('categories:' + category_id + ':tid', timestamp, tid);
|
|
||||||
RDB.hincrby('category:' + category_id, 'topic_count', 1);
|
|
||||||
RDB.incr('totaltopiccount');
|
|
||||||
|
|
||||||
feed.updateCategory(category_id);
|
|
||||||
|
|
||||||
posts.create(uid, tid, content, function(postData) {
|
|
||||||
if (postData) {
|
|
||||||
|
|
||||||
// Auto-subscribe the post creator to the newly created topic
|
|
||||||
threadTools.toggleFollow(tid, uid);
|
|
||||||
|
|
||||||
// Notify any users looking at the category that a new topic has arrived
|
|
||||||
Topics.getTopicForCategoryView(tid, uid, function(topicData) {
|
|
||||||
io.sockets.in('category_' + category_id).emit('event:new_topic', topicData);
|
|
||||||
io.sockets.in('recent_posts').emit('event:new_topic', topicData);
|
|
||||||
io.sockets.in('user/' + uid).emit('event:new_post', {
|
|
||||||
posts: postData
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, postData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Topics.getTopicField = function(tid, field, callback) {
|
Topics.getTopicField = function(tid, field, callback) {
|
||||||
RDB.hget('topic:' + tid, field, callback);
|
RDB.hget('topic:' + tid, field, callback);
|
||||||
}
|
}
|
||||||
@@ -780,7 +794,10 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
Topics.isLocked = function(tid, callback) {
|
Topics.isLocked = function(tid, callback) {
|
||||||
Topics.getTopicField(tid, 'locked', function(err, locked) {
|
Topics.getTopicField(tid, 'locked', function(err, locked) {
|
||||||
callback(locked);
|
if(err) {
|
||||||
|
return callback(err, null);
|
||||||
|
}
|
||||||
|
callback(null, locked === "1");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,7 +819,7 @@ var RDB = require('./redis.js'),
|
|||||||
Topics.getPids(tid, function(err, pids) {
|
Topics.getPids(tid, function(err, pids) {
|
||||||
|
|
||||||
function getUid(pid, next) {
|
function getUid(pid, next) {
|
||||||
posts.getPostField(pid, 'uid', function(uid) {
|
posts.getPostField(pid, 'uid', function(err, uid) {
|
||||||
if (err)
|
if (err)
|
||||||
return next(err);
|
return next(err);
|
||||||
uids[uid] = 1;
|
uids[uid] = 1;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ var RDB = require('./redis.js'),
|
|||||||
schemaDate, thisSchemaDate;
|
schemaDate, thisSchemaDate;
|
||||||
|
|
||||||
Upgrade.check = function(callback) {
|
Upgrade.check = function(callback) {
|
||||||
var latestSchema = new Date(2013, 10, 11).getTime();
|
var latestSchema = new Date(2013, 10, 22).getTime();
|
||||||
|
|
||||||
RDB.get('schemaDate', function(err, value) {
|
RDB.get('schemaDate', function(err, value) {
|
||||||
if (parseInt(value, 10) >= latestSchema) {
|
if (parseInt(value, 10) >= latestSchema) {
|
||||||
@@ -20,7 +20,7 @@ Upgrade.check = function(callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Upgrade.upgrade = function() {
|
Upgrade.upgrade = function(callback) {
|
||||||
winston.info('Beginning Redis database schema update');
|
winston.info('Beginning Redis database schema update');
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
@@ -128,6 +128,52 @@ Upgrade.upgrade = function() {
|
|||||||
winston.info('[2013/11/11] Update to postDelay skipped.');
|
winston.info('[2013/11/11] Update to postDelay skipped.');
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
thisSchemaDate = new Date(2013, 10, 22).getTime();
|
||||||
|
if (schemaDate < thisSchemaDate) {
|
||||||
|
RDB.keys('category:*', function(err, categories) {
|
||||||
|
async.each(categories, function(categoryStr, next) {
|
||||||
|
var hex;
|
||||||
|
RDB.hgetall(categoryStr, function(err, categoryObj) {
|
||||||
|
switch(categoryObj.blockclass) {
|
||||||
|
case 'category-purple':
|
||||||
|
hex = '#ab1290';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'category-darkblue':
|
||||||
|
hex = '#004c66';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'category-blue':
|
||||||
|
hex = '#0059b2';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'category-darkgreen':
|
||||||
|
hex = '#004000';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'category-orange':
|
||||||
|
hex = '#ff7a4d';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
hex = '#0059b2';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
RDB.hset(categoryStr, 'bgColor', hex, next);
|
||||||
|
RDB.hdel(categoryStr, 'blockclass');
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
winston.info('[2013/11/22] Updated Category colours.');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
winston.info('[2013/11/22] Update to Category colours skipped.');
|
||||||
|
next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add new schema updates here
|
// Add new schema updates here
|
||||||
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 12!!!
|
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 12!!!
|
||||||
@@ -136,13 +182,19 @@ Upgrade.upgrade = function() {
|
|||||||
RDB.set('schemaDate', thisSchemaDate, function(err) {
|
RDB.set('schemaDate', thisSchemaDate, function(err) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
winston.info('[upgrade] Redis schema update complete!');
|
winston.info('[upgrade] Redis schema update complete!');
|
||||||
process.exit();
|
if (callback) {
|
||||||
|
callback(err);
|
||||||
|
} else {
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
winston.error('[upgrade] Could not update NodeBB schema date!');
|
winston.error('[upgrade] Could not update NodeBB schema date!');
|
||||||
|
process.exit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message);
|
winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message);
|
||||||
|
process.exit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
57
src/user.js
@@ -1,16 +1,20 @@
|
|||||||
var utils = require('./../public/src/utils.js'),
|
var bcrypt = require('bcrypt'),
|
||||||
RDB = require('./redis.js'),
|
|
||||||
emailjs = require('emailjs'),
|
|
||||||
meta = require('./meta.js'),
|
|
||||||
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
|
|
||||||
bcrypt = require('bcrypt'),
|
|
||||||
Groups = require('./groups'),
|
|
||||||
notifications = require('./notifications.js'),
|
|
||||||
topics = require('./topics.js'),
|
|
||||||
async = require('async'),
|
async = require('async'),
|
||||||
|
emailjs = require('emailjs'),
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
|
winston = require('winston'),
|
||||||
userSearch = require('reds').createSearch('nodebbusersearch'),
|
userSearch = require('reds').createSearch('nodebbusersearch'),
|
||||||
winston = require('winston');
|
check = require('validator').check,
|
||||||
|
sanitize = require('validator').sanitize,
|
||||||
|
|
||||||
|
utils = require('./../public/src/utils'),
|
||||||
|
RDB = require('./redis'),
|
||||||
|
meta = require('./meta'),
|
||||||
|
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
|
||||||
|
Groups = require('./groups'),
|
||||||
|
notifications = require('./notifications'),
|
||||||
|
topics = require('./topics');
|
||||||
|
|
||||||
|
|
||||||
(function(User) {
|
(function(User) {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -114,13 +118,6 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
|
|
||||||
userSearch.index(username, uid);
|
userSearch.index(username, uid);
|
||||||
|
|
||||||
if (typeof io !== 'undefined') {
|
|
||||||
io.sockets.emit('user.latest', {
|
|
||||||
userslug: userslug,
|
|
||||||
username: username
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password !== undefined) {
|
if (password !== undefined) {
|
||||||
User.hashPassword(password, function(err, hash) {
|
User.hashPassword(password, function(err, hash) {
|
||||||
User.setUserField(uid, 'password', hash);
|
User.setUserField(uid, 'password', hash);
|
||||||
@@ -250,6 +247,9 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
|
|
||||||
function updateField(field, next) {
|
function updateField(field, next) {
|
||||||
if (data[field] !== undefined && typeof data[field] === 'string') {
|
if (data[field] !== undefined && typeof data[field] === 'string') {
|
||||||
|
data[field] = data[field].trim();
|
||||||
|
data[field] = sanitize(data[field]).escape();
|
||||||
|
|
||||||
if (field === 'email') {
|
if (field === 'email') {
|
||||||
var gravatarpicture = User.createGravatarURLFromEmail(data[field]);
|
var gravatarpicture = User.createGravatarURLFromEmail(data[field]);
|
||||||
User.setUserField(uid, 'gravatarpicture', gravatarpicture);
|
User.setUserField(uid, 'gravatarpicture', gravatarpicture);
|
||||||
@@ -271,6 +271,10 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
return;
|
return;
|
||||||
} else if (field === 'signature') {
|
} else if (field === 'signature') {
|
||||||
data[field] = utils.strip_tags(data[field]);
|
data[field] = utils.strip_tags(data[field]);
|
||||||
|
} else if (field === 'website') {
|
||||||
|
if(data[field].substr(0, 7) !== 'http://' && data[field].substr(0, 8) !== 'https://') {
|
||||||
|
data[field] = 'http://' + data[field];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User.setUserField(uid, field, data[field]);
|
User.setUserField(uid, field, data[field]);
|
||||||
@@ -642,21 +646,6 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
User.latest = function(socket) {
|
|
||||||
RDB.zrevrange('users:joindate', 0, 0, function(err, uid) {
|
|
||||||
RDB.handle(err);
|
|
||||||
|
|
||||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
|
||||||
if (!err && userData) {
|
|
||||||
socket.emit('user.latest', {
|
|
||||||
userslug: userData.userslug,
|
|
||||||
username: userData.username
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
User.getUidByUsername = function(username, callback) {
|
User.getUidByUsername = function(username, callback) {
|
||||||
RDB.hget('username:uid', username, callback);
|
RDB.hget('username:uid', username, callback);
|
||||||
};
|
};
|
||||||
@@ -1009,7 +998,9 @@ var utils = require('./../public/src/utils.js'),
|
|||||||
next(null, notif_data);
|
next(null, notif_data);
|
||||||
});
|
});
|
||||||
}, function(err, notifs) {
|
}, function(err, notifs) {
|
||||||
notifs = notifs.sort(function(a, b) {
|
notifs = notifs.filter(function(notif) {
|
||||||
|
return notif !== null;
|
||||||
|
}).sort(function(a, b) {
|
||||||
return parseInt(b.datetime, 10) - parseInt(a.datetime, 10);
|
return parseInt(b.datetime, 10) - parseInt(a.datetime, 10);
|
||||||
}).map(function(notif) {
|
}).map(function(notif) {
|
||||||
notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString();
|
notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString();
|
||||||
|
|||||||
104
src/webserver.js
@@ -1,31 +1,33 @@
|
|||||||
var express = require('express'),
|
var path = require('path'),
|
||||||
|
fs = require('fs'),
|
||||||
|
|
||||||
|
express = require('express'),
|
||||||
express_namespace = require('express-namespace'),
|
express_namespace = require('express-namespace'),
|
||||||
WebServer = express(),
|
WebServer = express(),
|
||||||
server = require('http').createServer(WebServer),
|
server = require('http').createServer(WebServer),
|
||||||
RedisStore = require('connect-redis')(express),
|
RedisStore = require('connect-redis')(express),
|
||||||
path = require('path'),
|
|
||||||
RDB = require('./redis'),
|
|
||||||
utils = require('../public/src/utils.js'),
|
|
||||||
pkg = require('../package.json'),
|
|
||||||
fs = require('fs'),
|
|
||||||
|
|
||||||
user = require('./user.js'),
|
|
||||||
categories = require('./categories.js'),
|
|
||||||
posts = require('./posts.js'),
|
|
||||||
topics = require('./topics.js'),
|
|
||||||
notifications = require('./notifications.js'),
|
|
||||||
admin = require('./routes/admin.js'),
|
|
||||||
userRoute = require('./routes/user.js'),
|
|
||||||
apiRoute = require('./routes/api.js'),
|
|
||||||
auth = require('./routes/authentication.js'),
|
|
||||||
meta = require('./meta.js'),
|
|
||||||
feed = require('./feed'),
|
|
||||||
plugins = require('./plugins'),
|
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
winston = require('winston'),
|
winston = require('winston'),
|
||||||
validator = require('validator'),
|
validator = require('validator'),
|
||||||
async = require('async'),
|
async = require('async'),
|
||||||
logger = require('./logger.js');
|
|
||||||
|
pkg = require('../package.json'),
|
||||||
|
|
||||||
|
utils = require('../public/src/utils'),
|
||||||
|
RDB = require('./redis'),
|
||||||
|
user = require('./user'),
|
||||||
|
categories = require('./categories'),
|
||||||
|
posts = require('./posts'),
|
||||||
|
topics = require('./topics'),
|
||||||
|
notifications = require('./notifications'),
|
||||||
|
admin = require('./routes/admin'),
|
||||||
|
userRoute = require('./routes/user'),
|
||||||
|
apiRoute = require('./routes/api'),
|
||||||
|
auth = require('./routes/authentication'),
|
||||||
|
meta = require('./meta'),
|
||||||
|
feed = require('./feed'),
|
||||||
|
plugins = require('./plugins'),
|
||||||
|
logger = require('./logger');
|
||||||
|
|
||||||
(function (app) {
|
(function (app) {
|
||||||
"use strict";
|
"use strict";
|
||||||
@@ -33,17 +35,21 @@ var express = require('express'),
|
|||||||
var templates = null,
|
var templates = null,
|
||||||
clientScripts;
|
clientScripts;
|
||||||
|
|
||||||
// Minify client-side libraries
|
|
||||||
meta.js.get(function (err, scripts) {
|
|
||||||
clientScripts = scripts.map(function (script) {
|
|
||||||
script = {
|
|
||||||
script: script
|
|
||||||
};
|
|
||||||
|
|
||||||
return script;
|
plugins.ready(function() {
|
||||||
|
// Minify client-side libraries
|
||||||
|
meta.js.get(function (err, scripts) {
|
||||||
|
clientScripts = scripts.map(function (script) {
|
||||||
|
script = {
|
||||||
|
script: script
|
||||||
|
};
|
||||||
|
|
||||||
|
return script;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
server.app = app;
|
server.app = app;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,7 +150,7 @@ var express = require('express'),
|
|||||||
res.locals.csrf_token = req.session._csrf;
|
res.locals.csrf_token = req.session._csrf;
|
||||||
|
|
||||||
// Disable framing
|
// Disable framing
|
||||||
res.setHeader("X-Frame-Options", "DENY");
|
res.setHeader('X-Frame-Options', 'DENY');
|
||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
@@ -306,9 +312,17 @@ var express = require('express'),
|
|||||||
templates = global.templates;
|
templates = global.templates;
|
||||||
|
|
||||||
// translate all static templates served by webserver here. ex. footer, logout
|
// translate all static templates served by webserver here. ex. footer, logout
|
||||||
translator.translate(templates.footer.toString(), function(parsedTemplate) {
|
plugins.fireHook('filter:footer.build', '', function(err, appendHTML) {
|
||||||
templates.footer = parsedTemplate;
|
var footer = templates.footer.parse({
|
||||||
|
footerHTML: appendHTML
|
||||||
|
});
|
||||||
|
|
||||||
|
translator.translate(footer, function(parsedTemplate) {
|
||||||
|
templates.footer = parsedTemplate;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
translator.translate(templates.logout.toString(), function(parsedTemplate) {
|
translator.translate(templates.logout.toString(), function(parsedTemplate) {
|
||||||
templates.logout = parsedTemplate;
|
templates.logout = parsedTemplate;
|
||||||
});
|
});
|
||||||
@@ -639,13 +653,39 @@ var express = require('express'),
|
|||||||
"Sitemap: " + nconf.get('url') + "sitemap.xml");
|
"Sitemap: " + nconf.get('url') + "sitemap.xml");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/recent.rss', function(req, res) {
|
||||||
|
var rssPath = path.join(__dirname, '../', 'feeds/recent.rss'),
|
||||||
|
loadFeed = function () {
|
||||||
|
fs.readFile(rssPath, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||||
|
} else {
|
||||||
|
res.type('xml').set('Content-Length', data.length).send(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fs.existsSync(rssPath)) {
|
||||||
|
feed.updateRecent(function (err) {
|
||||||
|
if (err) {
|
||||||
|
res.redirect('/404');
|
||||||
|
} else {
|
||||||
|
loadFeed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loadFeed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/recent/:term?', function (req, res) {
|
app.get('/recent/:term?', function (req, res) {
|
||||||
// TODO consolidate with /recent route as well -> that can be combined into this area. See "Basic Routes" near top.
|
// TODO consolidate with /recent route as well -> that can be combined into this area. See "Basic Routes" near top.
|
||||||
app.build_header({
|
app.build_header({
|
||||||
req: req,
|
req: req,
|
||||||
res: res
|
res: res
|
||||||
}, function (err, header) {
|
}, function (err, header) {
|
||||||
res.send(header + app.create_route("recent/" + req.params.term, null, "recent") + templates.footer);
|
res.send(header + app.create_route('recent/' + req.params.term, null, 'recent') + templates.footer);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -676,7 +716,7 @@ var express = require('express'),
|
|||||||
req: req,
|
req: req,
|
||||||
res: res
|
res: res
|
||||||
}, function (err, header) {
|
}, function (err, header) {
|
||||||
res.send(header + app.create_route("search/" + req.params.term, null, "search") + templates.footer);
|
res.send(header + app.create_route('search/' + req.params.term, null, 'search') + templates.footer);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
|
|
||||||
var cookie = require('cookie'),
|
var cookie = require('cookie'),
|
||||||
express = require('express'),
|
express = require('express'),
|
||||||
user = require('./user.js'),
|
|
||||||
Groups = require('./groups'),
|
|
||||||
posts = require('./posts.js'),
|
|
||||||
favourites = require('./favourites.js'),
|
|
||||||
utils = require('../public/src/utils.js'),
|
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
topics = require('./topics.js'),
|
|
||||||
categories = require('./categories.js'),
|
|
||||||
notifications = require('./notifications.js'),
|
|
||||||
threadTools = require('./threadTools.js'),
|
|
||||||
postTools = require('./postTools.js'),
|
|
||||||
meta = require('./meta.js'),
|
|
||||||
async = require('async'),
|
async = require('async'),
|
||||||
|
fs = require('fs'),
|
||||||
|
nconf = require('nconf'),
|
||||||
|
winston = require('winston'),
|
||||||
|
|
||||||
RedisStoreLib = require('connect-redis')(express),
|
RedisStoreLib = require('connect-redis')(express),
|
||||||
RDB = require('./redis'),
|
RDB = require('./redis'),
|
||||||
util = require('util'),
|
|
||||||
logger = require('./logger.js'),
|
|
||||||
fs = require('fs'),
|
|
||||||
RedisStore = new RedisStoreLib({
|
RedisStore = new RedisStoreLib({
|
||||||
client: RDB,
|
client: RDB,
|
||||||
ttl: 60 * 60 * 24 * 14
|
ttl: 60 * 60 * 24 * 14
|
||||||
}),
|
}),
|
||||||
nconf = require('nconf'),
|
|
||||||
|
user = require('./user'),
|
||||||
|
Groups = require('./groups'),
|
||||||
|
posts = require('./posts'),
|
||||||
|
favourites = require('./favourites'),
|
||||||
|
utils = require('../public/src/utils'),
|
||||||
|
topics = require('./topics'),
|
||||||
|
categories = require('./categories'),
|
||||||
|
notifications = require('./notifications'),
|
||||||
|
threadTools = require('./threadTools'),
|
||||||
|
postTools = require('./postTools'),
|
||||||
|
meta = require('./meta'),
|
||||||
|
logger = require('./logger'),
|
||||||
socketCookieParser = express.cookieParser(nconf.get('secret')),
|
socketCookieParser = express.cookieParser(nconf.get('secret')),
|
||||||
admin = {
|
admin = {
|
||||||
'categories': require('./admin/categories.js'),
|
'categories': require('./admin/categories'),
|
||||||
'user': require('./admin/user.js')
|
'user': require('./admin/user')
|
||||||
},
|
},
|
||||||
plugins = require('./plugins'),
|
plugins = require('./plugins');
|
||||||
winston = require('winston');
|
|
||||||
|
|
||||||
|
|
||||||
var users = {},
|
var users = {},
|
||||||
@@ -43,8 +44,9 @@ module.exports.logoutUser = function(uid) {
|
|||||||
userSockets[uid][i].emit('event:disconnect');
|
userSockets[uid][i].emit('event:disconnect');
|
||||||
userSockets[uid][i].disconnect();
|
userSockets[uid][i].disconnect();
|
||||||
|
|
||||||
if(!userSockets[uid])
|
if(!userSockets[uid]) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,11 +171,11 @@ module.exports.init = function(io) {
|
|||||||
var anonymousCount = getAnonymousCount(roomName);
|
var anonymousCount = getAnonymousCount(roomName);
|
||||||
|
|
||||||
if (uids.length === 0) {
|
if (uids.length === 0) {
|
||||||
io.sockets. in (roomName).emit('api:get_users_in_room', { users: [], anonymousCount:0 });
|
io.sockets. in (roomName).emit('api:get_users_in_room', { users: [], anonymousCount: anonymousCount });
|
||||||
} else {
|
} else {
|
||||||
user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) {
|
user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) {
|
||||||
if(!err)
|
if(!err)
|
||||||
io.sockets. in (roomName).emit('api:get_users_in_room', { users: users, anonymousCount:anonymousCount });
|
io.sockets. in (roomName).emit('api:get_users_in_room', { users: users, anonymousCount: anonymousCount });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,10 +249,6 @@ module.exports.init = function(io) {
|
|||||||
posts.getTopicPostStats();
|
posts.getTopicPostStats();
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('user.latest', function(data) {
|
|
||||||
user.latest(socket, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('user.email.exists', function(data) {
|
socket.on('user.email.exists', function(data) {
|
||||||
user.email.exists(socket, data.email);
|
user.email.exists(socket, data.email);
|
||||||
});
|
});
|
||||||
@@ -368,11 +366,24 @@ module.exports.init = function(io) {
|
|||||||
posts.emitContentTooShortAlert(socket);
|
posts.emitContentTooShortAlert(socket);
|
||||||
} else if (err.message === 'too-many-posts') {
|
} else if (err.message === 'too-many-posts') {
|
||||||
posts.emitTooManyPostsAlert(socket);
|
posts.emitTooManyPostsAlert(socket);
|
||||||
|
} else {
|
||||||
|
socket.emit('event:alert', {
|
||||||
|
title: 'Error',
|
||||||
|
message: err.message,
|
||||||
|
type: 'warning',
|
||||||
|
timeout: 7500
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
io.sockets.in('category_' + data.category_id).emit('event:new_topic', result.topicData);
|
||||||
|
io.sockets.in('recent_posts').emit('event:new_topic', result.topicData);
|
||||||
|
io.sockets.in('user/' + uid).emit('event:new_post', {
|
||||||
|
posts: result.postData
|
||||||
|
});
|
||||||
|
|
||||||
posts.getTopicPostStats();
|
posts.getTopicPostStats();
|
||||||
|
|
||||||
socket.emit('event:alert', {
|
socket.emit('event:alert', {
|
||||||
@@ -533,7 +544,7 @@ module.exports.init = function(io) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('api:posts.getRawPost', function(data) {
|
socket.on('api:posts.getRawPost', function(data) {
|
||||||
posts.getPostField(data.pid, 'content', function(raw) {
|
posts.getPostField(data.pid, 'content', function(err, raw) {
|
||||||
socket.emit('api:posts.getRawPost', {
|
socket.emit('api:posts.getRawPost', {
|
||||||
post: raw
|
post: raw
|
||||||
});
|
});
|
||||||
@@ -560,15 +571,33 @@ module.exports.init = function(io) {
|
|||||||
postTools.edit(uid, data.pid, data.title, data.content, data.images);
|
postTools.edit(uid, data.pid, data.title, data.content, data.images);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('api:posts.delete', function(data) {
|
socket.on('api:posts.delete', function(data, callback) {
|
||||||
postTools.delete(uid, data.pid, function() {
|
postTools.delete(uid, data.pid, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
posts.getTopicPostStats();
|
posts.getTopicPostStats();
|
||||||
|
|
||||||
|
io.sockets.in('topic_' + data.tid).emit('event:post_deleted', {
|
||||||
|
pid: data.pid
|
||||||
|
});
|
||||||
|
callback(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('api:posts.restore', function(data) {
|
socket.on('api:posts.restore', function(data, callback) {
|
||||||
postTools.restore(uid, data.pid, function() {
|
postTools.restore(uid, data.pid, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
posts.getTopicPostStats();
|
posts.getTopicPostStats();
|
||||||
|
|
||||||
|
io.sockets.in('topic_' + data.tid).emit('event:post_restored', {
|
||||||
|
pid: data.pid
|
||||||
|
});
|
||||||
|
callback(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -688,7 +717,7 @@ module.exports.init = function(io) {
|
|||||||
socket.on('api:composer.push', function(data) {
|
socket.on('api:composer.push', function(data) {
|
||||||
if (uid > 0 || meta.config.allowGuestPosting === '1') {
|
if (uid > 0 || meta.config.allowGuestPosting === '1') {
|
||||||
if (parseInt(data.tid) > 0) {
|
if (parseInt(data.tid) > 0) {
|
||||||
topics.getTopicData(data.tid, function(topicData) {
|
topics.getTopicData(data.tid, function(err, topicData) {
|
||||||
if (data.body)
|
if (data.body)
|
||||||
topicData.body = data.body;
|
topicData.body = data.body;
|
||||||
|
|
||||||
@@ -714,9 +743,7 @@ module.exports.init = function(io) {
|
|||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
posts.getPostFields(data.pid, ['content'], function(raw) {
|
posts.getPostFields(data.pid, ['content'], next);
|
||||||
next(null, raw);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
topics.getTitleByPid(data.pid, function(title) {
|
topics.getTitleByPid(data.pid, function(title) {
|
||||||
@@ -739,7 +766,7 @@ module.exports.init = function(io) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('api:composer.editCheck', function(pid) {
|
socket.on('api:composer.editCheck', function(pid) {
|
||||||
posts.getPostField(pid, 'tid', function(tid) {
|
posts.getPostField(pid, 'tid', function(err, tid) {
|
||||||
postTools.isMain(pid, tid, function(isMain) {
|
postTools.isMain(pid, tid, function(isMain) {
|
||||||
socket.emit('api:composer.editCheck', {
|
socket.emit('api:composer.editCheck', {
|
||||||
titleEditable: isMain
|
titleEditable: isMain
|
||||||
@@ -800,8 +827,12 @@ module.exports.init = function(io) {
|
|||||||
var start = data.after,
|
var start = data.after,
|
||||||
end = start + 9;
|
end = start + 9;
|
||||||
|
|
||||||
topics.getLatestTopics(uid, start, end, data.term, function(latestTopics) {
|
topics.getLatestTopics(uid, start, end, data.term, function(err, latestTopics) {
|
||||||
callback(latestTopics);
|
if (!err) {
|
||||||
|
callback(latestTopics);
|
||||||
|
} else {
|
||||||
|
winston.error('[socket api:topics.loadMoreRecentTopics] ' + err.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
85
tests/topics.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
var winston = require('winston');
|
||||||
|
|
||||||
|
process.on('uncaughtException', function (err) {
|
||||||
|
winston.error('Encountered error while running test suite: ' + err.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
var assert = require('assert'),
|
||||||
|
RDB = require('../mocks/redismock');
|
||||||
|
|
||||||
|
// Reds is not technically used in this test suite, but its invocation is required to stop the included
|
||||||
|
// libraries from trying to connect to the default Redis host/port
|
||||||
|
var reds = require('reds');
|
||||||
|
reds.createClient = function () {
|
||||||
|
return reds.client || (reds.client = RDB);
|
||||||
|
};
|
||||||
|
|
||||||
|
var Topics = require('../src/topics');
|
||||||
|
|
||||||
|
describe('Topic\'s', function() {
|
||||||
|
var topic;
|
||||||
|
|
||||||
|
beforeEach(function(){
|
||||||
|
topic = {
|
||||||
|
userId: 1,
|
||||||
|
categoryId: 1,
|
||||||
|
title: 'Test Topic Title',
|
||||||
|
content: 'The content of test topic'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.post', function() {
|
||||||
|
|
||||||
|
it('should create a new topic with proper parameters', function(done) {
|
||||||
|
Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) {
|
||||||
|
assert.equal(err, null, 'was created with error');
|
||||||
|
assert.ok(result);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to create new topic with wrong parameters', function(done) {
|
||||||
|
topic.userId = null;
|
||||||
|
|
||||||
|
Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) {
|
||||||
|
assert.equal(err.message, 'not-logged-in');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Get methods', function() {
|
||||||
|
var newTopic;
|
||||||
|
var newPost;
|
||||||
|
|
||||||
|
beforeEach(function(done){
|
||||||
|
Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) {
|
||||||
|
newTopic = result.topicData;
|
||||||
|
newPost = result.postData;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.getTopicData', function() {
|
||||||
|
it('should not receive errors', function(done) {
|
||||||
|
Topics.getTopicData(newTopic.tid, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.getTopicDataWithUser', function() {
|
||||||
|
it('should not receive errors', function(done) {
|
||||||
|
Topics.getTopicDataWithUser(newTopic.tid, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function() {
|
||||||
|
RDB.send_command('flushdb', [], function(error){
|
||||||
|
if(error){
|
||||||
|
winston.error(error);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||