mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-16 13:30:23 +01:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4d4c3cc92 | ||
|
|
3a12ba177a | ||
|
|
28e1538fdb | ||
|
|
eed66c099b | ||
|
|
ece2edf579 | ||
|
|
1961f01cab | ||
|
|
4a214b6ef0 | ||
|
|
dda429ab5d | ||
|
|
15feaafd68 | ||
|
|
14e7907e06 | ||
|
|
b4b483b35a | ||
|
|
79c9bdba7f | ||
|
|
a6837a7869 | ||
|
|
0a485a7ff6 | ||
|
|
bca1602474 | ||
|
|
96ee0a2017 | ||
|
|
25550e18d0 | ||
|
|
154d0160bc | ||
|
|
aefa56221b | ||
|
|
dd40cbc139 | ||
|
|
23db2e5c9e | ||
|
|
eff1b174c0 | ||
|
|
545069b069 | ||
|
|
f767535ce5 | ||
|
|
aeebd069e2 | ||
|
|
d2aa2a9a29 | ||
|
|
d7eb30ccbd | ||
|
|
9bc12f28b4 | ||
|
|
4d11fba20a | ||
|
|
cb6f587f24 | ||
|
|
c647793512 | ||
|
|
caa057ff4d | ||
|
|
43152bdaf9 | ||
|
|
4cdb7ff32b | ||
|
|
7cbb01a78a | ||
|
|
555eddff83 | ||
|
|
a3cab53b73 | ||
|
|
454d5827fd | ||
|
|
186c426691 | ||
|
|
5e1e1ecf6f | ||
|
|
2d3d0f688a | ||
|
|
7975844e4d | ||
|
|
f2b138510e | ||
|
|
1c95ef4060 | ||
|
|
b43e12a42a | ||
|
|
3f793ae902 | ||
|
|
ac56f3a30a | ||
|
|
e2ffac74bc | ||
|
|
1c08ca54c5 | ||
|
|
f1547a7b1f | ||
|
|
746fa93c80 | ||
|
|
5ab1758d28 | ||
|
|
2e4e1df3d9 | ||
|
|
eded61d66e | ||
|
|
d9360da9a5 | ||
|
|
2b7a1b7515 | ||
|
|
1e66116e1d | ||
|
|
a95582b382 | ||
|
|
7860cfdec3 | ||
|
|
30bbea9c74 | ||
|
|
481105d6be | ||
|
|
f939a632a6 | ||
|
|
c05f56d28c | ||
|
|
b844251587 | ||
|
|
b9bd907a6b | ||
|
|
17d86a2a35 | ||
|
|
c70c67394a | ||
|
|
92d3559146 | ||
|
|
0e9a3c3a9f | ||
|
|
41263f0332 | ||
|
|
3747427538 | ||
|
|
b65554ca15 | ||
|
|
00cb15d3c8 | ||
|
|
6690f49c4e | ||
|
|
ff805a704d | ||
|
|
f83be710a0 | ||
|
|
3933549659 | ||
|
|
4993b74c23 | ||
|
|
76e7a98c88 | ||
|
|
999e98e43d | ||
|
|
74af205426 | ||
|
|
9ad82f4907 | ||
|
|
2e6b37e018 | ||
|
|
67070e335e | ||
|
|
22536e694c | ||
|
|
929282a2f7 | ||
|
|
b0092b68c6 | ||
|
|
91446378bd | ||
|
|
dceec0ce46 | ||
|
|
1856e394f3 | ||
|
|
8dc7a0dbbf | ||
|
|
6e17ff7981 |
@@ -1,16 +1,15 @@
|
||||
Please support NodeBB development! Check out our IndieGoGo campaign and like, share, and follow us :)
|
||||
[NodeBB Homepage](http://www.nodebb.org/ "NodeBB") # [IndieGoGo campaign](https://www.indiegogo.com/projects/nodebb-the-discussion-platform-of-the-future/ "IndieGoGo") # [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") # [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
[NodeBB Homepage](http://www.nodebb.org/ "NodeBB") # [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") # [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
|
||||
# NodeBB
|
||||
**NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
* Our [Indiegogo campaign](https://www.indiegogo.com/projects/nodebb-the-discussion-platform-of-the-future/) is accepting contributions until August 17th, 2013
|
||||
* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/designcreateplay/NodeBB/wiki/Version-History-%26-Roadmap)
|
||||
* If you are a developer, feel free to check out the source and submit pull requests.
|
||||
* If you are a designer, NodeBB needs themes! NodeBB will accept any LESS or CSS file and use it in place of the default Twitter Bootstrap theme. Consider extending Bootstrap themes by extending the base bootstrap LESS file.
|
||||
|
||||
84
app.js
84
app.js
@@ -16,45 +16,64 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Configuration setup
|
||||
nconf = require('nconf');
|
||||
nconf.argv().file({ file: __dirname + '/config.json'});
|
||||
|
||||
var fs = require('fs'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
pkg = require('./package.json'),
|
||||
url = require('url');
|
||||
url = require('url'),
|
||||
meta = require('./src/meta.js');
|
||||
|
||||
// Runtime environment
|
||||
global.env = process.env.NODE_ENV || 'production',
|
||||
|
||||
// Configuration setup
|
||||
nconf.argv().file({ file: __dirname + '/config.json'});
|
||||
|
||||
|
||||
winston.remove(winston.transports.Console);
|
||||
winston.add(winston.transports.Console, {
|
||||
colorize:true
|
||||
});
|
||||
|
||||
winston.add(winston.transports.File, {
|
||||
filename:'error.log',
|
||||
level:'error'
|
||||
})
|
||||
|
||||
// TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
|
||||
winston.err = function(err) {
|
||||
winston.error(err.stack);
|
||||
};
|
||||
|
||||
// Log GNU copyright info along with server info
|
||||
console.log('Info: NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
|
||||
console.log('Info: This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
console.log('Info: This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||
console.log('Info: ===');
|
||||
winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
|
||||
winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||
winston.info('===');
|
||||
|
||||
|
||||
|
||||
if(nconf.get('upgrade')) {
|
||||
require('./src/upgrade').upgrade();
|
||||
meta.configs.init(function() {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
} else if (!nconf.get('setup') && nconf.get('base_url')) {
|
||||
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/');
|
||||
global.nconf = nconf;
|
||||
|
||||
console.log('Info: Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
|
||||
console.log('Info: Base Configuration OK.');
|
||||
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('Base Configuration OK.');
|
||||
|
||||
// TODO: Replace this with nconf-redis
|
||||
var meta = require('./src/meta.js');
|
||||
global.config = {};
|
||||
meta.config.get(function(config) {
|
||||
for(c in config) {
|
||||
if (config.hasOwnProperty(c)) {
|
||||
global.config[c] = config[c];
|
||||
}
|
||||
meta.configs.init(function() {
|
||||
// Initial setup for Redis & Reds
|
||||
var reds = require('reds');
|
||||
RDB = require('./src/redis.js');
|
||||
reds.createClient = function() {
|
||||
return reds.client || (reds.client = RDB);
|
||||
}
|
||||
|
||||
var categories = require('./src/categories.js'),
|
||||
RDB = require('./src/redis.js'),
|
||||
templates = require('./public/src/templates.js'),
|
||||
webserver = require('./src/webserver.js'),
|
||||
websockets = require('./src/websockets.js'),
|
||||
@@ -74,7 +93,7 @@ if(nconf.get('upgrade')) {
|
||||
templates.init([
|
||||
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
|
||||
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
|
||||
'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis',
|
||||
'emails/header', 'emails/footer',
|
||||
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
]);
|
||||
@@ -83,10 +102,10 @@ if(nconf.get('upgrade')) {
|
||||
|
||||
//setup scripts to be moved outside of the app in future.
|
||||
function setup_categories() {
|
||||
console.log('Info: Checking categories...');
|
||||
winston.info('Checking categories...');
|
||||
categories.getAllCategories(function(data) {
|
||||
if (data.categories.length === 0) {
|
||||
console.log('Info: Setting up default categories...');
|
||||
winston.info('Setting up default categories...');
|
||||
|
||||
fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
|
||||
default_categories = JSON.parse(default_categories);
|
||||
@@ -96,27 +115,26 @@ if(nconf.get('upgrade')) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
console.log('Info: Hardcoding uid 1 as an admin');
|
||||
|
||||
winston.info('Hardcoding uid 1 as an admin');
|
||||
var user = require('./src/user.js');
|
||||
user.makeAdministrator(1);
|
||||
|
||||
|
||||
} else {
|
||||
console.log('Info: Categories OK. Found ' + data.categories.length + ' categories.');
|
||||
winston.info('Categories OK. Found ' + data.categories.length + ' categories.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
setup_categories();
|
||||
}(global.configuration));
|
||||
});
|
||||
|
||||
} else {
|
||||
// New install, ask setup questions
|
||||
if (nconf.get('setup')) console.log('Info: NodeBB Setup Triggered via Command Line');
|
||||
else console.log('Info: Configuration not found, starting NodeBB setup');
|
||||
if (nconf.get('setup')) winston.info('NodeBB Setup Triggered via Command Line');
|
||||
else winston.info('Configuration not found, starting NodeBB setup');
|
||||
|
||||
var install = require('./src/install');
|
||||
|
||||
@@ -128,7 +146,7 @@ if(nconf.get('upgrade')) {
|
||||
|
||||
install.setup(function(err) {
|
||||
if (err) {
|
||||
console.log('Error: There was a problem completing NodeBB setup: ', err.message);
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
} else {
|
||||
if (!nconf.get('setup')) {
|
||||
process.stdout.write(
|
||||
@@ -136,7 +154,7 @@ if(nconf.get('upgrade')) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPLv3 or later",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -33,7 +33,9 @@
|
||||
"sitemap": "~0.6.0",
|
||||
"cheerio": "~0.12.0",
|
||||
"request": "~2.25.0",
|
||||
"reds": "~0.2.4"
|
||||
"reds": "~0.2.4",
|
||||
"winston": "~0.7.2",
|
||||
"nodebb-plugin-mentions": "~0.1.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/designcreateplay/NodeBB/issues"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
@import "mixins";
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
/*background: #fdfdfd;*/ // port to default theme when it is implemented.
|
||||
-webkit-transition: margin-bottom 250ms ease;
|
||||
@@ -38,7 +42,7 @@ button, a {
|
||||
vertical-align: 17%;
|
||||
}
|
||||
.nav .badge {
|
||||
vertical-align: 10%;
|
||||
vertical-align: 2%;
|
||||
}
|
||||
|
||||
#alert_window {
|
||||
@@ -197,6 +201,12 @@ footer.footer {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.account-block {
|
||||
div {
|
||||
padding-bottom:10px;
|
||||
}
|
||||
}
|
||||
|
||||
.account-picture-block{
|
||||
display:inline-block;
|
||||
vertical-align:top;
|
||||
@@ -213,7 +223,6 @@ footer.footer {
|
||||
|
||||
.user-profile-picture {
|
||||
width:128px;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.user-picture-label {
|
||||
@@ -235,6 +244,7 @@ footer.footer {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
overflow:hidden;
|
||||
p {
|
||||
color: #333;
|
||||
}
|
||||
@@ -435,14 +445,33 @@ body .navbar .nodebb-inline-block {
|
||||
padding-bottom:20px;
|
||||
}
|
||||
|
||||
|
||||
.dropdown-toggle {
|
||||
i {
|
||||
font-size: 12px;
|
||||
|
||||
@-webkit-keyframes glow
|
||||
{
|
||||
from {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
50% {text-shadow: 0 0 10px #aaf, 0 0 10px #aaf, 0 0 10px #aaf;}
|
||||
to {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
}
|
||||
@keyframes glow
|
||||
{
|
||||
from {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
50% {text-shadow: 0 0 10px #aaf, 0 0 10px #aaf, 0 0 10px #aaf;}
|
||||
to {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
}
|
||||
|
||||
|
||||
&.active {
|
||||
color: #558;
|
||||
|
||||
text-shadow: 0 0 1em #aaf, 0 0 1em #aaf, 0 0 1em #aaf;
|
||||
-webkit-animation:glow 1.5s infinite linear;
|
||||
animation:glow 1.5s infinite linear;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,16 +552,23 @@ body .navbar .nodebb-inline-block {
|
||||
visibility: visible;
|
||||
|
||||
.btn-toolbar {
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
&.formatting-bar {
|
||||
width: 90%;
|
||||
margin: 0 auto 8px auto;
|
||||
|
||||
span {
|
||||
color: white;
|
||||
span {
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.action-bar {
|
||||
width: 90%;
|
||||
margin: 8px auto 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
@@ -543,7 +579,8 @@ body .navbar .nodebb-inline-block {
|
||||
-webkit-border-radius: 0px;
|
||||
-moz-border-radius: 0px;
|
||||
border-radius: 0px;
|
||||
margin: 1% 1% 2% 1%;
|
||||
margin: 5px auto 10px auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@@ -552,41 +589,43 @@ body .navbar .nodebb-inline-block {
|
||||
padding: 0.5em;
|
||||
display: block;
|
||||
width: 90%;
|
||||
margin: 0.5em auto;
|
||||
margin: 0em auto;
|
||||
resize: none;
|
||||
color: white;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#imagedrop {
|
||||
text-align:center;
|
||||
color:white;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height:230px;
|
||||
line-height:230px;
|
||||
font-size:20px;
|
||||
vertical-align: middle;
|
||||
.imagedrop {
|
||||
text-align: center;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 214px;
|
||||
line-height: 214px;
|
||||
font-size: 20px;
|
||||
vertical-align: middle;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#imagelist {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 0px;
|
||||
padding-left:2em;
|
||||
|
||||
div {
|
||||
margin-right:5px;
|
||||
}
|
||||
span {
|
||||
line-height:20px;
|
||||
float:left;
|
||||
}
|
||||
button {
|
||||
padding-left:5px;
|
||||
}
|
||||
.imagelist {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
left: 5%;
|
||||
|
||||
div {
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
span {
|
||||
line-height:20px;
|
||||
float:left;
|
||||
}
|
||||
|
||||
button {
|
||||
padding-left:5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -827,7 +866,7 @@ body .navbar .nodebb-inline-block {
|
||||
.form-search {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
margin-bottom:0px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.search-result-post {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
}
|
||||
|
||||
.profile-block, .post-block {
|
||||
position:relative;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
@@ -98,12 +99,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&.deleted {
|
||||
-moz-opacity: 0.30;
|
||||
opacity: 0.30;
|
||||
}
|
||||
/*http://stackoverflow.com/questions/11037517/bootstrap-making-responsive-changes-to-layout*/
|
||||
@media (max-width: 979px) {
|
||||
|
||||
|
||||
.span12-tablet {
|
||||
width: 100% !important;
|
||||
margin-left:0px;
|
||||
@@ -111,14 +116,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 979px) {
|
||||
.speech-bubble:after
|
||||
{
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -7px;
|
||||
border-style: solid;
|
||||
border-width: 7px 7px 7px 0;
|
||||
border-color: transparent #FFFFFF;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.speech-bubble:before
|
||||
{
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -8px;
|
||||
border-style: solid;
|
||||
border-width: 7px 7px 7px 0;
|
||||
border-color: transparent rgba(0, 0, 0, 0.125);
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.main-post {
|
||||
h3 {
|
||||
margin: 0;
|
||||
|
||||
.topic-title {
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
text-overflow:ellipsis;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
@@ -2,7 +2,7 @@ var ajaxify = {};
|
||||
|
||||
|
||||
(function($) {
|
||||
|
||||
|
||||
var location = document.location || window.location,
|
||||
rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''),
|
||||
content = null;
|
||||
@@ -26,6 +26,7 @@ var ajaxify = {};
|
||||
};
|
||||
|
||||
ajaxify.go = function(url, callback, template, quiet) {
|
||||
$(window).off('scroll');
|
||||
// leave room and join global
|
||||
app.enter_room('global');
|
||||
|
||||
@@ -38,14 +39,14 @@ var ajaxify = {};
|
||||
}
|
||||
|
||||
var tpl_url = templates.get_custom_map(url.split('?')[0]);
|
||||
|
||||
|
||||
if (tpl_url == false && !templates[url]) {
|
||||
if(url === '' || url === '/') {
|
||||
tpl_url = 'home';
|
||||
} else {
|
||||
tpl_url = url.split('/')[0].split('?')[0];
|
||||
}
|
||||
|
||||
|
||||
} else if (templates[url]) {
|
||||
tpl_url = url;
|
||||
}
|
||||
@@ -66,9 +67,9 @@ var ajaxify = {};
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
|
||||
app.process_page();
|
||||
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function() {
|
||||
if(window.location.hash)
|
||||
hash = window.location.hash;
|
||||
@@ -76,11 +77,9 @@ var ajaxify = {};
|
||||
app.scrollToPost(hash.substr(1));
|
||||
});
|
||||
|
||||
}, url, template);
|
||||
utils.refreshTitle(url);
|
||||
|
||||
socket.emit('api:meta.buildTitle', url, function(title) {
|
||||
document.title = title;
|
||||
});
|
||||
}, url, template);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -95,12 +94,15 @@ var ajaxify = {};
|
||||
|
||||
// Enhancing all anchors to ajaxify...
|
||||
$(document.body).on('click', 'a', function(e) {
|
||||
if (this.href == window.location.href + "#") return;
|
||||
if(this.href.slice(-1) === "#") return;
|
||||
|
||||
|
||||
function hrefEmpty(href) {
|
||||
return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
|
||||
}
|
||||
|
||||
if(hrefEmpty(this.href)) return;
|
||||
|
||||
var url = this.href.replace(rootUrl +'/', '');
|
||||
|
||||
|
||||
if (this.target !== '') return;
|
||||
|
||||
if (!e.ctrlKey && e.which === 1) {
|
||||
@@ -127,7 +129,7 @@ var ajaxify = {};
|
||||
|
||||
script.type = "text/javascript";
|
||||
try {
|
||||
script.appendChild(document.createTextNode(data));
|
||||
script.appendChild(document.createTextNode(data));
|
||||
} catch(e) {
|
||||
script.text = data;
|
||||
}
|
||||
@@ -163,5 +165,5 @@ var ajaxify = {};
|
||||
evalScript(scripts[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}(jQuery));
|
||||
@@ -8,18 +8,18 @@ var socket,
|
||||
var showWelcomeMessage = false;
|
||||
|
||||
function loadConfig() {
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: RELATIVE_PATH + '/api/config',
|
||||
success: function(data) {
|
||||
API_URL = data.api_url;
|
||||
|
||||
|
||||
config = data;
|
||||
socket = io.connect(config.socket.address + (config.socket.port ? ':' + config.socket.port : ''));
|
||||
|
||||
|
||||
var reconnecting = false;
|
||||
var reconnectTries = 0;
|
||||
|
||||
|
||||
socket.on('event:connect', function(data) {
|
||||
console.log('connected to nodebb socket: ', data);
|
||||
app.username = data.username;
|
||||
@@ -29,11 +29,7 @@ var socket,
|
||||
socket.on('event:alert', function(data) {
|
||||
app.alert(data);
|
||||
});
|
||||
|
||||
socket.on('event:consolelog', function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
|
||||
socket.on('connect', function(data){
|
||||
if(reconnecting) {
|
||||
setTimeout(function(){
|
||||
@@ -50,28 +46,28 @@ var socket,
|
||||
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
socket.on('reconnecting', function(data) {
|
||||
function showDisconnectModal() {
|
||||
$('#disconnect-modal').modal({
|
||||
backdrop:'static',
|
||||
show:true
|
||||
});
|
||||
|
||||
|
||||
$('#reload-button').on('click',function(){
|
||||
$('#disconnect-modal').modal('hide');
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
reconnecting = true;
|
||||
reconnectTries++;
|
||||
|
||||
|
||||
if(reconnectTries > 4) {
|
||||
showDisconnectModal();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
app.alert({
|
||||
alert_id: 'connection_alert',
|
||||
title: 'Reconnecting',
|
||||
@@ -80,15 +76,15 @@ var socket,
|
||||
timeout: 5000
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:user.get_online_users', function(users) {
|
||||
jQuery('.username-field').each(function() {
|
||||
if (this.processed === true)
|
||||
if (this.processed === true)
|
||||
return;
|
||||
|
||||
|
||||
var el = jQuery(this),
|
||||
uid = el.parents('li').attr('data-uid');
|
||||
|
||||
|
||||
if (uid && jQuery.inArray(uid, users) !== -1) {
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle"></i>');
|
||||
@@ -96,19 +92,19 @@ var socket,
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle-blank"></i>');
|
||||
}
|
||||
|
||||
|
||||
el.processed = true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.enter_room('global');
|
||||
|
||||
|
||||
|
||||
},
|
||||
async: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// takes a string like 1000 and returns 1,000
|
||||
app.addCommas = function(text) {
|
||||
return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
|
||||
@@ -125,14 +121,14 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
// use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance
|
||||
// use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance
|
||||
// type : error, success, info, warning/notify
|
||||
// title = bolded title text
|
||||
// message = alert message content
|
||||
// timeout default = permanent
|
||||
// location : alert_window (default) or content
|
||||
app.alert = function(params) {
|
||||
var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime());
|
||||
var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime());
|
||||
|
||||
var alert = $('#'+alert_id);
|
||||
|
||||
@@ -142,7 +138,7 @@ var socket,
|
||||
$(this).remove();
|
||||
});
|
||||
}, timeout);
|
||||
|
||||
|
||||
$(div).attr('timeoutId', timeoutId);
|
||||
}
|
||||
|
||||
@@ -150,7 +146,7 @@ var socket,
|
||||
alert.find('strong').html(params.title);
|
||||
alert.find('p').html(params.message);
|
||||
alert.attr('class', "alert toaster-alert " + ((params.type=='warning') ? '' : "alert-" + params.type));
|
||||
|
||||
|
||||
clearTimeout(alert.attr('timeoutId'));
|
||||
startTimeout(alert, params.timeout);
|
||||
}
|
||||
@@ -164,7 +160,7 @@ var socket,
|
||||
strong.innerHTML = params.title;
|
||||
|
||||
div.className = "alert toaster-alert " + ((params.type=='warning') ? '' : "alert-" + params.type);
|
||||
|
||||
|
||||
div.setAttribute('id', alert_id);
|
||||
div.appendChild(button);
|
||||
div.appendChild(strong);
|
||||
@@ -176,7 +172,7 @@ var socket,
|
||||
div.parentNode.removeChild(div);
|
||||
}
|
||||
|
||||
if (params.location == null)
|
||||
if (params.location == null)
|
||||
params.location = 'alert_window';
|
||||
|
||||
jQuery('#'+params.location).prepend(jQuery(div).fadeIn('100'));
|
||||
@@ -220,18 +216,17 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
app.current_room = null;
|
||||
app.current_room = null;
|
||||
app.enter_room = function(room) {
|
||||
|
||||
if(socket) {
|
||||
if (app.current_room === room)
|
||||
if (app.current_room === room)
|
||||
return;
|
||||
|
||||
|
||||
socket.emit('event:enter_room', {
|
||||
'enter': room,
|
||||
'leave': app.current_room
|
||||
});
|
||||
|
||||
|
||||
app.current_room = room;
|
||||
}
|
||||
};
|
||||
@@ -242,7 +237,7 @@ var socket,
|
||||
jQuery('.post-row').each(function() {
|
||||
uids.push(this.getAttribute('data-uid'));
|
||||
});
|
||||
|
||||
|
||||
socket.emit('api:user.get_online_users', uids);
|
||||
}
|
||||
|
||||
@@ -258,7 +253,7 @@ var socket,
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
|
||||
|
||||
jQuery('#main-nav li').removeClass('active');
|
||||
if(active) {
|
||||
jQuery('#main-nav li a').each(function() {
|
||||
@@ -273,7 +268,7 @@ var socket,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -286,7 +281,7 @@ var socket,
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if(showWelcomeMessage) {
|
||||
showWelcomeMessage = false;
|
||||
if(document.readyState !== 'complete') {
|
||||
@@ -330,36 +325,54 @@ var socket,
|
||||
if(app.infiniteLoaderActive)
|
||||
return;
|
||||
app.infiniteLoaderActive = true;
|
||||
socket.emit('api:topic.loadMore', {
|
||||
tid: tid,
|
||||
after: document.querySelectorAll('#post-container li[data-pid]').length
|
||||
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) {
|
||||
app.createNewPosts(data);
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
if(callback)
|
||||
callback(data.posts);
|
||||
});
|
||||
}
|
||||
|
||||
app.scrollToPost = function(pid) {
|
||||
|
||||
if(!pid)
|
||||
return;
|
||||
var container = $(document.body),
|
||||
scrollTo = $('#post_anchor_' + pid);
|
||||
|
||||
if(!scrollTo.length) {
|
||||
var tid = $('#post-container').attr('data-tid');
|
||||
app.loadMorePosts(tid, function() {
|
||||
app.scrollToPost(pid);
|
||||
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()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
container.animate({
|
||||
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() {
|
||||
@@ -428,6 +441,6 @@ var socket,
|
||||
el.off('mousemove');
|
||||
onTouchEnd();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var rep = $('#reputation');
|
||||
rep.html(app.addCommas(rep.html()));
|
||||
|
||||
var postcount = $('#postcount');
|
||||
postcount.html(app.addCommas(postcount.html()));
|
||||
app.addCommasToNumbers();
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
|
||||
@@ -2,19 +2,35 @@
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid');
|
||||
|
||||
|
||||
function createMenu() {
|
||||
var userslug = $('.account-username-box').attr('data-userslug');
|
||||
var links = $('<div class="account-sub-links inline-block pull-right">\
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/' + userslug + '/settings">settings</a></span>\
|
||||
<span id="favouritesLink" class="pull-right"><a href="/users/' + userslug + '/favourites">favourites</a></span>\
|
||||
<span class="pull-right"><a href="/users/' + userslug + '/followers">followers</a></span>\
|
||||
<span class="pull-right"><a href="/users/' + userslug + '/following">following</a></span>\
|
||||
<span id="editLink" class="pull-right"><a href="/users/' + userslug + '/edit">edit</a></span>\
|
||||
</div>');
|
||||
|
||||
$('.account-username-box').append(links);
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
createMenu();
|
||||
|
||||
var editLink = $('#editLink');
|
||||
var settingsLink = $('#settingsLink');
|
||||
var favouritesLink = $('#favouritesLink');
|
||||
|
||||
if(yourid === "0") {
|
||||
editLink.hide();
|
||||
settingsLink.hide();
|
||||
}
|
||||
else if(yourid !== theirid) {
|
||||
if(yourid === "0" || yourid !== theirid) {
|
||||
editLink.hide();
|
||||
settingsLink.hide();
|
||||
favouritesLink.hide();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -8,6 +8,11 @@
|
||||
return (parent.attr('data-admin') !== "0");
|
||||
}
|
||||
|
||||
function isUserBanned(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0");
|
||||
}
|
||||
|
||||
function getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
@@ -34,6 +39,20 @@
|
||||
deleteBtn.show();
|
||||
});
|
||||
|
||||
jQuery('.ban-btn').each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var isAdmin = isUserAdmin(banBtn);
|
||||
var isBanned = isUserBanned(banBtn);
|
||||
|
||||
if(isAdmin)
|
||||
banBtn.addClass('disabled');
|
||||
else if(isBanned)
|
||||
banBtn.addClass('btn-warning');
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
|
||||
jQuery('.admin-btn').on('click', function() {
|
||||
var adminBtn = $(this);
|
||||
var isAdmin = isUserAdmin(adminBtn);
|
||||
@@ -74,6 +93,30 @@
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
jQuery('.ban-btn').on('click', function() {
|
||||
var banBtn = $(this);
|
||||
var isAdmin = isUserAdmin(banBtn);
|
||||
var isBanned = isUserBanned(banBtn);
|
||||
var parent = banBtn.parents('.users-box');
|
||||
var uid = getUID(banBtn);
|
||||
|
||||
if(!isAdmin) {
|
||||
if(isBanned) {
|
||||
socket.emit('api:admin.user.unbanUser', uid);
|
||||
banBtn.removeClass('btn-warning');
|
||||
parent.attr('data-banned', 0);
|
||||
} else {
|
||||
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') +'"?', function(confirm) {
|
||||
socket.emit('api:admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -171,9 +214,9 @@
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreUsers) {
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
facebook_url = templates.get('facebook-share-url'),
|
||||
google_url = templates.get('google-share-url'),
|
||||
loadingMoreTopics = false;
|
||||
|
||||
|
||||
app.enter_room(room);
|
||||
|
||||
twitterEl.addEventListener('click', function() {
|
||||
@@ -36,7 +36,6 @@
|
||||
]);
|
||||
|
||||
function onNewTopic(data) {
|
||||
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }),
|
||||
topic = document.createElement('div'),
|
||||
container = document.getElementById('topics-container'),
|
||||
@@ -49,7 +48,7 @@
|
||||
|
||||
topic.innerHTML = html;
|
||||
topic = topic.querySelector('a');
|
||||
|
||||
|
||||
if (numTopics > 0) {
|
||||
for(x=0;x<numTopics;x++) {
|
||||
if (topics[x].querySelector('.icon-pushpin')) continue;
|
||||
@@ -61,6 +60,8 @@
|
||||
container.insertBefore(topic, null);
|
||||
$(topic).hide().fadeIn('slow');
|
||||
}
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
}
|
||||
|
||||
socket.on('event:new_topic', onNewTopic);
|
||||
@@ -74,7 +75,7 @@
|
||||
var recent_replies = document.getElementById('category_recent_replies');
|
||||
|
||||
recent_replies.innerHTML = '';
|
||||
|
||||
|
||||
var frag = document.createDocumentFragment(),
|
||||
li = document.createElement('li');
|
||||
for (var i=0,numPosts=posts.length; i<numPosts; i++) {
|
||||
@@ -94,7 +95,7 @@
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: topics }),
|
||||
@@ -105,14 +106,14 @@
|
||||
|
||||
container.append(html);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function loadMoreTopics(cid) {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:category.loadMore', {
|
||||
cid: cid,
|
||||
after: $('#topics-container').children().length
|
||||
cid: cid,
|
||||
after: $('#topics-container').children().length
|
||||
}, function(data) {
|
||||
if(data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
@@ -122,9 +123,9 @@
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function(ev) {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreTopics) {
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics(cid);
|
||||
}
|
||||
});
|
||||
|
||||
11
public/src/forum/favourites.js
Normal file
11
public/src/forum/favourites.js
Normal file
@@ -0,0 +1,11 @@
|
||||
(function() {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('.user-favourite-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -10,13 +10,7 @@
|
||||
$('#no-followers-notice').show();
|
||||
}
|
||||
|
||||
$('.reputation').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
$('.postcount').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
app.addCommasToNumbers();
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -32,14 +32,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
$('.reputation').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
$('.postcount').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
app.addCommasToNumbers();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
user_label = document.getElementById('user_label'),
|
||||
active_record = document.getElementById('active_record'),
|
||||
right_menu = document.getElementById('right-menu');
|
||||
|
||||
|
||||
socket.emit('user.count', {});
|
||||
socket.on('user.count', function(data) {
|
||||
num_users.innerHTML = "We currently have <b>" + data.count + "</b> registered users.";
|
||||
@@ -28,24 +28,28 @@
|
||||
|
||||
socket.emit('api:user.active.get');
|
||||
socket.on('api:user.active.get', function(data) {
|
||||
|
||||
|
||||
var plural_users = parseInt(data.users) !== 1,
|
||||
plural_anon = parseInt(data.anon) !== 1;
|
||||
|
||||
active_users.innerHTML = 'There ' + (plural_users ? 'are' : 'is') + ' <strong>' + data.users + '</strong> user' + (plural_users ? 's' : '') + ' and <strong>' + data.anon + '</strong> guest' + (plural_anon ? 's' : '') + ' online';
|
||||
});
|
||||
|
||||
|
||||
socket.emit('api:user.active.get_record');
|
||||
socket.on('api:user.active.get_record', function(data) {
|
||||
active_record.innerHTML = "most users ever online was <strong>" + data.record + "</strong> on <strong>" + (new Date(parseInt(data.timestamp,10))).toUTCString() + "</strong>";
|
||||
});
|
||||
|
||||
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
|
||||
|
||||
|
||||
socket.on('api:updateHeader', function(data) {
|
||||
|
||||
var rightMenu = $('#right-menu');
|
||||
if (data.uid > 0) {
|
||||
|
||||
var rightMenu = $('#right-menu'),
|
||||
isLoggedIn = data.uid > 0;
|
||||
|
||||
if (isLoggedIn) {
|
||||
jQuery('.nodebb-loggedin').show();
|
||||
jQuery('.nodebb-loggedout').hide();
|
||||
|
||||
var userLabel = rightMenu.find('#user_label');
|
||||
if(userLabel.length) {
|
||||
@@ -55,8 +59,7 @@
|
||||
userLabel.find('img').attr('src',data['picture']);
|
||||
if(data['username'])
|
||||
userLabel.find('span').html(data['username']);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
var userli = $('<li> \
|
||||
<a id="user_label" href="/users/'+data['userslug']+'"> \
|
||||
<img src="'+data['picture']+'"/> \
|
||||
@@ -69,6 +72,9 @@
|
||||
rightMenu.append(logoutli);
|
||||
}
|
||||
} else {
|
||||
jQuery('.nodebb-loggedin').hide();
|
||||
jQuery('.nodebb-loggedout').show();
|
||||
|
||||
rightMenu.html('');
|
||||
|
||||
var registerEl = document.createElement('li'),
|
||||
@@ -90,10 +96,39 @@
|
||||
notifTrigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (notifContainer.className.indexOf('open') === -1) {
|
||||
socket.emit('api:notifications.get');
|
||||
socket.emit('api:notifications.mark_all_read', null, function() {
|
||||
notifIcon.className = 'icon-circle-blank';
|
||||
utils.refreshTitle();
|
||||
socket.emit('api:notifications.get', null, function(data) {
|
||||
var notifFrag = document.createDocumentFragment(),
|
||||
notifEl = document.createElement('li'),
|
||||
numRead = data.read.length,
|
||||
numUnread = data.unread.length,
|
||||
x;
|
||||
notifList.innerHTML = '';
|
||||
if ((data.read.length + data.unread.length) > 0) {
|
||||
for(x=0;x<numUnread;x++) {
|
||||
notifEl.setAttribute('data-nid', data.unread[x].nid);
|
||||
notifEl.className = 'unread';
|
||||
notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
for(x=0;x<numRead;x++) {
|
||||
notifEl.setAttribute('data-nid', data.read[x].nid);
|
||||
notifEl.className = '';
|
||||
notifEl.innerHTML = '<a href="' + data.read[x].path + '"><span class="pull-right">' + utils.relativeTime(data.read[x].datetime, true) + '</span>' + data.read[x].text + '</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
notifEl.innerHTML = '<a>You have no notifications</a>';
|
||||
notifFrag.appendChild(notifEl);
|
||||
}
|
||||
notifList.appendChild(notifFrag);
|
||||
|
||||
if (data.unread.length > 0) notifIcon.className = 'icon-circle active';
|
||||
else notifIcon.className = 'icon-circle-blank';
|
||||
|
||||
socket.emit('api:notifications.mark_all_read', null, function() {
|
||||
notifIcon.className = 'icon-circle-blank';
|
||||
utils.refreshTitle();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -109,37 +144,15 @@
|
||||
if (nid > 0) socket.emit('api:notifications.mark_read', nid);
|
||||
}
|
||||
});
|
||||
socket.on('api:notifications.get', function(data) {
|
||||
var notifFrag = document.createDocumentFragment(),
|
||||
notifEl = document.createElement('li'),
|
||||
numRead = data.read.length,
|
||||
numUnread = data.unread.length,
|
||||
x;
|
||||
notifList.innerHTML = '';
|
||||
if ((data.read.length + data.unread.length) > 0) {
|
||||
for(x=0;x<numUnread;x++) {
|
||||
notifEl.setAttribute('data-nid', data.unread[x].nid);
|
||||
notifEl.className = 'unread';
|
||||
notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
for(x=0;x<numRead;x++) {
|
||||
notifEl.setAttribute('data-nid', data.read[x].nid);
|
||||
notifEl.className = '';
|
||||
notifEl.innerHTML = '<a href="' + data.read[x].path + '"><span class="pull-right">' + utils.relativeTime(data.read[x].datetime, true) + '</span>' + data.read[x].text + '</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
notifEl.innerHTML = '<a>You have no notifications</a>';
|
||||
notifFrag.appendChild(notifEl);
|
||||
}
|
||||
notifList.appendChild(notifFrag);
|
||||
|
||||
if (data.unread.length > 0) notifIcon.className = 'icon-circle active';
|
||||
else notifIcon.className = 'icon-circle-blank';
|
||||
});
|
||||
socket.on('event:new_notification', function() {
|
||||
document.querySelector('.notifications a i').className = 'icon-circle active';
|
||||
app.alert({
|
||||
alert_id: 'new_notif',
|
||||
title: 'New notification',
|
||||
message: 'You have unread notifications.',
|
||||
type: 'notify',
|
||||
timeout: 2000
|
||||
});
|
||||
utils.refreshTitle();
|
||||
});
|
||||
|
||||
@@ -148,12 +161,12 @@
|
||||
var username = data.username;
|
||||
var fromuid = data.fromuid;
|
||||
var message = data.message;
|
||||
|
||||
|
||||
require(['chat'], function(chat) {
|
||||
var chatModal = chat.createModalIfDoesntExist(username, fromuid);
|
||||
chatModal.show();
|
||||
chat.bringModalToTop(chatModal);
|
||||
|
||||
|
||||
chat.appendChatMessage(chatModal, message);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,26 +12,29 @@
|
||||
if (target) {
|
||||
document.location.href = target.getAttribute('data-url');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$('#login').on('click', function() {
|
||||
|
||||
var loginData = {
|
||||
'username': $('#username').val(),
|
||||
'password': $('#password').val(),
|
||||
'_csrf': $('#csrf-token').val()
|
||||
};
|
||||
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').hide();
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
if(!data.success) {
|
||||
$('#login-error-notify').html(data.message).show();
|
||||
} else {
|
||||
$('#login-error-notify').hide();
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
}
|
||||
},
|
||||
error : function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').show().delay(1000).fadeOut(250);
|
||||
$('#login-error-notify').show();
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
@@ -40,5 +43,6 @@
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
document.querySelector('#content input').focus();
|
||||
}());
|
||||
|
||||
@@ -69,9 +69,9 @@
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreTopics) {
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
pinned: templates.get('pinned')
|
||||
},
|
||||
topic_name = templates.get('topic_name');
|
||||
|
||||
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
|
||||
var room = 'topic_' + tid,
|
||||
adminTools = document.getElementById('thread-tools');
|
||||
|
||||
@@ -26,17 +26,12 @@
|
||||
if (thread_state.pinned === '1') set_pinned_state(true);
|
||||
|
||||
if (expose_tools === '1') {
|
||||
var deleteThreadEl = document.getElementById('delete_thread'),
|
||||
lockThreadEl = document.getElementById('lock_thread'),
|
||||
pinThreadEl = document.getElementById('pin_thread'),
|
||||
moveThreadEl = document.getElementById('move_thread'),
|
||||
moveThreadModal = $('#move_thread_modal');
|
||||
var moveThreadModal = $('#move_thread_modal');
|
||||
|
||||
adminTools.style.visibility = 'inherit';
|
||||
|
||||
// Add events to the thread tools
|
||||
deleteThreadEl.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('#delete_thread').on('click', function(e) {
|
||||
if (thread_state.deleted !== '1') {
|
||||
bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) {
|
||||
if (confirm) socket.emit('api:topic.delete', { tid: tid });
|
||||
@@ -46,30 +41,31 @@
|
||||
if (confirm) socket.emit('api:topic.restore', { tid: tid });
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
});
|
||||
|
||||
lockThreadEl.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('#lock_thread').on('click', function(e) {
|
||||
if (thread_state.locked !== '1') {
|
||||
socket.emit('api:topic.lock', { tid: tid });
|
||||
} else {
|
||||
socket.emit('api:topic.unlock', { tid: tid });
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
});
|
||||
|
||||
pinThreadEl.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('#pin_thread').on('click', function(e) {
|
||||
if (thread_state.pinned !== '1') {
|
||||
socket.emit('api:topic.pin', { tid: tid });
|
||||
} else {
|
||||
socket.emit('api:topic.unpin', { tid: tid });
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
});
|
||||
|
||||
moveThreadEl.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('#move_thread').on('click', function(e) {
|
||||
moveThreadModal.modal('show');
|
||||
}, false);
|
||||
return false;
|
||||
});
|
||||
|
||||
moveThreadModal.on('shown', function() {
|
||||
var loadingEl = document.getElementById('categories-loading');
|
||||
@@ -195,16 +191,18 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.emit('api:topic.followCheck', tid);
|
||||
followEl[0].addEventListener('click', function() {
|
||||
socket.emit('api:topic.follow', tid);
|
||||
}, false);
|
||||
if(followEl[0]) {
|
||||
followEl[0].addEventListener('click', function() {
|
||||
socket.emit('api:topic.follow', tid);
|
||||
}, false);
|
||||
}
|
||||
|
||||
// Infinite scrolling of posts
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
|
||||
if ($(window).scrollTop() > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
|
||||
app.loadMorePosts(tid);
|
||||
}
|
||||
});
|
||||
@@ -254,11 +252,9 @@
|
||||
|
||||
var element = $(this).find('i');
|
||||
if(element.attr('class') == 'icon-star-empty') {
|
||||
element.attr('class', 'icon-star');
|
||||
socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room});
|
||||
}
|
||||
else {
|
||||
element.attr('class', 'icon-star-empty');
|
||||
socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room});
|
||||
}
|
||||
});
|
||||
@@ -284,19 +280,21 @@
|
||||
socket.emit('api:posts.delete', { pid: pid }) :
|
||||
socket.emit('api:posts.restore', { pid: pid });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.post-container').delegate('.chat', 'click', function(e) {
|
||||
|
||||
var username = $(this).parents('li').attr('data-username');
|
||||
var touid = $(this).parents('li').attr('data-uid');
|
||||
|
||||
require(['chat'], function(chat){
|
||||
if(username === app.username || !app.username)
|
||||
return;
|
||||
|
||||
require(['chat'], function(chat) {
|
||||
var chatModal = chat.createModalIfDoesntExist(username, touid);
|
||||
chatModal.show();
|
||||
chat.bringModalToTop(chatModal);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
ajaxify.register_events([
|
||||
@@ -372,7 +370,7 @@
|
||||
var editedPostEl = document.getElementById('content_' + data.pid);
|
||||
|
||||
var editedPostTitle = $('#topic_title_'+data.pid);
|
||||
|
||||
|
||||
if(editedPostTitle.length > 0) {
|
||||
editedPostTitle.fadeOut(250, function() {
|
||||
editedPostTitle.html(data.title);
|
||||
@@ -387,9 +385,22 @@
|
||||
});
|
||||
|
||||
socket.on('api:posts.favourite', function(data) {
|
||||
if (data.status !== 'ok' && data.pid) {
|
||||
if (data.status === 'ok' && data.pid) {
|
||||
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
|
||||
if (favEl) favEl.className = 'icon-star-empty';
|
||||
if (favEl) {
|
||||
favEl.className = 'icon-star';
|
||||
$(favEl).parent().addClass('btn-warning');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:posts.unfavourite', function(data) {
|
||||
if (data.status === 'ok' && data.pid) {
|
||||
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
|
||||
if (favEl) {
|
||||
favEl.className = 'icon-star-empty';
|
||||
$(favEl).parent().removeClass('btn-warning');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -582,7 +593,7 @@
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
editEl = postEl.find('.edit'),
|
||||
deleteEl = postEl.find('.delete');
|
||||
|
||||
|
||||
if (state) {
|
||||
editEl.removeClass('none');
|
||||
deleteEl.removeClass('none');
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
$('#topics-container').empty();
|
||||
$('#category-no-topics').removeClass('hidden');
|
||||
app.alertSuccess('All topics marked as read!');
|
||||
$('#numUnreadBadge')
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse')
|
||||
.html('0');
|
||||
} else {
|
||||
app.alertError('There was an error marking topics read!');
|
||||
}
|
||||
@@ -78,21 +82,24 @@
|
||||
if(data.topics && data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
$('#topics-container').attr('data-next-start', data.nextStart);
|
||||
} else {
|
||||
$('#load-more-btn').hide();
|
||||
}
|
||||
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreTopics) {
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if($("body").height() <= $(window).height() && $('#topics-container').children().length)
|
||||
if($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
|
||||
$('#load-more-btn').show();
|
||||
|
||||
$('#load-more-btn').on('click', function() {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
@@ -67,13 +69,7 @@
|
||||
|
||||
});
|
||||
|
||||
$('.reputation').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
$('.postcount').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['users'].blocks['users']).parse({ users: users });
|
||||
@@ -109,9 +105,9 @@
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreUsers) {
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
define(['taskbar'], function(taskbar) {
|
||||
|
||||
|
||||
var module = {};
|
||||
|
||||
|
||||
module.bringModalToTop = function(chatModal) {
|
||||
var topZ = 0;
|
||||
$('.modal').each(function() {
|
||||
@@ -11,7 +10,7 @@ define(['taskbar'], function(taskbar) {
|
||||
topZ = thisZ;
|
||||
}
|
||||
});
|
||||
chatModal.css('zIndex', topZ+1);
|
||||
chatModal.css('zIndex', topZ + 1);
|
||||
}
|
||||
|
||||
module.createModalIfDoesntExist = function(username, touid) {
|
||||
@@ -39,7 +38,9 @@ define(['taskbar'], function(taskbar) {
|
||||
module.bringModalToTop(chatModal);
|
||||
});
|
||||
|
||||
addSendHandler(chatModal, touid);
|
||||
addSendHandler(chatModal, touid);
|
||||
|
||||
getChatMessages(chatModal, touid);
|
||||
}
|
||||
|
||||
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username});
|
||||
@@ -58,6 +59,14 @@ define(['taskbar'], function(taskbar) {
|
||||
taskbar.minimize('chat', uuid);
|
||||
}
|
||||
|
||||
function getChatMessages(chatModal, touid) {
|
||||
socket.emit('getChatMessages', {touid:touid}, function(messages) {
|
||||
for(var i = 0; i<messages.length; ++i) {
|
||||
module.appendChatMessage(chatModal, messages[i].content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addSendHandler(chatModal, touid) {
|
||||
chatModal.find('#chat-message-input').off('keypress');
|
||||
chatModal.find('#chat-message-input').on('keypress', function(e) {
|
||||
|
||||
@@ -7,12 +7,29 @@ define(['taskbar'], function(taskbar) {
|
||||
postContainer: undefined,
|
||||
};
|
||||
|
||||
function createImageLabel(img, postImages) {
|
||||
var imageLabel = $('<div class="label"><span>'+ img.name +'</span></div>');
|
||||
var closeButton = $('<button class="close">×</button>');
|
||||
|
||||
closeButton.on('click', function(e) {
|
||||
|
||||
imageLabel.remove();
|
||||
var index = postImages.indexOf(img);
|
||||
if(index !== -1) {
|
||||
postImages.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
imageLabel.append(closeButton);
|
||||
return imageLabel;
|
||||
}
|
||||
|
||||
function loadFile(file) {
|
||||
var reader = new FileReader();
|
||||
var dropDiv = $('#imagedrop');
|
||||
var imagelist = $('#imagelist');
|
||||
var uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
|
||||
var posts = composer.posts[uuid];
|
||||
var reader = new FileReader(),
|
||||
dropDiv = $('.post-window .imagedrop'),
|
||||
imagelist = $('.post-window .imagelist'),
|
||||
uuid = dropDiv.parents('[data-uuid]').attr('data-uuid'),
|
||||
posts = composer.posts[uuid];
|
||||
|
||||
$(reader).on('loadend', function(e) {
|
||||
var bin = this.result;
|
||||
@@ -25,19 +42,9 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
posts.images.push(img);
|
||||
|
||||
var imageLabel = $('<div class="label"><span>'+ file.name +'</span></div>');
|
||||
var closeButton = $('<button class="close">×</button>');
|
||||
closeButton.on('click', function(e) {
|
||||
|
||||
imageLabel.remove();
|
||||
var index = posts.images.indexOf(img);
|
||||
if(index !== -1) {
|
||||
posts.images.splice(index, 1);
|
||||
}
|
||||
});
|
||||
var imageLabel = createImageLabel(img, posts.images);
|
||||
|
||||
imageLabel.append(closeButton);
|
||||
imagelist.append(imageLabel);
|
||||
imagelist.append(imageLabel);
|
||||
dropDiv.hide();
|
||||
});
|
||||
|
||||
@@ -46,12 +53,29 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
function initializeFileReader() {
|
||||
jQuery.event.props.push( "dataTransfer" );
|
||||
|
||||
if(window.FileReader) {
|
||||
var drop = $('#imagedrop');
|
||||
|
||||
var draggingDocument = false;
|
||||
|
||||
$(composer.postContainer).on('dragenter dragover', function() {
|
||||
drop.show();
|
||||
if(window.FileReader) {
|
||||
var drop = $('.post-window .imagedrop'),
|
||||
textarea = $('.post-window textarea');
|
||||
|
||||
$(document).on('dragstart', function(e) {
|
||||
draggingDocument = true;
|
||||
}).on('dragend', function(e) {
|
||||
draggingDocument = false;
|
||||
});
|
||||
|
||||
textarea.on('dragenter', function(e) {
|
||||
if(draggingDocument)
|
||||
return;
|
||||
drop.css('top', textarea.position().top + 'px');
|
||||
drop.show();
|
||||
|
||||
drop.on('dragleave', function(ev) {
|
||||
drop.hide();
|
||||
drop.off('dragleave');
|
||||
});
|
||||
});
|
||||
|
||||
function cancel(e) {
|
||||
@@ -61,22 +85,22 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
drop.on('dragover', cancel);
|
||||
drop.on('dragenter', cancel);
|
||||
|
||||
|
||||
drop.on('drop', function(e) {
|
||||
e.preventDefault();
|
||||
var uuid = drop.parents('[data-uuid]').attr('data-uuid');
|
||||
var posts = composer.posts[uuid];
|
||||
|
||||
var dt = e.dataTransfer;
|
||||
var files = dt.files;
|
||||
|
||||
var uuid = drop.parents('[data-uuid]').attr('data-uuid'),
|
||||
posts = composer.posts[uuid],
|
||||
dt = e.dataTransfer,
|
||||
files = dt.files;
|
||||
|
||||
for (var i=0; i<files.length; i++) {
|
||||
loadFile(files[i]);
|
||||
}
|
||||
|
||||
if(!files.length)
|
||||
drop.hide();
|
||||
return false;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,21 +112,19 @@ define(['taskbar'], function(taskbar) {
|
||||
composer.postContainer.className = 'post-window row-fluid';
|
||||
composer.postContainer.innerHTML = '<div class="span5">' +
|
||||
'<input type="text" tabIndex="1" placeholder="Enter your topic title here..." />' +
|
||||
'<div class="btn-toolbar">' +
|
||||
'<div class="btn-group formatting-bar">' +
|
||||
'<div class="btn-toolbar formatting-bar">' +
|
||||
'<div class="btn-group">' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-bold"></i></span>' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-italic"></i></span>' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-list"></i></span>' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-link"></i></span>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div style="position:relative;">'+
|
||||
'<div id="imagedrop" class=""><div>Drag and Drop Images Here</div></div>'+
|
||||
'<textarea tabIndex="2"></textarea>' +
|
||||
'<div id="imagelist"></div>'+
|
||||
'</div>'+
|
||||
'<div class="btn-toolbar">' +
|
||||
'<div class="btn-group action-bar" style="float: right; margin-right: -8px">' +
|
||||
'<textarea tabIndex="2"></textarea>' +
|
||||
'<div class="imagelist"></div>'+
|
||||
'<div class="imagedrop"><div>Drag and Drop Images Here</div></div>'+
|
||||
'<div class="btn-toolbar action-bar">' +
|
||||
'<div class="btn-group" style="float: right; margin-right: -8px">' +
|
||||
'<button data-action="minimize" class="btn hidden-phone" tabIndex="4"><i class="icon-download-alt"></i> Minimize</button>' +
|
||||
'<button class="btn" data-action="discard" tabIndex="5"><i class="icon-remove"></i> Discard</button>' +
|
||||
'<button data-action="post" class="btn" tabIndex="3"><i class="icon-ok"></i> Submit</button>' +
|
||||
@@ -112,7 +134,8 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
document.body.insertBefore(composer.postContainer, taskbar);
|
||||
|
||||
initializeFileReader();
|
||||
if(config.imgurClientIDSet)
|
||||
initializeFileReader();
|
||||
|
||||
socket.on('api:composer.push', function(threadData) {
|
||||
if (!threadData.error) {
|
||||
@@ -152,12 +175,14 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
// Post Window events
|
||||
var jPostContainer = $(composer.postContainer),
|
||||
postContentEl = composer.postContainer.querySelector('textarea')
|
||||
postContentEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
jPostContainer.on('change', 'input, textarea', function() {
|
||||
var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid');
|
||||
if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value;
|
||||
else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value;
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.action-bar button', function() {
|
||||
var action = this.getAttribute('data-action'),
|
||||
uuid = $(this).parents('.post-window').attr('data-uuid');
|
||||
@@ -167,6 +192,7 @@ define(['taskbar'], function(taskbar) {
|
||||
case 'discard': composer.discard(uuid); break;
|
||||
}
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.formatting-bar span', function() {
|
||||
var iconClass = this.querySelector('i').className,
|
||||
cursorEnd = postContentEl.value.length,
|
||||
@@ -239,15 +265,22 @@ define(['taskbar'], function(taskbar) {
|
||||
});
|
||||
}
|
||||
|
||||
function createPostImages(images) {
|
||||
var imagelist = $(composer.postContainer).find('.imagelist');
|
||||
imagelist.empty();
|
||||
|
||||
if(images && images.length) {
|
||||
for(var i=0; i<images.length; ++i) {
|
||||
var imageLabel = createImageLabel(images[i], images);
|
||||
imagelist.append(imageLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composer.load = function(post_uuid) {
|
||||
var post_data = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
bodyEl = composer.postContainer.querySelector('textarea'),
|
||||
dropDiv = $(composer.postContainer).find('#imagedrop'),
|
||||
imagelist = $(composer.postContainer).find('#imagelist');
|
||||
|
||||
dropDiv.hide();
|
||||
imagelist.empty();
|
||||
bodyEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
composer.reposition(post_uuid);
|
||||
composer.active = post_uuid;
|
||||
@@ -264,7 +297,9 @@ define(['taskbar'], function(taskbar) {
|
||||
titleEl.value = post_data.title;
|
||||
titleEl.readOnly = false;
|
||||
}
|
||||
bodyEl.value = post_data.body
|
||||
bodyEl.value = post_data.body;
|
||||
|
||||
createPostImages(post_data.images);
|
||||
|
||||
// Direct user focus to the correct element
|
||||
if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) {
|
||||
@@ -292,7 +327,7 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
composer.post = function(post_uuid) {
|
||||
// Check title and post length
|
||||
var postData = composer.posts[post_uuid],
|
||||
var postData = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
bodyEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
@@ -347,7 +382,8 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
composer.discard = function(post_uuid) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$(composer.postContainer).find('#imagedrop').html('');
|
||||
$(composer.postContainer).find('.imagedrop').hide();
|
||||
$(composer.postContainer).find('.imagelist').empty();
|
||||
delete composer.posts[post_uuid];
|
||||
composer.minimize();
|
||||
taskbar.discard('composer', post_uuid);
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
if (!templates[tpl_url] || !template_data) return;
|
||||
|
||||
if(typeof global !== "undefined")
|
||||
template_data['relative_path'] = global.nconf.get('relative_path');
|
||||
template_data['relative_path'] = nconf.get('relative_path');
|
||||
else
|
||||
template_data['relative_path'] = RELATIVE_PATH;
|
||||
|
||||
|
||||
@@ -126,13 +126,38 @@
|
||||
return tags;
|
||||
},
|
||||
|
||||
refreshTitle: function() {
|
||||
var a = document.createElement('a');
|
||||
refreshTitle: function(url) {
|
||||
if (!url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = document.location;
|
||||
url = a.pathname.slice(1);
|
||||
}
|
||||
|
||||
a.href = document.location;
|
||||
socket.emit('api:meta.buildTitle', a.pathname.slice(1), function(title) {
|
||||
document.title = title;
|
||||
socket.emit('api:meta.buildTitle', url, function(title, numNotifications) {
|
||||
document.title = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title;
|
||||
if (numNotifications > 0) document.querySelector('.notifications a i').className = 'icon-circle active';
|
||||
});
|
||||
|
||||
jQuery.getJSON(RELATIVE_PATH + '/api/unread/total', function(data) {
|
||||
var badge = jQuery('#numUnreadBadge');
|
||||
badge.html(data.count > 20 ? '20+' : data.count);
|
||||
|
||||
if (data.count > 0) {
|
||||
badge
|
||||
.removeClass('badge-inverse')
|
||||
.addClass('badge-important')
|
||||
}
|
||||
else {
|
||||
badge
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse')
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isRelativeUrl: function(url) {
|
||||
var firstChar = url.slice(0, 1);
|
||||
return (firstChar === '.' || firstChar === '/');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,55 +1,48 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
|
||||
|
||||
<div class="account-username-box">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a>
|
||||
</span>
|
||||
<div class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span2" style="text-align: center; margin-bottom:20px;">
|
||||
<div class="span2 account-block" style="text-align: center; margin-bottom:20px;">
|
||||
<div class="account-picture-block">
|
||||
<img src="{picture}" class="user-profile-picture img-polaroid"/>
|
||||
</div>
|
||||
<div class="account-online-status">
|
||||
<span><i class="icon-circle-blank"></i> <span>offline</span></span>
|
||||
</div>
|
||||
<div class="{show_banned}">
|
||||
<span class="label label-important">banned</span>
|
||||
</div>
|
||||
<div id="user-actions">
|
||||
<a id="follow-btn" href="#" class="btn hide">Follow</a>
|
||||
<a id="unfollow-btn" href="#" class="btn hide">Unfollow</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="span4">
|
||||
<h4>profile</h4>
|
||||
<div class="inline-block">
|
||||
<div class="account-bio-block">
|
||||
<span class="account-bio-label">email</span><i class="icon-eye-close {emailClass}" title="Email hidden"></i>
|
||||
<span>{email}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">full name</span>
|
||||
<span>{fullname}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">website</span>
|
||||
<span><a href="{website}">{website}</a></span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">location</span>
|
||||
<span>{location}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">age</span>
|
||||
<span>{age}</span>
|
||||
<br/>
|
||||
@@ -58,22 +51,26 @@
|
||||
<span>{joindate}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">profile views</span>
|
||||
<span class="formatted-number">{profileviews}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">reputation</span>
|
||||
<span id='reputation'>{reputation}</span>
|
||||
<br/>
|
||||
|
||||
<span class="formatted-number">{reputation}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">posts</span>
|
||||
<span id='postcount'>{postcount}</span>
|
||||
<span class="formatted-number">{postcount}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">followers</span>
|
||||
<span>{followerCount}</span>
|
||||
<span class="formatted-number">{followerCount}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">following</span>
|
||||
<span>{followingCount}</span>
|
||||
<span class="formatted-number">{followingCount}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<hr/>
|
||||
<span class="account-bio-label">signature</span>
|
||||
<div class="post-signature">
|
||||
@@ -82,14 +79,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="span6 user-recent-posts">
|
||||
<h4>recent posts </h4>
|
||||
<!-- BEGIN posts -->
|
||||
<div class="topic-row img-polaroid clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
|
||||
<span>{posts.content}</span>
|
||||
<span class="pull-right">{posts.relativeTime} ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END posts -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,17 +60,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="account-username-box">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/edit">edit</a>
|
||||
</span>
|
||||
<div class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
@@ -171,4 +165,5 @@
|
||||
<input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" />
|
||||
<input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountedit.js"></script>
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div class="account-username-box">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/settings">settings</a>
|
||||
</span>
|
||||
<div class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
@@ -31,4 +25,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountsettings.js"></script>
|
||||
@@ -27,6 +27,8 @@
|
||||
</script>
|
||||
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
|
||||
<script type="text/javascript" src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
|
||||
<script src="{relative_path}/src/utils.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/style.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/admin.css" />
|
||||
</head>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
<input type="text" placeholder="Your Community Name" data-field="title" />
|
||||
<label>Site Description</label>
|
||||
<input type="text" class="input-xxlarge" placeholder="A short description about your community" data-field="description" />
|
||||
<label>Imgur Client ID</label>
|
||||
<input type="text" class="input-xxlarge" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<ul id="users-container" class="users">
|
||||
<!-- BEGIN users -->
|
||||
<div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}">
|
||||
<div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}" data-banned="{users.banned}">
|
||||
<a href="/users/{users.userslug}">
|
||||
<img src="{users.picture}" class="img-polaroid"/>
|
||||
</a>
|
||||
@@ -37,6 +37,9 @@
|
||||
<div>
|
||||
<a href="#" class="btn delete-btn btn-danger">Delete</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn ban-btn">Ban</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END users -->
|
||||
</ul>
|
||||
|
||||
@@ -10,13 +10,8 @@
|
||||
<div id="category_active_users"></div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
|
||||
<strong>There are no topics in this category.</strong><br />
|
||||
Why don't you try posting one?
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<button id="new_post" class="btn btn-primary btn-large {show_topic_button}">New Topic</button>
|
||||
|
||||
<div class="inline-block pull-right">
|
||||
@@ -27,7 +22,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="{show_sidebar}" />
|
||||
<hr/>
|
||||
|
||||
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
|
||||
<strong>There are no topics in this category.</strong><br />
|
||||
Why don't you try posting one?
|
||||
</div>
|
||||
|
||||
<div class="category row">
|
||||
<div class="{topic_row_size}">
|
||||
|
||||
@@ -25,13 +25,17 @@
|
||||
"users[^]*following": "following",
|
||||
"users[^]*followers": "followers",
|
||||
"users[^]*settings": "accountsettings",
|
||||
"users[^]*favourites": "favourites",
|
||||
"users/[^]*": "account",
|
||||
|
||||
"recent": "recent",
|
||||
"unread": "unread",
|
||||
"popular": "category",
|
||||
"active": "category",
|
||||
"search": "search"
|
||||
"search": "search",
|
||||
"reset/[^]*": "reset_code",
|
||||
"reset": "reset"
|
||||
|
||||
},
|
||||
"force_refresh": {
|
||||
"logout": true
|
||||
|
||||
34
public/templates/favourites.tpl
Normal file
34
public/templates/favourites.tpl
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="no-favourites-notice" class="alert alert-warning {show_nofavourites}">You don't have any favourites, favourite some posts to see them here!</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12 user-favourite-posts">
|
||||
<!-- BEGIN posts -->
|
||||
<div class="topic-row img-polaroid clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
|
||||
<span><strong>{posts.username}</strong> : </span>
|
||||
<span>{posts.content}</span>
|
||||
<div>
|
||||
<span class="pull-right">{posts.relativeTime} ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<!-- END posts -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/favourites.js"></script>
|
||||
@@ -1,17 +1,11 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div class="account-username-box">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/followers">followers</a>
|
||||
</span>
|
||||
<div class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -24,15 +18,14 @@
|
||||
<a href="/users/{followers.userslug}">{followers.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='reputation'>{followers.reputation}</span>
|
||||
<span class='formatted-number'>{followers.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='postcount'>{followers.postcount}</span>
|
||||
<span class='formatted-number'>{followers.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- END followers -->
|
||||
</div>
|
||||
<div id="no-followers-notice" class="alert alert-warning hide">This user doesn't have any followers :(</div>
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
|
||||
|
||||
<div class="account-username-box">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/following">following</a>
|
||||
</span>
|
||||
<div class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -26,11 +18,11 @@
|
||||
<a href="/users/{following.userslug}">{following.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='reputation'>{following.reputation}</span>
|
||||
<span class='formatted-number'>{following.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='postcount'>{following.postcount}</span>
|
||||
<span class='formatted-number'>{following.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
<a id="unfollow-btn" href="#" class="btn unfollow-btn" followingUid="{following.uid}" data-username="{following.username}">Unfollow</a>
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
<li>
|
||||
<a href="/recent">Recent</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/unread">Unread</a>
|
||||
<li class="nodebb-loggedin">
|
||||
<a href="/unread"><span id="numUnreadBadge" class="badge badge-inverse">0</span> Unread</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/users">Users</a>
|
||||
@@ -62,11 +62,12 @@
|
||||
<li><a href="#"><i class="icon-refresh icon-spin"></i> Loading Notifications</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<form id="search-form"
|
||||
class="form-search form-inline" action="" method="GET">
|
||||
<input type="text" name="query" class="input-medium search-query">
|
||||
<button type="submit" class="btn hide">Search</button>
|
||||
</form>
|
||||
<li>
|
||||
<form id="search-form" class="form-search form-inline" action="" method="GET">
|
||||
<input type="text" name="query" class="input-medium search-query">
|
||||
<button type="submit" class="btn hide">Search</button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -110,7 +110,6 @@
|
||||
contentEl.addEventListener('click', function(e) {
|
||||
if (e.target.hasAttribute('data-path')) {
|
||||
var href = 'install/' + e.target.getAttribute('data-path');
|
||||
console.log(href);
|
||||
if (!e.target.disabled) ajaxify.go(href);
|
||||
}
|
||||
}, false);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<button class="btn btn-primary" id="login" type="submit">Login</button> <a href="/reset">Forgot Password?</a>
|
||||
</form>
|
||||
|
||||
<span id="login-error-notify" class="label label-important hide">Invalid username/password</span><br/>
|
||||
<div id="login-error-notify" class="alert alert-danger hide">Invalid username/password</div>
|
||||
</div>
|
||||
|
||||
<div class="well span6 {alternate_logins:display}">
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<button id="ids_{main_posts.pid}_{main_posts.uid}" class="btn delete {main_posts.display_moderator_tools}" type="button" title="Delete"><i class="icon-trash"></i></button>
|
||||
<div class="btn-group">
|
||||
<button class="btn follow" type="button" title="Be notified of new replies in this topic"><i class="icon-eye-open"></i></button>
|
||||
<button id="favs_{main_posts.pid}_{main_posts.uid}" class="favourite btn" type="button">
|
||||
<button id="favs_{main_posts.pid}_{main_posts.uid}" class="favourite btn {main_posts.fav_button_class}" type="button">
|
||||
<span>Favourite</span>
|
||||
<span class="post_rep_{main_posts.pid}">{main_posts.post_rep} </span><i class="{main_posts.fav_star_class}"></i>
|
||||
</button>
|
||||
@@ -89,9 +89,10 @@
|
||||
<i class="icon-star"></i><span class="user_rep_{posts.uid} formatted-number">{posts.user_rep}</span>
|
||||
<div id="ids_{posts.pid}_{posts.uid}" class="chat hidden-phone" title="Chat"><i class="icon-comment"></i></div>
|
||||
</div>
|
||||
<span class="label label-important {posts.show_banned}">banned</span>
|
||||
</div>
|
||||
<div class="span11 span12-tablet">
|
||||
<div class="post-block">
|
||||
<div class="post-block speech-bubble">
|
||||
<div id="content_{posts.pid}" class="post-content">{posts.content}</div>
|
||||
<div id="images_{posts.pid}" class="post-images">
|
||||
<!-- BEGIN uploadedImages -->
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="mark-allread-btn" class="btn {show_markallread_button}">Mark All Read</button>
|
||||
<button id="mark-allread-btn" class="btn {show_markallread_button}">Mark All As Read</button>
|
||||
</div>
|
||||
|
||||
<div class="category row">
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
<a href="/users/{users.userslug}">{users.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='reputation'>{users.reputation}</span>
|
||||
<span class='formatted-number'>{users.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='postcount'>{users.postcount}</span>
|
||||
<span class='formatted-number'>{users.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ var RDB = require('./../redis.js'),
|
||||
|
||||
UserAdmin.deleteUser = function(uid, theirid, socket) {
|
||||
user.isAdministrator(uid, function(amIAdmin) {
|
||||
user.isAdministrator(theirid, function(areTheyAdmin){
|
||||
user.isAdministrator(theirid, function(areTheyAdmin) {
|
||||
if(amIAdmin && !areTheyAdmin) {
|
||||
user.delete(theirid, function(data) {
|
||||
|
||||
@@ -58,8 +58,39 @@ var RDB = require('./../redis.js'),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
UserAdmin.banUser = function(uid, theirid, socket) {
|
||||
user.isAdministrator(uid, function(amIAdmin) {
|
||||
user.isAdministrator(theirid, function(areTheyAdmin) {
|
||||
if(amIAdmin && !areTheyAdmin) {
|
||||
user.ban(theirid, function(err, result) {
|
||||
|
||||
socket.emit('event:alert', {
|
||||
title: 'User Banned',
|
||||
message: 'This user is banned!',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
UserAdmin.unbanUser = function(uid, theirid, socket) {
|
||||
user.isAdministrator(uid, function(amIAdmin) {
|
||||
if(amIAdmin) {
|
||||
user.unban(theirid, function(err, result) {
|
||||
socket.emit('event:alert', {
|
||||
title: 'User Unbanned',
|
||||
message: 'This user is unbanned!',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ var RDB = require('./redis.js'),
|
||||
utils = require('./../public/src/utils.js'),
|
||||
user = require('./user.js'),
|
||||
async = require('async'),
|
||||
topics = require('./topics.js');
|
||||
topics = require('./topics.js'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(Categories) {
|
||||
|
||||
Categories.getCategoryById = function(category_id, current_user, callback) {
|
||||
|
||||
|
||||
Categories.getCategoryData(category_id, function(err, categoryData) {
|
||||
if (err) return callback(err);
|
||||
|
||||
@@ -38,9 +39,9 @@ var RDB = require('./redis.js'),
|
||||
'category_id': category_id,
|
||||
'active_users': [],
|
||||
'topics' : [],
|
||||
'twitter-intent-url': 'https://twitter.com/intent/tweet?url=' + encodeURIComponent(global.nconf.get('url') + 'category/' + category_slug) + '&text=' + encodeURIComponent(category_name),
|
||||
'facebook-share-url': 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(global.nconf.get('url') + 'category/' + category_slug),
|
||||
'google-share-url': 'https://plus.google.com/share?url=' + encodeURIComponent(global.nconf.get('url') + 'category/' + category_slug)
|
||||
'twitter-intent-url': 'https://twitter.com/intent/tweet?url=' + encodeURIComponent(nconf.get('url') + 'category/' + category_slug) + '&text=' + encodeURIComponent(category_name),
|
||||
'facebook-share-url': 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(nconf.get('url') + 'category/' + category_slug),
|
||||
'google-share-url': 'https://plus.google.com/share?url=' + encodeURIComponent(nconf.get('url') + 'category/' + category_slug)
|
||||
};
|
||||
|
||||
function getTopics(next) {
|
||||
@@ -48,7 +49,7 @@ var RDB = require('./redis.js'),
|
||||
next(null, topicsData);
|
||||
}, category_id);
|
||||
}
|
||||
|
||||
|
||||
function getModerators(next) {
|
||||
Categories.getModerators(category_id, next);
|
||||
}
|
||||
@@ -65,7 +66,6 @@ var RDB = require('./redis.js'),
|
||||
categoryData.moderators = moderators;
|
||||
categoryData.show_sidebar = 'hidden';
|
||||
categoryData.no_topics_message = 'show';
|
||||
|
||||
callback(null, categoryData);
|
||||
});
|
||||
} else {
|
||||
@@ -156,22 +156,22 @@ var RDB = require('./redis.js'),
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback(allread);
|
||||
callback(allread);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Categories.markAsRead = function(cid, uid) {
|
||||
RDB.sadd('cid:' + cid + ':read_by_uid', uid);
|
||||
RDB.sadd('cid:' + cid + ':read_by_uid', uid);
|
||||
}
|
||||
|
||||
Categories.hasReadCategories = function(cids, uid, callback) {
|
||||
var batch = RDB.multi();
|
||||
|
||||
for (var i=0, ii=cids.length; i<ii; i++) {
|
||||
batch.sismember('cid:' + cids[i] + ':read_by_uid', uid);
|
||||
batch.sismember('cid:' + cids[i] + ':read_by_uid', uid);
|
||||
}
|
||||
|
||||
|
||||
batch.exec(function(err, hasRead) {
|
||||
callback(hasRead);
|
||||
});
|
||||
@@ -180,15 +180,16 @@ var RDB = require('./redis.js'),
|
||||
Categories.hasReadCategory = function(cid, uid, callback) {
|
||||
RDB.sismember('cid:' + cid + ':read_by_uid', uid, function(err, hasRead) {
|
||||
RDB.handle(err);
|
||||
|
||||
|
||||
callback(hasRead);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Categories.getRecentReplies = function(cid, count, callback) {
|
||||
RDB.zrevrange('categories:recent_posts:cid:' + cid, 0, (count<10)?10:count, function(err, pids) {
|
||||
|
||||
if(err) {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
@@ -198,11 +199,11 @@ var RDB = require('./redis.js'),
|
||||
return;
|
||||
}
|
||||
|
||||
posts.getPostSummaryByPids(pids, function(posts) {
|
||||
if(posts.length > count) {
|
||||
posts = posts.slice(0, count);
|
||||
posts.getPostSummaryByPids(pids, function(err, postData) {
|
||||
if(postData.length > count) {
|
||||
postData = postData.slice(0, count);
|
||||
}
|
||||
callback(posts);
|
||||
callback(postData);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -213,8 +214,8 @@ var RDB = require('./redis.js'),
|
||||
|
||||
function movePost(pid, callback) {
|
||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
||||
RDB.zrem('categories:recent_posts:cid:' + oldCid, pid);
|
||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||
RDB.zrem('categories:recent_posts:cid:' + oldCid, pid);
|
||||
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -222,12 +223,12 @@ var RDB = require('./redis.js'),
|
||||
if(!err) {
|
||||
callback(null, 1)
|
||||
} else {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
@@ -239,20 +240,20 @@ var RDB = require('./redis.js'),
|
||||
else callback(new Error('No category found!'));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Categories.getCategoryField = function(cid, field, callback) {
|
||||
RDB.hget('category:' + cid, field, callback);
|
||||
}
|
||||
|
||||
Categories.getCategoryFields = function(cid, fields, callback) {
|
||||
RDB.hmgetObject('category:' + cid, fields, function(err, data) {
|
||||
if(err === null)
|
||||
if(err === null)
|
||||
callback(data);
|
||||
else
|
||||
console.log(err);
|
||||
});
|
||||
winston.err(err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Categories.setCategoryField = function(cid, field, value) {
|
||||
RDB.hset('category:' + cid, field, value);
|
||||
}
|
||||
@@ -266,37 +267,37 @@ var RDB = require('./redis.js'),
|
||||
callback({'categories' : []});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var categories = [];
|
||||
|
||||
function getCategory(cid, callback) {
|
||||
Categories.getCategoryData(cid, function(err, categoryData) {
|
||||
|
||||
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
Categories.hasReadCategory(cid, current_user, function(hasRead) {
|
||||
categoryData['badgeclass'] = (parseInt(categoryData.topic_count,10) === 0 || (hasRead && current_user != 0)) ? '' : 'badge-important';
|
||||
categoryData['badgeclass'] = (parseInt(categoryData.topic_count, 10) === 0 || (hasRead && current_user != 0)) ? '' : 'badge-important';
|
||||
|
||||
categories.push(categoryData);
|
||||
callback(null);
|
||||
}) ;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.eachSeries(cids, getCategory, function(err) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
callback({'categories': categories});
|
||||
|
||||
callback({'categories': categories});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}(exports));
|
||||
|
||||
|
||||
@@ -13,33 +13,31 @@ var RDB = require('./redis.js'),
|
||||
type: 'error',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
socket.emit('api:posts.favourite', {
|
||||
status: 'error',
|
||||
pid: pid
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
posts.getPostField(pid, 'uid', function(uid_of_poster) {
|
||||
posts.getPostFields(pid, ['uid', 'timestamp'], function(postData) {
|
||||
|
||||
Favourites.hasFavourited(pid, uid, function(hasFavourited) {
|
||||
if (hasFavourited == false) {
|
||||
RDB.sadd('pid:' + pid + ':users_favourited', uid);
|
||||
RDB.zadd('uid:' + uid + ':favourites', postData.timestamp, pid);
|
||||
|
||||
RDB.hincrby('post:' + pid, 'reputation', 1);
|
||||
|
||||
if (uid !== uid_of_poster) {
|
||||
user.incrementUserFieldBy(uid_of_poster, 'reputation', 1, function(err, newreputation) {
|
||||
RDB.zadd('users:reputation', newreputation, uid_of_poster);
|
||||
if (uid !== postData.uid) {
|
||||
user.incrementUserFieldBy(postData.uid, 'reputation', 1, function(err, newreputation) {
|
||||
RDB.zadd('users:reputation', newreputation, postData.uid);
|
||||
});
|
||||
}
|
||||
|
||||
if (room_id) {
|
||||
io.sockets.in(room_id).emit('event:rep_up', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
|
||||
io.sockets.in(room_id).emit('event:rep_up', {uid: uid !== postData.uid ? postData.uid : 0, pid: pid});
|
||||
}
|
||||
|
||||
socket.emit('api:posts.favourite', {
|
||||
status: 'ok'
|
||||
status: 'ok',
|
||||
pid: pid
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -62,10 +60,12 @@ var RDB = require('./redis.js'),
|
||||
|
||||
Favourites.hasFavourited(pid, uid, function(hasFavourited) {
|
||||
if (hasFavourited == true) {
|
||||
|
||||
|
||||
RDB.srem('pid:' + pid + ':users_favourited', uid);
|
||||
RDB.zrem('uid:' + uid + ':favourites', pid);
|
||||
|
||||
RDB.hincrby('post:' + pid, 'reputation', -1);
|
||||
|
||||
|
||||
if (uid !== uid_of_poster) {
|
||||
user.incrementUserFieldBy(uid_of_poster, 'reputation', -1, function(err, newreputation) {
|
||||
RDB.zadd('users:reputation', newreputation, uid_of_poster);
|
||||
@@ -75,6 +75,11 @@ var RDB = require('./redis.js'),
|
||||
if (room_id) {
|
||||
io.sockets.in(room_id).emit('event:rep_down', {uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid});
|
||||
}
|
||||
|
||||
socket.emit('api:posts.unfavourite', {
|
||||
status: 'ok',
|
||||
pid: pid
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -83,7 +88,7 @@ var RDB = require('./redis.js'),
|
||||
Favourites.hasFavourited = function(pid, uid, callback) {
|
||||
RDB.sismember('pid:' + pid + ':users_favourited', uid, function(err, hasFavourited) {
|
||||
RDB.handle(err);
|
||||
|
||||
|
||||
callback(hasFavourited);
|
||||
});
|
||||
}
|
||||
@@ -95,10 +100,10 @@ var RDB = require('./redis.js'),
|
||||
for (var i=0, ii=pids.length; i<ii; i++) {
|
||||
(function(post_id) {
|
||||
Favourites.hasFavourited(post_id, uid, function(hasFavourited) {
|
||||
|
||||
|
||||
data[post_id] = hasFavourited;
|
||||
loaded ++;
|
||||
if (loaded === pids.length)
|
||||
if (loaded === pids.length)
|
||||
callback(data);
|
||||
});
|
||||
}(pids[i]));
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
posts = require('./posts.js'),
|
||||
topics = require('./topics.js'),
|
||||
fs = require('fs'),
|
||||
rss = require('node-rss');
|
||||
rss = require('node-rss'),
|
||||
winston = require('winston');
|
||||
|
||||
function saveFeed(location, feed) {
|
||||
fs.writeFile(location, rss.getFeedXML(feed), function (err) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -32,7 +33,7 @@
|
||||
var cache_time_in_seconds = 60;
|
||||
|
||||
topics.getTopicWithPosts(tid, 0, function(err, topicData) {
|
||||
if (err) console.log('Error: Problem saving topic RSS feed', err);
|
||||
if (err) winston.error('Problem saving topic RSS feed', err.stack);
|
||||
|
||||
var location = '/topic/' + topicData.slug,
|
||||
xml_url = '/topic/' + tid + '.rss';
|
||||
@@ -69,7 +70,7 @@
|
||||
Feed.updateCategory = function(cid) {
|
||||
categories.getCategoryById(cid, 0, function(err, categoryData) {
|
||||
if (err) {
|
||||
console.log('Error: Could not update RSS feed for category ' + cid);
|
||||
winston.error('Could not update RSS feed for category ' + cid, err.stack);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ var request = require('request');
|
||||
}
|
||||
};
|
||||
|
||||
var post = request.post(options, function(err, req, body){
|
||||
var post = request.post(options, function(err, req, body) {
|
||||
try{
|
||||
callback(err, JSON.parse(body));
|
||||
} catch(e) {
|
||||
|
||||
@@ -79,16 +79,16 @@ var async = require('async'),
|
||||
port: config.port
|
||||
},
|
||||
api_url: protocol + '//' + host + (config.use_port ? ':' + config.port : '') + relative_path + '/api/',
|
||||
relative_path: relative_path
|
||||
relative_path: relative_path
|
||||
};
|
||||
|
||||
server_conf.base_url = protocol + '//' + host;
|
||||
server_conf.relative_path = relative_path;
|
||||
server_conf.imgurClientID = '';
|
||||
|
||||
meta.config.set('postDelay', 10000);
|
||||
meta.config.set('minimumPostLength', 8);
|
||||
meta.config.set('minimumTitleLength', 3);
|
||||
|
||||
meta.configs.set('postDelay', 10000);
|
||||
meta.configs.set('minimumPostLength', 8);
|
||||
meta.configs.set('minimumTitleLength', 3);
|
||||
meta.configs.set('imgurClientID', '');
|
||||
|
||||
install.save(server_conf, client_conf, callback);
|
||||
});
|
||||
|
||||
23
src/login.js
23
src/login.js
@@ -2,7 +2,8 @@
|
||||
var user = require('./user.js'),
|
||||
bcrypt = require('bcrypt'),
|
||||
RDB = require('./redis.js'),
|
||||
path = require('path');
|
||||
path = require('path'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(Login){
|
||||
|
||||
@@ -23,18 +24,28 @@ var user = require('./user.js'),
|
||||
message: 'invalid-user'
|
||||
});
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'password', function(user_password) {
|
||||
bcrypt.compare(password, user_password, function(err, res) {
|
||||
|
||||
user.getUserFields(uid, ['password', 'banned'], function(err, userData) {
|
||||
if(err)
|
||||
return next(err);
|
||||
|
||||
if(userData.banned && userData.banned === '1') {
|
||||
return next({
|
||||
status: "error",
|
||||
message: "user-banned"
|
||||
});
|
||||
}
|
||||
|
||||
bcrypt.compare(password, userData.password, function(err, res) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
next({
|
||||
status: "error",
|
||||
message: 'bcrypt compare error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (res) {
|
||||
next({
|
||||
status: "ok",
|
||||
|
||||
76
src/messaging.js
Normal file
76
src/messaging.js
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
var RDB = require('./redis'),
|
||||
async = require('async');
|
||||
|
||||
|
||||
(function(Messaging) {
|
||||
|
||||
function sortUids(fromuid, touid) {
|
||||
var uids = [fromuid, touid];
|
||||
uids.sort();
|
||||
return uids;
|
||||
}
|
||||
|
||||
Messaging.addMessage = function(fromuid, touid, content, callback) {
|
||||
var uids = sortUids(fromuid, touid);
|
||||
|
||||
RDB.incr('global:next_message_id', function(err, mid) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
var message = {
|
||||
content: content,
|
||||
timestamp: Date.now(),
|
||||
fromuid: fromuid,
|
||||
touid: touid
|
||||
};
|
||||
|
||||
RDB.hmset('message:' + mid, message);
|
||||
RDB.rpush('messages:' + uids[0] + ':' + uids[1], mid);
|
||||
|
||||
callback(null, message);
|
||||
});
|
||||
}
|
||||
|
||||
Messaging.getMessages = function(fromuid, touid, callback) {
|
||||
var uids = sortUids(fromuid, touid);
|
||||
|
||||
RDB.lrange('messages:' + uids[0] + ':' + uids[1], 0, -1, function(err, mids) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
if(!mids || !mids.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
|
||||
user.getUserField(touid, 'username', function(err, tousername) {
|
||||
|
||||
var messages = [];
|
||||
|
||||
function getMessage(mid, next) {
|
||||
RDB.hgetall('message:' + mid, function(err, message) {
|
||||
if(err)
|
||||
return next(err);
|
||||
|
||||
if(message.fromuid === fromuid)
|
||||
message.content = 'You : ' + message.content;
|
||||
else
|
||||
message.content = tousername + ' : ' + message.content;
|
||||
|
||||
messages.push(message);
|
||||
next(null);
|
||||
});
|
||||
}
|
||||
|
||||
async.eachSeries(mids, getMessage, function(err) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
callback(null, messages);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}(exports));
|
||||
26
src/meta.js
26
src/meta.js
@@ -5,7 +5,14 @@ var utils = require('./../public/src/utils.js'),
|
||||
fs = require('fs');
|
||||
|
||||
(function(Meta) {
|
||||
Meta.config = {
|
||||
|
||||
Meta.configs = {
|
||||
init: function(callback) {
|
||||
Meta.configs.get(function(config) {
|
||||
Meta.config = config;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
get: function(callback) {
|
||||
RDB.hgetall('config', function(err, config) {
|
||||
if (!err) {
|
||||
@@ -48,9 +55,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
if (exists) {
|
||||
fs.readFile(themeConfPath, function(err, conf) {
|
||||
conf = JSON.parse(conf);
|
||||
conf.src = global.nconf.get('url') + 'themes/' + themeDir + '/' + conf.src;
|
||||
if (conf.screenshot) conf.screenshot = global.nconf.get('url') + 'themes/' + themeDir + '/' + conf.screenshot;
|
||||
else conf.screenshot = global.nconf.get('url') + 'images/themes/default.png';
|
||||
conf.src = nconf.get('url') + 'themes/' + themeDir + '/' + conf.src;
|
||||
if (conf.screenshot) conf.screenshot = nconf.get('url') + 'themes/' + themeDir + '/' + conf.screenshot;
|
||||
else conf.screenshot = nconf.get('url') + 'images/themes/default.png';
|
||||
themeArr.push(conf);
|
||||
next();
|
||||
});
|
||||
@@ -80,10 +87,10 @@ var utils = require('./../public/src/utils.js'),
|
||||
}, function(err, values) {
|
||||
var title;
|
||||
|
||||
if (err) title = global.config.title || 'NodeBB';
|
||||
else title = (values.notifCount > 0 ? '(' + values.notifCount + ') ' : '') + (values.title ? values.title + ' | ' : '') + (global.config.title || 'NodeBB');
|
||||
if (err) title = Meta.config.title || 'NodeBB';
|
||||
else title = (values.title ? values.title + ' | ' : '') + (Meta.config.title || 'NodeBB');
|
||||
|
||||
callback(null, title);
|
||||
callback(null, title, values.notifCount);
|
||||
});
|
||||
},
|
||||
parseFragment: function(urlFragment, callback) {
|
||||
@@ -110,4 +117,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
} else callback(null);
|
||||
}
|
||||
}
|
||||
}(exports));
|
||||
|
||||
|
||||
}(exports));
|
||||
|
||||
|
||||
114
src/plugins.js
114
src/plugins.js
@@ -2,12 +2,13 @@ var fs = require('fs'),
|
||||
path = require('path'),
|
||||
RDB = require('./redis.js'),
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
plugins = {
|
||||
libraries: [],
|
||||
loadedHooks: {},
|
||||
init: function() {
|
||||
if (this.initialized) return;
|
||||
if (global.env === 'development') console.log('Info: [plugins] Initializing plugins system');
|
||||
if (global.env === 'development') winston.info('[plugins] Initializing plugins system');
|
||||
|
||||
var _self = this;
|
||||
|
||||
@@ -19,40 +20,44 @@ var fs = require('fs'),
|
||||
function(plugins, next) {
|
||||
async.each(plugins, function(plugin) {
|
||||
// TODO: Update this check to also check node_modules
|
||||
var pluginPath = path.join(__dirname, '../plugins/', plugin);
|
||||
fs.exists(pluginPath, function(exists) {
|
||||
if (exists) {
|
||||
fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) {
|
||||
if (err) return next(err);
|
||||
|
||||
var pluginData = JSON.parse(data);
|
||||
_self.libraries[pluginData.id] = require(path.join(pluginPath, pluginData.library));
|
||||
if (pluginData.hooks) {
|
||||
for(var x=0,numHooks=pluginData.hooks.length;x<numHooks;x++) {
|
||||
_self.registerHook(pluginData.id, pluginData.hooks[x]);
|
||||
}
|
||||
}
|
||||
if (global.env === 'development') console.log('Info: [plugins] Loaded plugin: ' + pluginData.id);
|
||||
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
if (global.env === 'development') console.log('Info: [plugins] Plugin \'' + plugin + '\' not found');
|
||||
next(); // Ignore this plugin silently
|
||||
}
|
||||
})
|
||||
var pluginPath = path.join(__dirname, '../plugins/', plugin),
|
||||
modulePath = path.join(__dirname, '../node_modules/', plugin);
|
||||
if (fs.existsSync(pluginPath)) _self.loadPlugin(pluginPath, next);
|
||||
else if (fs.existsSync(modulePath)) _self.loadPlugin(modulePath, next);
|
||||
else {
|
||||
if (global.env === 'development') winston.info('[plugins] Plugin \'' + plugin + '\' not found');
|
||||
next(); // Ignore this plugin silently
|
||||
}
|
||||
}, next);
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
if (global.env === 'development') console.log('Info: [plugins] NodeBB encountered a problem while loading plugins', err.message);
|
||||
if (global.env === 'development') winston.info('[plugins] NodeBB encountered a problem while loading plugins', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (global.env === 'development') console.log('Info: [plugins] Plugins OK');
|
||||
if (global.env === 'development') winston.info('[plugins] Plugins OK');
|
||||
});
|
||||
},
|
||||
initialized: false,
|
||||
loadPlugin: function(pluginPath, callback) {
|
||||
var _self = this;
|
||||
|
||||
fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) {
|
||||
if (err) return callback(err);
|
||||
|
||||
var pluginData = JSON.parse(data);
|
||||
_self.libraries[pluginData.id] = require(path.join(pluginPath, pluginData.library));
|
||||
if (pluginData.hooks) {
|
||||
for(var x=0,numHooks=pluginData.hooks.length;x<numHooks;x++) {
|
||||
_self.registerHook(pluginData.id, pluginData.hooks[x]);
|
||||
}
|
||||
}
|
||||
if (global.env === 'development') winston.info('[plugins] Loaded plugin: ' + pluginData.id);
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
registerHook: function(id, data) {
|
||||
/*
|
||||
`data` is an object consisting of (* is required):
|
||||
@@ -65,8 +70,8 @@ var fs = require('fs'),
|
||||
|
||||
if (data.hook && data.method) {
|
||||
_self.loadedHooks[data.hook] = _self.loadedHooks[data.hook] || [];
|
||||
_self.loadedHooks[data.hook].push([id, data.method]);
|
||||
if (global.env === 'development') console.log('Info: [plugins] Hook registered: ' + data.hook + ' will call ' + id);
|
||||
_self.loadedHooks[data.hook].push([id, data.method, !!data.callbacked]);
|
||||
if (global.env === 'development') winston.info('[plugins] Hook registered: ' + data.hook + ' will call ' + id);
|
||||
} else return;
|
||||
},
|
||||
fireHook: function(hook, args, callback) {
|
||||
@@ -75,7 +80,7 @@ var fs = require('fs'),
|
||||
hookList = this.loadedHooks[hook];
|
||||
|
||||
if (hookList && Array.isArray(hookList)) {
|
||||
if (global.env === 'development') console.log('Info: [plugins] Firing hook: \'' + hook + '\'');
|
||||
if (global.env === 'development') winston.info('[plugins] Firing hook: \'' + hook + '\'');
|
||||
var hookType = hook.split(':')[0];
|
||||
switch(hookType) {
|
||||
case 'filter':
|
||||
@@ -83,7 +88,7 @@ var fs = require('fs'),
|
||||
var returnVal = (Array.isArray(args) ? args[0] : args);
|
||||
|
||||
async.each(hookList, function(hookObj, next) {
|
||||
if (hookObj.callbacked) {
|
||||
if (hookObj[2]) {
|
||||
_self.libraries[hookObj[0]][hookObj[1]](returnVal, function(err, afterVal) {
|
||||
returnVal = afterVal;
|
||||
next(err);
|
||||
@@ -94,7 +99,9 @@ var fs = require('fs'),
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
if (global.env === 'development') console.log('Info: [plugins] Problem executing hook: ' + hook);
|
||||
if (global.env === 'development') {
|
||||
winston.info('[plugins] Problem executing hook: ' + hook);
|
||||
}
|
||||
}
|
||||
|
||||
callback(returnVal);
|
||||
@@ -109,7 +116,7 @@ var fs = require('fs'),
|
||||
) {
|
||||
_self.libraries[hookObj[0]][hookObj[1]].apply(_self.libraries[hookObj[0]], args);
|
||||
} else {
|
||||
if (global.env === 'development') console.log('Info: [plugins] Expected method \'' + hookObj[1] + '\' in plugin \'' + hookObj[0] + '\' not found, skipping.');
|
||||
if (global.env === 'development') winston.info('[plugins] Expected method \'' + hookObj[1] + '\' in plugin \'' + hookObj[0] + '\' not found, skipping.');
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -129,13 +136,13 @@ var fs = require('fs'),
|
||||
toggleActive: function(id, callback) {
|
||||
this.isActive(id, function(err, active) {
|
||||
if (err) {
|
||||
if (global.env === 'development') console.log('Info: [plugins] Could not toggle active state on plugin \'' + id + '\'');
|
||||
if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
|
||||
return;
|
||||
}
|
||||
|
||||
RDB[(active ? 'srem' : 'sadd')]('plugins:active', id, function(err, success) {
|
||||
if (err) {
|
||||
if (global.env === 'development') console.log('Info: [plugins] Could not toggle active state on plugin \'' + id + '\'');
|
||||
if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -149,26 +156,49 @@ var fs = require('fs'),
|
||||
showInstalled: function(callback) {
|
||||
// TODO: Also check /node_modules
|
||||
var _self = this;
|
||||
moduleBasePath = path.join(__dirname, '../plugins');
|
||||
localPluginPath = path.join(__dirname, '../plugins'),
|
||||
npmPluginPath = path.join(__dirname, '../node_modules');
|
||||
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
fs.readdir(moduleBasePath, next);
|
||||
async.parallel([
|
||||
function(next) {
|
||||
fs.readdir(localPluginPath, next);
|
||||
},
|
||||
function(next) {
|
||||
fs.readdir(npmPluginPath, next);
|
||||
}
|
||||
], function(err, dirs) {
|
||||
if (err) return next(err);
|
||||
|
||||
dirs[0] = dirs[0].map(function(file) {
|
||||
return path.join(localPluginPath, file);
|
||||
}).filter(function(file) {
|
||||
var stats = fs.statSync(file);
|
||||
if (stats.isDirectory()) return true;
|
||||
else return false;
|
||||
});
|
||||
|
||||
dirs[1] = dirs[1].map(function(file) {
|
||||
return path.join(npmPluginPath, file);
|
||||
}).filter(function(file) {
|
||||
var stats = fs.statSync(file);
|
||||
if (stats.isDirectory() && file.substr(npmPluginPath.length+1, 14) === 'nodebb-plugin-') return true;
|
||||
else return false;
|
||||
});
|
||||
|
||||
next(err, dirs[0].concat(dirs[1]));
|
||||
});
|
||||
},
|
||||
function(files, next) {
|
||||
var plugins = [];
|
||||
|
||||
async.each(files, function(file, next) {
|
||||
var modulePath = path.join(moduleBasePath, file),
|
||||
configPath;
|
||||
var configPath;
|
||||
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
fs.stat(path.join(moduleBasePath, file), next);
|
||||
},
|
||||
function(stats, next) {
|
||||
if (stats.isDirectory()) fs.readFile(path.join(modulePath, 'plugin.json'), next);
|
||||
else next(new Error('not-a-directory'));
|
||||
fs.readFile(path.join(file, 'plugin.json'), next);
|
||||
},
|
||||
function(configJSON, next) {
|
||||
var config = JSON.parse(configJSON);
|
||||
|
||||
@@ -4,12 +4,14 @@ var RDB = require('./redis.js'),
|
||||
threadTools = require('./threadTools.js'),
|
||||
user = require('./user.js'),
|
||||
async = require('async'),
|
||||
|
||||
|
||||
utils = require('../public/src/utils'),
|
||||
plugins = require('./plugins'),
|
||||
reds = require('reds'),
|
||||
postSearch = reds.createSearch('nodebbpostsearch'),
|
||||
topicSearch = reds.createSearch('nodebbtopicsearch');
|
||||
topicSearch = reds.createSearch('nodebbtopicsearch'),
|
||||
winston = require('winston'),
|
||||
meta = require('./meta.js');
|
||||
|
||||
(function(PostTools) {
|
||||
PostTools.isMain = function(pid, tid, callback) {
|
||||
@@ -20,8 +22,8 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
PostTools.privileges = function(pid, uid, callback) {
|
||||
//todo: break early if one condition is true
|
||||
|
||||
//todo: break early if one condition is true
|
||||
|
||||
function getThreadPrivileges(next) {
|
||||
posts.getPostField(pid, 'tid', function(tid) {
|
||||
threadTools.privileges(tid, uid, function(privileges) {
|
||||
@@ -39,8 +41,9 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
function hasEnoughRep(next) {
|
||||
user.getUserField(uid, 'reputation', function(reputation) {
|
||||
next(null, reputation >= global.config['privileges:manage_content']);
|
||||
user.getUserField(uid, 'reputation', function(err, reputation) {
|
||||
if (err) return next(null, false);
|
||||
next(null, reputation >= meta.config['privileges:manage_content']);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,7 +97,7 @@ var RDB = require('./redis.js'),
|
||||
PostTools.delete = function(uid, pid) {
|
||||
var success = function() {
|
||||
posts.setPostField(pid, 'deleted', 1);
|
||||
|
||||
|
||||
postSearch.remove(pid);
|
||||
|
||||
posts.getPostFields(pid, ['tid', 'uid'], function(postData) {
|
||||
@@ -102,7 +105,7 @@ var RDB = require('./redis.js'),
|
||||
user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) {
|
||||
RDB.zadd('users:postcount', postcount, postData.uid);
|
||||
});
|
||||
|
||||
|
||||
io.sockets.in('topic_' + postData.tid).emit('event:post_deleted', {
|
||||
pid: pid
|
||||
});
|
||||
@@ -111,12 +114,12 @@ var RDB = require('./redis.js'),
|
||||
threadTools.get_latest_undeleted_pid(postData.tid, function(err, pid) {
|
||||
if (err && err.message === 'no-undeleted-pids-found') {
|
||||
threadTools.delete(postData.tid, -1, function(err) {
|
||||
if (err) console.log('Error: Could not delete topic (tid: ' + postData.tid + ')');
|
||||
if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack);
|
||||
});
|
||||
} else {
|
||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
||||
topics.updateTimestamp(postData.tid, timestamp);
|
||||
});
|
||||
topics.updateTimestamp(postData.tid, timestamp);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -140,11 +143,11 @@ var RDB = require('./redis.js'),
|
||||
io.sockets.in('topic_' + postData.tid).emit('event:post_restored', {
|
||||
pid: pid
|
||||
});
|
||||
|
||||
|
||||
threadTools.get_latest_undeleted_pid(postData.tid, function(err, pid) {
|
||||
posts.getPostField(pid, 'timestamp', function(timestamp) {
|
||||
topics.updateTimestamp(postData.tid, timestamp);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
postSearch.index(postData.content, pid);
|
||||
@@ -168,18 +171,18 @@ var RDB = require('./redis.js'),
|
||||
|
||||
if (md && md.length > 0) {
|
||||
var parsedContentDOM = cheerio.load(marked(md));
|
||||
var domain = global.nconf.get('url');
|
||||
|
||||
var domain = nconf.get('url');
|
||||
|
||||
parsedContentDOM('a').each(function() {
|
||||
this.attr('rel', 'nofollow');
|
||||
var href = this.attr('href');
|
||||
|
||||
if (href && !href.match(domain)) {
|
||||
if (href && !href.match(domain) && !utils.isRelativeUrl(href)) {
|
||||
this.attr('href', domain + 'outgoing?url=' + encodeURIComponent(href));
|
||||
if (!isSignature) this.append(' <i class="icon-external-link"></i>');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
html = parsedContentDOM.html();
|
||||
} else {
|
||||
|
||||
210
src/posts.js
210
src/posts.js
@@ -10,18 +10,20 @@ var RDB = require('./redis.js'),
|
||||
async = require('async'),
|
||||
plugins = require('./plugins'),
|
||||
reds = require('reds'),
|
||||
postSearch = reds.createSearch('nodebbpostsearch'),
|
||||
nconf = require('nconf'),
|
||||
postSearch = reds.createSearch('nodebbpostsearch');
|
||||
meta = require('./meta.js'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(Posts) {
|
||||
|
||||
Posts.getPostsByTid = function(tid, start, end, callback) {
|
||||
RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
|
||||
|
||||
|
||||
RDB.handle(err);
|
||||
|
||||
if (pids.length) {
|
||||
Posts.getPostsByPids(pids, function(posts) {
|
||||
Posts.getPostsByPids(pids, function(err, posts) {
|
||||
callback(posts);
|
||||
});
|
||||
} else {
|
||||
@@ -29,21 +31,26 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Posts.addUserInfoToPost = function(post, callback) {
|
||||
user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature'], function(userData) {
|
||||
user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(err, userData) {
|
||||
if(err)
|
||||
return callback();
|
||||
|
||||
post.username = userData.username || 'anonymous';
|
||||
post.userslug = userData.userslug || '';
|
||||
post.user_rep = userData.reputation || 0;
|
||||
post.user_postcount = userData.postcount || 0;
|
||||
post.picture = userData.picture || require('gravatar').url('', {}, https=global.nconf.get('https'));
|
||||
post.user_banned = userData.banned || '0';
|
||||
post.picture = userData.picture || require('gravatar').url('', {}, https=nconf.get('https'));
|
||||
post.signature = postTools.markdownToHTML(userData.signature, true);
|
||||
|
||||
if(post.editor !== '') {
|
||||
user.getUserFields(post.editor, ['username', 'userslug'], function(editorData) {
|
||||
user.getUserFields(post.editor, ['username', 'userslug'], function(err, editorData) {
|
||||
if(err)
|
||||
return callback();
|
||||
post.editorname = editorData.username;
|
||||
post.editorslug = editorData.userslug;
|
||||
post.editorslug = editorData.userslug;
|
||||
callback();
|
||||
});
|
||||
} else {
|
||||
@@ -53,9 +60,9 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
Posts.getPostSummaryByPids = function(pids, callback) {
|
||||
|
||||
var returnData = [];
|
||||
|
||||
|
||||
var posts = [];
|
||||
|
||||
function getPostSummary(pid, callback) {
|
||||
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) {
|
||||
if(postData.deleted === '1') {
|
||||
@@ -63,33 +70,46 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
Posts.addUserInfoToPost(postData, function() {
|
||||
topics.getTopicField(postData.tid, 'slug', function(err, topicSlug) {
|
||||
topics.getTopicFields(postData.tid, ['slug', 'deleted'], function(err, topicData) {
|
||||
if(err)
|
||||
return callback(err);
|
||||
|
||||
if(topicData.deleted === '1')
|
||||
return callback(null);
|
||||
|
||||
if(postData.content)
|
||||
postData.content = utils.strip_tags(postTools.markdownToHTML(postData.content));
|
||||
|
||||
postData.topicSlug = topicSlug;
|
||||
returnData.push(postData);
|
||||
postData.relativeTime = utils.relativeTime(postData.timestamp);
|
||||
postData.topicSlug = topicData.slug;
|
||||
posts.push(postData);
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.eachSeries(pids, getPostSummary, function(err) {
|
||||
if(!err) {
|
||||
callback(returnData);
|
||||
callback(null, posts);
|
||||
} else {
|
||||
console.log(err);
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Posts.filterBannedPosts = function(posts) {
|
||||
return posts.filter(function(post) {
|
||||
return post.user_banned === '0';
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getPostData = function(pid, callback) {
|
||||
RDB.hgetall('post:' + pid, function(err, data) {
|
||||
if(err === null) {
|
||||
callback(data);
|
||||
plugins.fireHook('filter:post.get', data, function(data) {
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
else
|
||||
console.log(err);
|
||||
@@ -104,7 +124,7 @@ var RDB = require('./redis.js'),
|
||||
else {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getPostField = function(pid, field, callback) {
|
||||
@@ -122,20 +142,25 @@ var RDB = require('./redis.js'),
|
||||
|
||||
|
||||
Posts.getPostsByPids = function(pids, callback) {
|
||||
var posts = [];
|
||||
var posts = [];
|
||||
|
||||
function iterator(pid, callback) {
|
||||
Posts.getPostData(pid, function(postData) {
|
||||
if(postData) {
|
||||
postData.relativeTime = utils.relativeTime(postData.timestamp);
|
||||
postData.relativeTime = utils.relativeTime(postData.timestamp);
|
||||
postData.post_rep = postData.reputation;
|
||||
postData['edited-class'] = postData.editor !== '' ? '' : 'none';
|
||||
postData['relativeEditTime'] = postData.edited !== '0' ? utils.relativeTime(postData.edited) : '';
|
||||
|
||||
|
||||
postData.content = postTools.markdownToHTML(postData.content);
|
||||
|
||||
if(postData.uploadedImages) {
|
||||
postData.uploadedImages = JSON.parse(postData.uploadedImages);
|
||||
try {
|
||||
postData.uploadedImages = JSON.parse(postData.uploadedImages);
|
||||
} catch(err) {
|
||||
postData.uploadedImages = [];
|
||||
winston.err(err);
|
||||
}
|
||||
} else {
|
||||
postData.uploadedImages = [];
|
||||
}
|
||||
@@ -147,9 +172,9 @@ var RDB = require('./redis.js'),
|
||||
|
||||
async.eachSeries(pids, iterator, function(err) {
|
||||
if(!err) {
|
||||
callback(posts);
|
||||
callback(null, posts);
|
||||
} else {
|
||||
callback([]);
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -173,15 +198,15 @@ var RDB = require('./redis.js'),
|
||||
type: 'error',
|
||||
timeout: 2000,
|
||||
title: 'Content too short',
|
||||
message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
|
||||
message: "Please enter a longer post. At least " + meta.config.minimumPostLength + " characters.",
|
||||
alert_id: 'post_error'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Posts.emitTooManyPostsAlert = function(socket) {
|
||||
socket.emit('event:alert', {
|
||||
title: 'Too many posts!',
|
||||
message: 'You can only post every '+ config.postDelay/1000 + ' seconds.',
|
||||
message: 'You can only post every '+ meta.config.postDelay/1000 + ' seconds.',
|
||||
type: 'error',
|
||||
timeout: 2000
|
||||
});
|
||||
@@ -192,49 +217,36 @@ var RDB = require('./redis.js'),
|
||||
content = content.trim();
|
||||
}
|
||||
|
||||
if (!content || content.length < config.minimumPostLength) {
|
||||
if (!content || content.length < meta.config.minimumPostLength) {
|
||||
callback(new Error('content-too-short'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'lastposttime', function(lastposttime) {
|
||||
if(Date.now() - lastposttime < config.postDelay) {
|
||||
if(Date.now() - lastposttime < meta.config.postDelay) {
|
||||
callback(new Error('too-many-posts'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
Posts.create(uid, tid, content, images, function(postData) {
|
||||
if (postData) {
|
||||
topics.addPostToTopic(tid, postData.pid);
|
||||
|
||||
|
||||
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);
|
||||
topics.markAsRead(tid, uid);
|
||||
});
|
||||
});
|
||||
|
||||
threadTools.notify_followers(tid, uid);
|
||||
|
||||
postData.content = postTools.markdownToHTML(postData.content);
|
||||
postData.post_rep = 0;
|
||||
postData.relativeTime = utils.relativeTime(postData.timestamp)
|
||||
postData.fav_star_class = 'icon-star-empty';
|
||||
postData['edited-class'] = 'none';
|
||||
postData.uploadedImages = JSON.parse(postData.uploadedImages);
|
||||
|
||||
var socketData = {
|
||||
'posts' : [
|
||||
postData
|
||||
]
|
||||
};
|
||||
|
||||
posts.addUserInfoToPost(socketData['posts'][0], function() {
|
||||
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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
callback(null, 'Reply successful');
|
||||
} else {
|
||||
callback(new Error('reply-error'), null);
|
||||
@@ -242,19 +254,19 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Posts.create = function(uid, tid, content, images, 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:save_post_content', content, function(content) {
|
||||
plugins.fireHook('filter:post.save', content, function(content) {
|
||||
var timestamp = Date.now(),
|
||||
postData = {
|
||||
'pid': pid,
|
||||
@@ -266,16 +278,24 @@ var RDB = require('./redis.js'),
|
||||
'editor': '',
|
||||
'edited': 0,
|
||||
'deleted': 0,
|
||||
'uploadedImages': ''
|
||||
'uploadedImages': '[]',
|
||||
'fav_button_class': '',
|
||||
'fav_star_class': 'icon-star-empty',
|
||||
'show_banned': 'hide',
|
||||
'relativeTime': '0 seconds',
|
||||
'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.getTopicField(tid, 'cid', function(err, cid) {
|
||||
RDB.handle(err);
|
||||
|
||||
@@ -292,22 +312,36 @@ var RDB = require('./redis.js'),
|
||||
|
||||
RDB.sadd('cid:' + cid + ':active_users', uid);
|
||||
});
|
||||
});
|
||||
|
||||
user.onNewPostMade(uid, tid, pid, timestamp);
|
||||
});
|
||||
|
||||
uploadPostImages(postData, images, function(err, uploadedImages) {
|
||||
if(err) {
|
||||
console.log('Uploading images failed!');
|
||||
} else {
|
||||
postData.uploadedImages = JSON.stringify(uploadedImages);
|
||||
Posts.setPostField(pid, 'uploadedImages', postData.uploadedImages);
|
||||
user.onNewPostMade(uid, tid, pid, timestamp);
|
||||
|
||||
async.parallel({
|
||||
uploadedImages: function(next) {
|
||||
uploadPostImages(postData, images, function(err, uploadedImages) {
|
||||
if(err) {
|
||||
winston.error('Uploading images failed!', err.stack);
|
||||
next(null, []);
|
||||
} else {
|
||||
next(null, uploadedImages);
|
||||
}
|
||||
});
|
||||
},
|
||||
content: function(next) {
|
||||
plugins.fireHook('filter:post.get', postData, function(postData) {
|
||||
postData.content = postTools.markdownToHTML(postData.content, false);
|
||||
next(null, postData.content);
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
postData.uploadedImages = results.uploadedImages;
|
||||
Posts.setPostField(pid, 'uploadedImages', JSON.stringify(postData.uploadedImages));
|
||||
postData.content = results.content;
|
||||
callback(postData);
|
||||
});
|
||||
|
||||
plugins.fireHook('action:save_post_content', [pid, content]);
|
||||
|
||||
|
||||
plugins.fireHook('action:post.save', [postData]);
|
||||
|
||||
postSearch.index(content, pid);
|
||||
});
|
||||
});
|
||||
@@ -319,9 +353,9 @@ var RDB = require('./redis.js'),
|
||||
|
||||
function uploadPostImages(postData, images, callback) {
|
||||
var imgur = require('./imgur');
|
||||
imgur.setClientID(global.nconf.get('imgurClientID'));
|
||||
|
||||
var uploadedImages = [];
|
||||
imgur.setClientID(meta.config.imgurClientID);
|
||||
|
||||
var uploadedImages = [];
|
||||
|
||||
function uploadImage(image, callback) {
|
||||
imgur.upload(image.data, 'base64', function(err, data) {
|
||||
@@ -333,10 +367,11 @@ var RDB = require('./redis.js'),
|
||||
uploadedImages.push(img);
|
||||
callback(null);
|
||||
} else {
|
||||
winston.error('Can\'t upload image, did you set imgurClientID?');
|
||||
callback(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if(!images) {
|
||||
@@ -352,20 +387,20 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Posts.getPostsByUid = function(uid, start, end, callback) {
|
||||
|
||||
|
||||
user.getPostIds(uid, start, end, function(pids) {
|
||||
|
||||
|
||||
if(pids && pids.length) {
|
||||
|
||||
Posts.getPostsByPids(pids, function(posts) {
|
||||
|
||||
Posts.getPostsByPids(pids, function(err, posts) {
|
||||
callback(posts);
|
||||
});
|
||||
}
|
||||
else
|
||||
callback([]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getTopicPostStats = function(socket) {
|
||||
@@ -373,11 +408,11 @@ var RDB = require('./redis.js'),
|
||||
if(err === null) {
|
||||
var stats = {
|
||||
topics: data[0]?data[0]:0,
|
||||
posts: data[1]?data[1]:0
|
||||
posts: data[1]?data[1]:0
|
||||
};
|
||||
|
||||
socket.emit('post.stats', stats);
|
||||
}
|
||||
}
|
||||
else
|
||||
console.log(err);
|
||||
});
|
||||
@@ -407,4 +442,19 @@ var RDB = require('./redis.js'),
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getFavourites = function(uid, callback) {
|
||||
RDB.zrevrange('uid:' + uid + ':favourites', 0, -1, function(err, pids) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
Posts.getPostSummaryByPids(pids, function(err, posts) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
callback(null, posts);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}(exports));
|
||||
18
src/redis.js
18
src/redis.js
@@ -1,8 +1,9 @@
|
||||
(function(RedisDB) {
|
||||
var redis = require('redis'),
|
||||
nconf = require('nconf'),
|
||||
utils = require('./../public/src/utils.js');
|
||||
|
||||
utils = require('./../public/src/utils.js'),
|
||||
winston = require('winston');
|
||||
|
||||
RedisDB.exports = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'));
|
||||
|
||||
if(nconf.get('redis:password')) {
|
||||
@@ -11,14 +12,9 @@
|
||||
|
||||
RedisDB.exports.handle = function(error) {
|
||||
if (error !== null) {
|
||||
winston.err(error);
|
||||
if (global.env !== 'production') {
|
||||
console.log("################# ERROR LOG ####################");
|
||||
console.log(error);
|
||||
console.log(arguments.callee.name);
|
||||
console.log("################# ERROR LOG ####################");
|
||||
throw new Error(error);
|
||||
} else {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +40,7 @@
|
||||
RedisDB.exports.hmget(key, fields, function(err, data) {
|
||||
if(err === null) {
|
||||
var returnData = {};
|
||||
|
||||
|
||||
for(var i=0, ii=fields.length; i<ii; ++i) {
|
||||
returnData[fields[i]] = data[i];
|
||||
}
|
||||
@@ -55,8 +51,8 @@
|
||||
console.log(err);
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ var user = require('./../user.js'),
|
||||
RDB = require('./../redis.js'),
|
||||
pkg = require('./../../package.json'),
|
||||
categories = require('./../categories.js'),
|
||||
plugins = require('../plugins');
|
||||
plugins = require('../plugins'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(Admin) {
|
||||
Admin.isAdmin = function(req, res, next) {
|
||||
@@ -17,7 +18,7 @@ var user = require('./../user.js'),
|
||||
Admin.build_header = function(res) {
|
||||
return templates['admin/header'].parse({
|
||||
csrf:res.locals.csrf_token,
|
||||
relative_path: global.nconf.get('relative_path')
|
||||
relative_path: nconf.get('relative_path')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -142,7 +143,7 @@ var user = require('./../user.js'),
|
||||
finalData[key] = jsonObject[key];
|
||||
}
|
||||
} catch(err){
|
||||
console.log('invalid redis status', i, data[i], err);
|
||||
winston.warn('can\'t parse redis status variable, ignoring', i, data[i], err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ var user = require('./../user.js'),
|
||||
utils = require('./../../public/src/utils.js'),
|
||||
pkg = require('../../package.json'),
|
||||
meta = require('./../meta.js');
|
||||
|
||||
|
||||
|
||||
(function(Api) {
|
||||
Api.create_routes = function(app) {
|
||||
@@ -16,18 +16,16 @@ var user = require('./../user.js'),
|
||||
});
|
||||
|
||||
app.get('/api/config', function(req, res, next) {
|
||||
meta.config.getFields(['postDelay', 'minimumTitleLength', 'minimumPostLength'], function(err, metaConfig) {
|
||||
if(err) return next();
|
||||
var clientConfig = require('../../public/config.json');
|
||||
|
||||
for (var attrname in metaConfig) {
|
||||
clientConfig[attrname] = metaConfig[attrname];
|
||||
}
|
||||
var config = require('../../public/config.json');
|
||||
|
||||
res.json(200, clientConfig);
|
||||
})
|
||||
config['postDelay'] = meta.config['postDelay'];
|
||||
config['minimumTitleLength'] = meta.config['minimumTitleLength'];
|
||||
config['minimumPostLength'] = meta.config['minimumPostLength'];
|
||||
config['imgurClientIDSet'] = !!meta.config['imgurClientID'];
|
||||
|
||||
res.json(200, config);
|
||||
});
|
||||
|
||||
|
||||
app.get('/api/home', function(req, res) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
categories.getAllCategories(function(data) {
|
||||
@@ -45,8 +43,8 @@ var user = require('./../user.js'),
|
||||
}
|
||||
|
||||
require('async').each(data.categories, iterator, function(err) {
|
||||
data.motd_class = (config.show_motd === '1' || config.show_motd === undefined) ? '' : 'none';
|
||||
data.motd = marked(config.motd || "# NodeBB v" + pkg.version + "\nWelcome to NodeBB, the discussion platform of the future.\n\n<a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-large\"><i class=\"icon-comment\"></i> Get NodeBB</a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-large\"><i class=\"icon-github-alt\"></i> Fork us on Github</a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-large\"><i class=\"icon-twitter\"></i> @dcplabs</a>");
|
||||
data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : 'none';
|
||||
data.motd = marked(meta.config.motd || "# NodeBB <span class='hidden-phone'>v " + pkg.version + "</span>\nWelcome to NodeBB, the discussion platform of the future.\n\n<a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-large\"><i class=\"icon-comment\"></i><span class='hidden-phone'> Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-large\"><i class=\"icon-github-alt\"></i><span class='hidden-phone'> Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-large\"><i class=\"icon-twitter\"></i><span class='hidden-phone'> @dcplabs</span></a>");
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
@@ -62,7 +60,7 @@ var user = require('./../user.js'),
|
||||
data = {
|
||||
'login_window:spansize': 'span12',
|
||||
'alternate_logins:display': 'none'
|
||||
};
|
||||
};
|
||||
} else {
|
||||
data = {
|
||||
'login_window:spansize': 'span6',
|
||||
@@ -77,7 +75,7 @@ var user = require('./../user.js'),
|
||||
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
|
||||
app.get('/api/register', function(req, res) {
|
||||
var data = {},
|
||||
login_strategies = auth.get_login_strategies(),
|
||||
@@ -87,7 +85,7 @@ var user = require('./../user.js'),
|
||||
data = {
|
||||
'register_window:spansize': 'span12',
|
||||
'alternate_logins:display': 'none'
|
||||
};
|
||||
};
|
||||
} else {
|
||||
data = {
|
||||
'register_window:spansize': 'span6',
|
||||
@@ -102,22 +100,25 @@ var user = require('./../user.js'),
|
||||
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
app.get('/api/topic/:id/:slug?', function(req, res) {
|
||||
|
||||
app.get('/api/topic/:id/:slug?', function(req, res, next) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
topics.getTopicWithPosts(req.params.id, uid, function(err, data) {
|
||||
if(data.deleted === '1' && data.expose_tools === 0) {
|
||||
return res.json(404, {});
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/api/category/:id/:slug?', function(req, res, next) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
categories.getCategoryById(req.params.id, uid, function(err, data) {
|
||||
if (!err)
|
||||
if (!err)
|
||||
res.json(data);
|
||||
else
|
||||
else
|
||||
next();
|
||||
}, req.params.id, uid);
|
||||
}, req.params.id, uid);
|
||||
});
|
||||
|
||||
app.get('/api/recent', function(req, res) {
|
||||
@@ -134,6 +135,13 @@ var user = require('./../user.js'),
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/unread/total', function(req, res) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
topics.getTotalUnread(uid, function(data) {
|
||||
res.json(data);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/confirm/:id', function(req, res) {
|
||||
user.email.confirm(req.params.id, function(data) {
|
||||
if (data.status === 'ok') {
|
||||
@@ -158,13 +166,13 @@ var user = require('./../user.js'),
|
||||
if (url) {
|
||||
res.json({
|
||||
url: url,
|
||||
home: global.nconf.get('url')
|
||||
home: nconf.get('url')
|
||||
});
|
||||
} else {
|
||||
res.status(404);
|
||||
res.redirect(global.nconf.get('relative_path') + '/404');
|
||||
res.redirect(nconf.get('relative_path') + '/404');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/search', function(req, res) {
|
||||
return res.json({
|
||||
@@ -191,7 +199,9 @@ var user = require('./../user.js'),
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
posts.getPostSummaryByPids(pids, function(posts) {
|
||||
posts.getPostSummaryByPids(pids, function(err, posts) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
callback(null, posts);
|
||||
});
|
||||
})
|
||||
@@ -201,7 +211,7 @@ var user = require('./../user.js'),
|
||||
search(topicSearch, function(err, tids) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
console.log(tids);
|
||||
|
||||
topics.getTopicsByTids(tids, 0, function(topics) {
|
||||
callback(null, topics);
|
||||
}, 0);
|
||||
@@ -209,7 +219,7 @@ var user = require('./../user.js'),
|
||||
}
|
||||
|
||||
async.parallel([searchPosts, searchTopics], function(err, results) {
|
||||
if (err)
|
||||
if (err)
|
||||
return next();
|
||||
var noresults = !results[0].length && !results[1].length;
|
||||
return res.json({
|
||||
@@ -220,11 +230,19 @@ var user = require('./../user.js'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/api/reset', function(req, res) {
|
||||
res.json({});
|
||||
});
|
||||
|
||||
app.get('/api/reset/:code', function(req, res) {
|
||||
res.json({ reset_code: req.params.code });
|
||||
});
|
||||
|
||||
app.get('/api/404', function(req, res) {
|
||||
res.json({});
|
||||
});
|
||||
|
||||
|
||||
app.get('/api/403', function(req, res) {
|
||||
res.json({});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
passportFacebook = require('passport-facebook').Strategy,
|
||||
login_strategies = [],
|
||||
nconf = require('nconf'),
|
||||
users = require('../user'),
|
||||
meta = require('../meta'),
|
||||
user = require('../user'),
|
||||
winston = require('winston'),
|
||||
login_module = require('./../login.js');
|
||||
|
||||
passport.use(new passportLocal(function(user, password, next) {
|
||||
@@ -16,10 +18,10 @@
|
||||
});
|
||||
}));
|
||||
|
||||
if (global.config['social:twitter:key'] && global.config['social:twitter:secret']) {
|
||||
if (meta.config['social:twitter:key'] && meta.config['social:twitter:secret']) {
|
||||
passport.use(new passportTwitter({
|
||||
consumerKey: global.config['social:twitter:key'],
|
||||
consumerSecret: global.config['social:twitter:secret'],
|
||||
consumerKey: meta.config['social:twitter:key'],
|
||||
consumerSecret: meta.config['social:twitter:secret'],
|
||||
callbackURL: nconf.get('url') + 'auth/twitter/callback'
|
||||
}, function(token, tokenSecret, profile, done) {
|
||||
login_module.loginViaTwitter(profile.id, profile.username, profile.photos, function(err, user) {
|
||||
@@ -31,10 +33,10 @@
|
||||
login_strategies.push('twitter');
|
||||
}
|
||||
|
||||
if (global.config['social:google:id'] && global.config['social:google:secret']) {
|
||||
if (meta.config['social:google:id'] && meta.config['social:google:secret']) {
|
||||
passport.use(new passportGoogle({
|
||||
clientID: global.config['social:google:id'],
|
||||
clientSecret: global.config['social:google:secret'],
|
||||
clientID: meta.config['social:google:id'],
|
||||
clientSecret: meta.config['social:google:secret'],
|
||||
callbackURL: nconf.get('url') + 'auth/google/callback'
|
||||
}, function(accessToken, refreshToken, profile, done) {
|
||||
login_module.loginViaGoogle(profile.id, profile.displayName, profile.emails[0].value, function(err, user) {
|
||||
@@ -46,10 +48,10 @@
|
||||
login_strategies.push('google');
|
||||
}
|
||||
|
||||
if (global.config['social:facebook:app_id'] && global.config['social:facebook:secret']) {
|
||||
if (meta.config['social:facebook:app_id'] && meta.config['social:facebook:secret']) {
|
||||
passport.use(new passportFacebook({
|
||||
clientID: global.config['social:facebook:app_id'],
|
||||
clientSecret: global.config['social:facebook:secret'],
|
||||
clientID: meta.config['social:facebook:app_id'],
|
||||
clientSecret: meta.config['social:facebook:secret'],
|
||||
callbackURL: nconf.get('url') + 'auth/facebook/callback'
|
||||
}, function(accessToken, refreshToken, profile, done) {
|
||||
login_module.loginViaFacebook(profile.id, profile.displayName, profile.emails[0].value, function(err, user) {
|
||||
@@ -75,7 +77,7 @@
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
}
|
||||
|
||||
|
||||
|
||||
Auth.get_login_strategies = function() {
|
||||
return login_strategies;
|
||||
@@ -85,7 +87,7 @@
|
||||
|
||||
app.get('/logout', function(req, res) {
|
||||
if (req.user && req.user.uid > 0) {
|
||||
console.log('info: [Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')');
|
||||
winston.info('[Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')');
|
||||
login_module.logout(req.sessionID, function(logout) {
|
||||
req.logout();
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
@@ -126,33 +128,43 @@
|
||||
|
||||
app.get('/reset/:code', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + templates['reset_code'].parse({ reset_code: req.params.code }) + templates['footer']);
|
||||
res.send(header + app.create_route('reset/'+req.params.code) + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/reset', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
console.log(header);
|
||||
res.send(header + templates['reset'] + templates['footer']);
|
||||
res.send(header + app.create_route('reset') + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.post('/login', passport.authenticate('local'), function(req, res) {
|
||||
res.json({success:1});
|
||||
app.post('/login', function(req, res, next) {
|
||||
passport.authenticate('local', function(err, user, info) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!user) {
|
||||
return res.send({ success : false, message : info.message });
|
||||
}
|
||||
req.login({
|
||||
uid: user.uid
|
||||
}, function() {
|
||||
res.send({ success : true, message : 'authentication succeeded' });
|
||||
});
|
||||
})(req, res, next);
|
||||
});
|
||||
|
||||
app.post('/register', function(req, res) {
|
||||
users.create(req.body.username, req.body.password, req.body.email, function(err, uid) {
|
||||
|
||||
if (err === null && uid > 0) {
|
||||
app.post('/register', function(req, res) {
|
||||
user.create(req.body.username, req.body.password, req.body.email, function(err, uid) {
|
||||
|
||||
if (err === null && uid) {
|
||||
req.login({
|
||||
uid: uid
|
||||
}, function() {
|
||||
res.redirect(global.nconf.get('relative_path') + '/');
|
||||
res.redirect(nconf.get('relative_path') + '/');
|
||||
});
|
||||
} else {
|
||||
res.redirect(global.nconf.get('relative_path') + '/register');
|
||||
res.redirect(nconf.get('relative_path') + '/register');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,16 +4,17 @@ var user = require('./../user.js'),
|
||||
fs = require('fs'),
|
||||
utils = require('./../../public/src/utils.js'),
|
||||
path = require('path'),
|
||||
marked = require('marked');
|
||||
marked = require('marked'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(User) {
|
||||
User.create_routes = function(app) {
|
||||
|
||||
|
||||
app.get('/uid/:uid', function(req, res) {
|
||||
|
||||
|
||||
if(!req.params.uid)
|
||||
return res.redirect('/404');
|
||||
|
||||
|
||||
user.getUserData(req.params.uid, function(data) {
|
||||
if(data) {
|
||||
res.send(data);
|
||||
@@ -21,7 +22,7 @@ var user = require('./../user.js'),
|
||||
res.json(404, {error:"User doesn't exist!"});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res) {
|
||||
@@ -29,25 +30,25 @@ var user = require('./../user.js'),
|
||||
res.send(header + app.create_route("users", "users") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/users-latest', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route("users-latest", "users") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/users-sort-posts', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route("users-sort-posts", "users") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/users-sort-reputation', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route("users-sort-reputation", "users") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/users-search', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route("users-search", "users") + templates['footer']);
|
||||
@@ -63,23 +64,22 @@ var user = require('./../user.js'),
|
||||
|
||||
user.get_uid_by_userslug(req.params.userslug, function(uid) {
|
||||
if(!uid) {
|
||||
next();
|
||||
return;
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route('users/'+req.params.userslug, 'account') + templates['footer']);
|
||||
res.send(header + app.create_route('users/' + req.params.userslug, 'account') + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/users/:userslug/edit', function(req, res) {
|
||||
|
||||
if(!req.user)
|
||||
return res.redirect('/403');
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function(userslug) {
|
||||
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function(err, userslug) {
|
||||
if(req.params.userslug && userslug === req.params.userslug) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route('users/'+req.params.userslug+'/edit','accountedit') + templates['footer']);
|
||||
@@ -87,46 +87,46 @@ var user = require('./../user.js'),
|
||||
} else {
|
||||
return res.redirect('/404');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/users/:userslug/settings', function(req, res) {
|
||||
|
||||
if(!req.user)
|
||||
return res.redirect('/403');
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function(userslug) {
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function(err, userslug) {
|
||||
if(req.params.userslug && userslug === req.params.userslug) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route('users/'+req.params.userslug+'/settings','accountsettings') + templates['footer']);
|
||||
})
|
||||
})
|
||||
} else {
|
||||
return res.redirect('/404');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/users/uploadpicture', function(req, res) {
|
||||
if(!req.user)
|
||||
return res.redirect('/403');
|
||||
|
||||
|
||||
if(req.files.userPhoto.size > 262144) {
|
||||
res.send({
|
||||
error: 'Images must be smaller than 256kb!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
|
||||
|
||||
|
||||
if(allowedTypes.indexOf(req.files.userPhoto.type) === -1) {
|
||||
res.send({
|
||||
error: 'Allowed image types are png, jpg and gif!'
|
||||
});
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
user.getUserField(req.user.uid, 'uploadedpicture', function(oldpicture) {
|
||||
user.getUserField(req.user.uid, 'uploadedpicture', function(err, oldpicture) {
|
||||
if(!oldpicture) {
|
||||
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
|
||||
return;
|
||||
@@ -135,15 +135,15 @@ var user = require('./../user.js'),
|
||||
var absolutePath = path.join(global.configuration['ROOT_DIRECTORY'], global.nconf.get('upload_path'), path.basename(oldpicture));
|
||||
|
||||
fs.unlink(absolutePath, function(err) {
|
||||
if(err) {
|
||||
console.error('[%d] %s', Date.now(), + err);
|
||||
if(err) {
|
||||
winston.error('[%d] %s', Date.now(), + err);
|
||||
}
|
||||
|
||||
uploadUserPicture(req.user.uid, path.extname(req.files.userPhoto.name), req.files.userPhoto.path, res);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function uploadUserPicture(uid, extension, tempPath, res) {
|
||||
if(!extension) {
|
||||
res.send({
|
||||
@@ -154,10 +154,9 @@ var user = require('./../user.js'),
|
||||
|
||||
var filename = uid + '-profileimg' + extension;
|
||||
var uploadPath = path.join(global.configuration['ROOT_DIRECTORY'], global.nconf.get('upload_path'), filename);
|
||||
|
||||
// @todo move to proper logging code - this should only be temporary
|
||||
console.log('Info: Attempting upload to: '+ uploadPath);
|
||||
|
||||
|
||||
winston.info('Attempting upload to: '+ uploadPath);
|
||||
|
||||
var is = fs.createReadStream(tempPath);
|
||||
var os = fs.createWriteStream(uploadPath);
|
||||
|
||||
@@ -176,11 +175,7 @@ var user = require('./../user.js'),
|
||||
height: 128
|
||||
}, function(err, stdout, stderr){
|
||||
if (err) {
|
||||
// @todo: better logging method; for now, send to stderr.
|
||||
// ideally, this should be happening in another process
|
||||
// to avoid poisoning the main process on error or allowing a significant problem
|
||||
// to crash the main process
|
||||
console.error('[%d] %s', Date.now(), + err);
|
||||
winston.err(err.message, err.stack);
|
||||
}
|
||||
|
||||
res.json({ path: imageUrl });
|
||||
@@ -189,7 +184,7 @@ var user = require('./../user.js'),
|
||||
});
|
||||
|
||||
os.on('error', function(err) {
|
||||
console.error('[%d] %s', Date.now(), + err);
|
||||
winston.error('[%d] %s', Date.now(), + err);
|
||||
});
|
||||
|
||||
is.pipe(os);
|
||||
@@ -199,24 +194,24 @@ var user = require('./../user.js'),
|
||||
|
||||
if(!req.user)
|
||||
return res.redirect('/403');
|
||||
|
||||
|
||||
user.get_uid_by_userslug(req.params.userslug, function(uid) {
|
||||
if(!uid) {
|
||||
res.redirect('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route('users/'+req.params.userslug+'/following','following') + templates['footer']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/users/:userslug/followers', function(req, res) {
|
||||
|
||||
if(!req.user)
|
||||
return res.redirect('/403');
|
||||
|
||||
|
||||
user.get_uid_by_userslug(req.params.userslug, function(uid) {
|
||||
if(!uid) {
|
||||
res.redirect('/404');
|
||||
@@ -228,6 +223,22 @@ var user = require('./../user.js'),
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/users/:userslug/favourites', function(req, res) {
|
||||
|
||||
if(!req.user)
|
||||
return res.redirect('/403');
|
||||
|
||||
user.get_uid_by_userslug(req.params.userslug, function(uid) {
|
||||
if(!uid) {
|
||||
res.redirect('/404');
|
||||
return;
|
||||
}
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route('users/'+req.params.userslug+'/favourites','favourites') + templates['footer']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/users/:userslug/following', function(req, res) {
|
||||
var callerUID = req.user ? req.user.uid : 0;
|
||||
|
||||
@@ -238,7 +249,7 @@ var user = require('./../user.js'),
|
||||
userData.followingCount = followingData.length;
|
||||
res.json(userData);
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
}
|
||||
@@ -247,7 +258,7 @@ var user = require('./../user.js'),
|
||||
|
||||
app.get('/api/users/:userslug/followers', function(req, res) {
|
||||
var callerUID = req.user ? req.user.uid : 0;
|
||||
|
||||
|
||||
getUserDataByUserSlug(req.params.userslug, callerUID, function(userData) {
|
||||
if(userData) {
|
||||
user.getFollowers(userData.uid, function(followersData) {
|
||||
@@ -257,7 +268,7 @@ var user = require('./../user.js'),
|
||||
});
|
||||
} else {
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -269,7 +280,7 @@ var user = require('./../user.js'),
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/users/:userslug/settings', function(req, res) {
|
||||
app.get('/api/users/:userslug/settings', function(req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : 0;
|
||||
|
||||
user.get_uid_by_userslug(req.params.userslug, function(uid) {
|
||||
@@ -277,12 +288,15 @@ var user = require('./../user.js'),
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(uid !== callerUID || callerUID === "0") {
|
||||
res.json(403, { error: 'Not allowed!' });
|
||||
return;
|
||||
}
|
||||
user.getUserFields(uid, ['username','userslug','showemail'], function(userData) {
|
||||
user.getUserFields(uid, ['username','userslug','showemail'], function(err, userData) {
|
||||
if(err)
|
||||
return next(err);
|
||||
|
||||
if(userData) {
|
||||
if(userData.showemail && userData.showemail === "1")
|
||||
userData.showemail = "checked";
|
||||
@@ -291,9 +305,42 @@ var user = require('./../user.js'),
|
||||
res.json(userData);
|
||||
} else {
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/users/:userslug/favourites', function(req, res, next) {
|
||||
var callerUID = req.user ? req.user.uid : 0;
|
||||
|
||||
user.get_uid_by_userslug(req.params.userslug, function(uid) {
|
||||
if(!uid) {
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
return;
|
||||
}
|
||||
|
||||
if(uid !== callerUID || callerUID === "0") {
|
||||
res.json(403, { error: 'Not allowed!' });
|
||||
return;
|
||||
}
|
||||
|
||||
user.getUserFields(uid, ['username','userslug'], function(err, userData) {
|
||||
if(err)
|
||||
return next(err);
|
||||
|
||||
if(userData) {
|
||||
posts.getFavourites(uid, function(err, posts) {
|
||||
if(err)
|
||||
return next(err);
|
||||
userData.posts = posts;
|
||||
userData.show_nofavourites = posts.length?'hide':'show';
|
||||
res.json(userData);
|
||||
});
|
||||
} else {
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/users/:userslug', function(req, res) {
|
||||
@@ -307,13 +354,17 @@ var user = require('./../user.js'),
|
||||
userData.posts = posts.filter(function(p) {return p.deleted !== "1";});
|
||||
userData.isFollowing = isFollowing;
|
||||
userData.signature = postTools.markdownToHTML(userData.signature, true);
|
||||
if(!userData.profileviews)
|
||||
userData.profileviews = 1;
|
||||
if(callerUID !== userData.uid)
|
||||
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
|
||||
res.json(userData);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
res.json(404, { error: 'User not found!' }) ;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/users', getUsersSortedByJoinDate);
|
||||
@@ -321,17 +372,17 @@ var user = require('./../user.js'),
|
||||
app.get('/api/users-sort-reputation', getUsersSortedByReputation);
|
||||
app.get('/api/users-latest', getUsersSortedByJoinDate);
|
||||
app.get('/api/users-search', getUsersForSearch);
|
||||
|
||||
|
||||
|
||||
|
||||
function getUsersSortedByJoinDate(req, res) {
|
||||
user.getUsers('users:joindate', 0, 49, function(err, data) {
|
||||
res.json({ search_display: 'none',loadmore_display:'block', users:data });
|
||||
res.json({ search_display: 'none', loadmore_display:'block', users:data });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getUsersSortedByPosts(req, res) {
|
||||
user.getUsers('users:postcount', 0, 49, function(err, data) {
|
||||
res.json({ search_display: 'none',loadmore_display:'block', users:data });
|
||||
res.json({ search_display: 'none', loadmore_display:'block', users:data });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -340,19 +391,19 @@ var user = require('./../user.js'),
|
||||
res.json({ search_display: 'none', loadmore_display:'block', users:data });
|
||||
});
|
||||
}
|
||||
|
||||
function getUsersForSearch(req, res) {
|
||||
|
||||
function getUsersForSearch(req, res) {
|
||||
res.json({ search_display: 'block', loadmore_display:'none', users: [] });
|
||||
}
|
||||
|
||||
function getUserDataByUserSlug(userslug, callerUID, callback) {
|
||||
user.get_uid_by_userslug(userslug, function(uid) {
|
||||
|
||||
|
||||
if(uid === null) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
user.getUserData(uid, function(data) {
|
||||
if(data) {
|
||||
data.joindate = utils.relativeTime(data.joindate);
|
||||
@@ -362,19 +413,20 @@ var user = require('./../user.js'),
|
||||
} else {
|
||||
data.age = new Date().getFullYear() - new Date(data.birthday).getFullYear();
|
||||
}
|
||||
|
||||
|
||||
function canSeeEmail() {
|
||||
return callerUID === uid || (data.email && (data.showemail && data.showemail === "1"));
|
||||
}
|
||||
|
||||
if(!canSeeEmail())
|
||||
if(!canSeeEmail())
|
||||
data.email = "";
|
||||
|
||||
if(callerUID === uid && (!data.showemail || data.showemail === "0"))
|
||||
data.emailClass = "";
|
||||
else
|
||||
else
|
||||
data.emailClass = "hide";
|
||||
|
||||
data.show_banned = data.banned === '1'?'':'hide';
|
||||
|
||||
data.uid = uid;
|
||||
data.yourid = callerUID;
|
||||
@@ -391,7 +443,7 @@ var user = require('./../user.js'),
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ var path = require('path'),
|
||||
async.parallel([sitemap.getStaticUrls, sitemap.getDynamicUrls], function(err, urls) {
|
||||
var urls = urls[0].concat(urls[1]),
|
||||
map = sm.createSitemap({
|
||||
hostname: global.nconf.get('url'),
|
||||
hostname: nconf.get('url'),
|
||||
cacheTime: 600000,
|
||||
urls: urls
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,9 @@ var RDB = require('./redis.js'),
|
||||
notifications = require('./notifications.js'),
|
||||
posts = require('./posts'),
|
||||
reds = require('reds'),
|
||||
topicSearch = reds.createSearch('nodebbtopicsearch');
|
||||
topicSearch = reds.createSearch('nodebbtopicsearch'),
|
||||
winston = require('winston'),
|
||||
meta = require('./meta');
|
||||
|
||||
(function(ThreadTools) {
|
||||
|
||||
@@ -16,10 +18,10 @@ var RDB = require('./redis.js'),
|
||||
callback(!!ismember || false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ThreadTools.privileges = function(tid, uid, callback) {
|
||||
//todo: break early if one condition is true
|
||||
|
||||
//todo: break early if one condition is true
|
||||
|
||||
function getCategoryPrivileges(next) {
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
categories.privileges(cid, uid, function(privileges) {
|
||||
@@ -29,11 +31,12 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
function hasEnoughRep(next) {
|
||||
user.getUserField(uid, 'reputation', function(reputation) {
|
||||
next(null, reputation >= global.config['privileges:manage_topic']);
|
||||
user.getUserField(uid, 'reputation', function(err, reputation) {
|
||||
if (err) return next(null, false);
|
||||
next(null, reputation >= meta.config['privileges:manage_topic']);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
async.parallel([getCategoryPrivileges, hasEnoughRep], function(err, results) {
|
||||
callback({
|
||||
@@ -86,8 +89,8 @@ var RDB = require('./redis.js'),
|
||||
ThreadTools.delete = function(tid, uid, callback) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable || uid === -1) {
|
||||
|
||||
topics.setTopicField(tid, 'deleted', 1);
|
||||
|
||||
topics.delete(tid);
|
||||
ThreadTools.lock(tid, uid);
|
||||
|
||||
topicSearch.remove(tid);
|
||||
@@ -106,15 +109,15 @@ var RDB = require('./redis.js'),
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
|
||||
topics.setTopicField(tid, 'deleted', 0);
|
||||
topics.restore(tid);
|
||||
ThreadTools.unlock(tid, uid);
|
||||
|
||||
if (socket) {
|
||||
io.sockets.in('topic_' + tid).emit('event:topic_restored', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
io.sockets.in('topic_' + tid).emit('event:topic_restored', {
|
||||
tid: tid,
|
||||
status: 'ok'
|
||||
});
|
||||
|
||||
if (socket) {
|
||||
socket.emit('api:topic.restore', {
|
||||
status: 'ok',
|
||||
tid: tid
|
||||
@@ -131,12 +134,12 @@ var RDB = require('./redis.js'),
|
||||
ThreadTools.pin = function(tid, uid, socket) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
|
||||
|
||||
topics.setTopicField(tid, 'pinned', 1);
|
||||
topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
RDB.zadd('categories:' + cid + ':tid', Math.pow(2,53), tid);
|
||||
});
|
||||
|
||||
|
||||
if (socket) {
|
||||
io.sockets.in('topic_' + tid).emit('event:topic_pinned', {
|
||||
tid: tid,
|
||||
@@ -155,9 +158,9 @@ var RDB = require('./redis.js'),
|
||||
ThreadTools.unpin = function(tid, uid, socket) {
|
||||
ThreadTools.privileges(tid, uid, function(privileges) {
|
||||
if (privileges.editable) {
|
||||
|
||||
|
||||
topics.setTopicField(tid, 'pinned', 0);
|
||||
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(topicData) {
|
||||
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
|
||||
RDB.zadd('categories:' + topicData.cid + ':tid', topicData.lastposttime, tid);
|
||||
});
|
||||
if (socket) {
|
||||
@@ -176,23 +179,23 @@ var RDB = require('./redis.js'),
|
||||
}
|
||||
|
||||
ThreadTools.move = function(tid, cid, socket) {
|
||||
|
||||
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(topicData) {
|
||||
|
||||
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
|
||||
var oldCid = topicData.cid;
|
||||
var multi = RDB.multi();
|
||||
|
||||
multi.zrem('categories:' + oldCid + ':tid', tid);
|
||||
multi.zadd('categories:' + cid + ':tid', topicData.lastposttime, tid);
|
||||
|
||||
|
||||
multi.exec(function(err, result) {
|
||||
|
||||
if (!err && result === 1) {
|
||||
if (!err && result[0] === 1 && result[1] === 1) {
|
||||
|
||||
topics.setTopicField(tid, 'cid', cid);
|
||||
|
||||
categories.moveRecentReplies(tid, oldCid, cid, function(err, data) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -257,7 +260,7 @@ var RDB = require('./redis.js'),
|
||||
ThreadTools.notify_followers = function(tid, exceptUid) {
|
||||
async.parallel([
|
||||
function(next) {
|
||||
|
||||
|
||||
topics.getTopicField(tid, 'title', function(err, title) {
|
||||
topics.getTeaser(tid, function(err, teaser) {
|
||||
if (!err) {
|
||||
@@ -267,8 +270,8 @@ var RDB = require('./redis.js'),
|
||||
} else next(err);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
function(next) {
|
||||
ThreadTools.get_followers(tid, function(err, followers) {
|
||||
@@ -289,16 +292,15 @@ var RDB = require('./redis.js'),
|
||||
var numPosts = posts.length;
|
||||
if(!numPosts)
|
||||
return callback(new Error('no-undeleted-pids-found'));
|
||||
|
||||
|
||||
while(numPosts--) {
|
||||
if(posts[numPosts].deleted !== '1') {
|
||||
callback(null, posts[numPosts].pid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, nothing was found...
|
||||
|
||||
callback(new Error('no-undeleted-pids-found'));
|
||||
});
|
||||
});
|
||||
}
|
||||
}(exports));
|
||||
293
src/topics.js
293
src/topics.js
@@ -20,8 +20,6 @@ marked.setOptions({
|
||||
|
||||
(function(Topics) {
|
||||
|
||||
|
||||
|
||||
Topics.getTopicData = function(tid, callback) {
|
||||
RDB.hgetall('topic:' + tid, function(err, data) {
|
||||
if(err === null)
|
||||
@@ -33,8 +31,7 @@ marked.setOptions({
|
||||
|
||||
Topics.getTopicDataWithUsername = function(tid, callback) {
|
||||
Topics.getTopicData(tid, function(topic) {
|
||||
user.getUserField(topic.uid, 'username', function(username) {
|
||||
|
||||
user.getUserField(topic.uid, 'username', function(err, username) {
|
||||
topic.username = username;
|
||||
callback(topic);
|
||||
});
|
||||
@@ -45,15 +42,15 @@ marked.setOptions({
|
||||
posts.getPostsByTid(tid, start, end, function(postData) {
|
||||
if(Array.isArray(postData) && !postData.length)
|
||||
return callback([]);
|
||||
|
||||
|
||||
function getFavouritesData(next) {
|
||||
var pids = [];
|
||||
for(var i=0; i<postData.length; ++i)
|
||||
for(var i=0; i<postData.length; ++i)
|
||||
pids.push(postData[i].pid);
|
||||
|
||||
favourites.getFavouritesByPostIDs(pids, current_user, function(fav_data) {
|
||||
next(null, fav_data);
|
||||
});
|
||||
next(null, fav_data);
|
||||
});
|
||||
}
|
||||
|
||||
function addUserInfoToPosts(next) {
|
||||
@@ -63,26 +60,28 @@ marked.setOptions({
|
||||
});
|
||||
}
|
||||
|
||||
async.each(postData, iterator, function(err) {
|
||||
async.each(postData, iterator, function(err) {
|
||||
next(err, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getPrivileges(next) {
|
||||
threadTools.privileges(tid, current_user, function(privData) {
|
||||
next(null, privData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async.parallel([getFavouritesData, addUserInfoToPosts, getPrivileges], function(err, results) {
|
||||
var fav_data = results[0],
|
||||
privileges = results[2];
|
||||
|
||||
for(var i=0; i<postData.length; ++i) {
|
||||
postData[i].fav_button_class = fav_data[postData[i].pid]? 'btn-warning' : '';
|
||||
postData[i].fav_star_class = fav_data[postData[i].pid] ? 'icon-star' : 'icon-star-empty';
|
||||
postData[i]['display_moderator_tools'] = (postData[i].uid == current_user || privileges.editable) ? 'show' : 'none';
|
||||
postData[i].show_banned = postData[i].user_banned === '1'?'show':'hide';
|
||||
}
|
||||
|
||||
|
||||
callback(postData);
|
||||
});
|
||||
});
|
||||
@@ -99,9 +98,9 @@ marked.setOptions({
|
||||
var timestamp = Date.now();
|
||||
|
||||
var args = [ 'topics:recent', '+inf', timestamp - 86400000, 'WITHSCORES', 'LIMIT', start, end - start + 1];
|
||||
|
||||
|
||||
RDB.zrevrangebyscore(args, function(err, tids) {
|
||||
|
||||
|
||||
var latestTopics = {
|
||||
'category_name' : 'Recent',
|
||||
'show_sidebar' : 'hidden',
|
||||
@@ -124,7 +123,49 @@ marked.setOptions({
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Topics.getTotalUnread = function(uid, callback) {
|
||||
|
||||
var unreadTids = [],
|
||||
start = 0,
|
||||
stop = 21,
|
||||
done = false;
|
||||
|
||||
async.whilst(
|
||||
function () { return unreadTids.length < 21 && !done; },
|
||||
function (callback) {
|
||||
RDB.zrevrange('topics:recent', start, stop, function(err, tids) {
|
||||
|
||||
if(err)
|
||||
return callback(err);
|
||||
|
||||
if(tids && !tids.length) {
|
||||
done = true;
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
Topics.hasReadTopics(tids, uid, function(read) {
|
||||
|
||||
var newtids = tids.filter(function(tid, index, self) {
|
||||
return read[index] === 0;
|
||||
});
|
||||
|
||||
unreadTids.push.apply(unreadTids, newtids);
|
||||
|
||||
start = stop + 1;
|
||||
stop = start + 21;
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
},
|
||||
function (err) {
|
||||
callback({
|
||||
count: unreadTids.length
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Topics.getUnreadTopics = function(uid, start, stop, callback) {
|
||||
|
||||
var unreadTopics = {
|
||||
@@ -137,65 +178,82 @@ marked.setOptions({
|
||||
'topics' : []
|
||||
};
|
||||
|
||||
RDB.zrevrange('topics:recent', start, stop, function (err, tids) {
|
||||
|
||||
function noUnreadTopics() {
|
||||
unreadTopics.no_topics_message = 'show';
|
||||
unreadTopics.show_markallread_button = 'hidden';
|
||||
function noUnreadTopics() {
|
||||
unreadTopics.no_topics_message = 'show';
|
||||
unreadTopics.show_markallread_button = 'hidden';
|
||||
callback(unreadTopics);
|
||||
}
|
||||
|
||||
function sendUnreadTopics(topicIds) {
|
||||
Topics.getTopicsByTids(topicIds, uid, function(topicData) {
|
||||
unreadTopics.topics = topicData;
|
||||
unreadTopics.nextStart = start + topicIds.length;
|
||||
if(!topicData || topicData.length === 0)
|
||||
unreadTopics.no_topics_message = 'show';
|
||||
if(uid === 0 || topicData.length === 0)
|
||||
unreadTopics.show_markallread_button = 'hidden';
|
||||
callback(unreadTopics);
|
||||
}
|
||||
|
||||
function sendUnreadTopics(topicIds) {
|
||||
Topics.getTopicsByTids(topicIds, uid, function(topicData) {
|
||||
unreadTopics.topics = topicData;
|
||||
unreadTopics.nextStart = start + tids.length;
|
||||
if(uid === 0)
|
||||
unreadTopics.show_markallread_button = 'hidden';
|
||||
callback(unreadTopics);
|
||||
});
|
||||
}
|
||||
|
||||
if (!tids || !tids.length) {
|
||||
noUnreadTopics();
|
||||
return;
|
||||
}
|
||||
|
||||
if(uid === 0) {
|
||||
sendUnreadTopics(tids);
|
||||
} else {
|
||||
|
||||
Topics.hasReadTopics(tids, uid, function(read) {
|
||||
|
||||
var unreadTids = tids.filter(function(tid, index, self) {
|
||||
return read[index] === 0;
|
||||
});
|
||||
|
||||
if (!unreadTids || !unreadTids.length) {
|
||||
noUnreadTopics();
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
var unreadTids = [],
|
||||
done = false;
|
||||
|
||||
async.whilst(
|
||||
function () { return unreadTids.length < 20 && !done; },
|
||||
function (callback) {
|
||||
RDB.zrevrange('topics:recent', start, stop, function(err, tids) {
|
||||
if(err)
|
||||
return callback(err);
|
||||
|
||||
if(tids && !tids.length) {
|
||||
done = true;
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
if(uid === 0) {
|
||||
unreadTids.push.apply(unreadTids, tids);
|
||||
callback(null);
|
||||
} else {
|
||||
Topics.hasReadTopics(tids, uid, function(read) {
|
||||
|
||||
var newtids = tids.filter(function(tid, index, self) {
|
||||
return read[index] === 0;
|
||||
});
|
||||
|
||||
unreadTids.push.apply(unreadTids, newtids);
|
||||
start = stop + 1;
|
||||
stop = start + 19;
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
function (err) {
|
||||
if(err)
|
||||
return callback([]);
|
||||
if(unreadTids.length)
|
||||
sendUnreadTopics(unreadTids);
|
||||
});
|
||||
else
|
||||
noUnreadTopics();
|
||||
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
Topics.getTopicsByTids = function(tids, current_user, callback, category_id) {
|
||||
|
||||
var retrieved_topics = [];
|
||||
|
||||
|
||||
if(!Array.isArray(tids) || tids.length === 0) {
|
||||
callback(retrieved_topics);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
function getTopicInfo(topicData, callback) {
|
||||
|
||||
function getUserName(next) {
|
||||
user.getUserField(topicData.uid, 'username', function(username) {
|
||||
next(null, username);
|
||||
});
|
||||
function getUserInfo(next) {
|
||||
user.getUserFields(topicData.uid, ['username'], next);
|
||||
}
|
||||
|
||||
function hasReadTopic(next) {
|
||||
@@ -217,17 +275,13 @@ marked.setOptions({
|
||||
});
|
||||
}
|
||||
|
||||
async.parallel([getUserName, hasReadTopic, getTeaserInfo, getPrivileges], function(err, results) {
|
||||
var username = results[0],
|
||||
hasReadTopic = results[1],
|
||||
teaserInfo = results[2],
|
||||
privileges = results[3];
|
||||
|
||||
async.parallel([getUserInfo, hasReadTopic, getTeaserInfo, getPrivileges], function(err, results) {
|
||||
callback({
|
||||
username: username,
|
||||
hasread: hasReadTopic,
|
||||
teaserInfo: teaserInfo,
|
||||
privileges: privileges
|
||||
username: results[0].username,
|
||||
userbanned: results[0].banned,
|
||||
hasread: results[1],
|
||||
teaserInfo: results[2],
|
||||
privileges: results[3]
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -242,7 +296,7 @@ marked.setOptions({
|
||||
if(!topicData) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
|
||||
getTopicInfo(topicData, function(topicInfo) {
|
||||
|
||||
topicData['pin-icon'] = topicData.pinned === '1' ? 'icon-pushpin' : 'none';
|
||||
@@ -260,12 +314,12 @@ marked.setOptions({
|
||||
|
||||
if (isTopicVisible(topicData, topicInfo))
|
||||
retrieved_topics.push(topicData);
|
||||
|
||||
callback(null);
|
||||
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.eachSeries(tids, loadTopic, function(err) {
|
||||
if(!err) {
|
||||
callback(retrieved_topics);
|
||||
@@ -276,7 +330,7 @@ marked.setOptions({
|
||||
|
||||
Topics.getTopicWithPosts = function(tid, current_user, callback) {
|
||||
threadTools.exists(tid, function(exists) {
|
||||
if (!exists)
|
||||
if (!exists)
|
||||
return callback(new Error('Topic tid \'' + tid + '\' not found'));
|
||||
|
||||
Topics.markAsRead(tid, current_user);
|
||||
@@ -297,8 +351,8 @@ marked.setOptions({
|
||||
threadTools.privileges(tid, current_user, function(privData) {
|
||||
next(null, privData);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getCategoryData(next) {
|
||||
Topics.getCategoryData(tid, next);
|
||||
}
|
||||
@@ -309,14 +363,14 @@ marked.setOptions({
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var topicData = results[0],
|
||||
topicPosts = results[1],
|
||||
privileges = results[2],
|
||||
categoryData = results[3];
|
||||
|
||||
var main_posts = topicPosts.splice(0, 1);
|
||||
|
||||
|
||||
callback(null, {
|
||||
'topic_name':topicData.title,
|
||||
'category_name':categoryData.name,
|
||||
@@ -364,7 +418,7 @@ marked.setOptions({
|
||||
if (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
|
||||
var topicData = results[0],
|
||||
hasRead = results[1],
|
||||
teaser = results[2];
|
||||
@@ -375,7 +429,7 @@ marked.setOptions({
|
||||
topicData.teaser_username = teaser.username || '';
|
||||
topicData.teaser_timestamp = teaser.timestamp ? utils.relativeTime(teaser.timestamp) : '';
|
||||
topicData.teaser_userpicture = teaser.picture;
|
||||
|
||||
|
||||
callback(topicData);
|
||||
});
|
||||
}
|
||||
@@ -417,7 +471,7 @@ marked.setOptions({
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Topics.markAllRead = function(uid, callback) {
|
||||
RDB.smembers('topics:tid', function(err, tids) {
|
||||
if(err) {
|
||||
@@ -425,13 +479,13 @@ marked.setOptions({
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(tids && tids.length) {
|
||||
for(var i=0; i<tids.length; ++i) {
|
||||
Topics.markAsRead(tids[i], uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
@@ -445,15 +499,15 @@ marked.setOptions({
|
||||
}
|
||||
|
||||
Topics.markUnRead = function(tid) {
|
||||
RDB.del('tid:' + tid + ':read_by_uid');
|
||||
RDB.del('tid:' + tid + ':read_by_uid');
|
||||
}
|
||||
|
||||
Topics.markAsRead = function(tid, uid) {
|
||||
|
||||
|
||||
RDB.sadd(schema.topics(tid).read_by_uid, uid);
|
||||
|
||||
|
||||
Topics.getTopicField(tid, 'cid', function(err, cid) {
|
||||
|
||||
|
||||
categories.isTopicsRead(cid, uid, function(read) {
|
||||
if(read) {
|
||||
categories.markAsRead(cid, uid);
|
||||
@@ -466,9 +520,9 @@ marked.setOptions({
|
||||
var batch = RDB.multi();
|
||||
|
||||
for (var i=0, ii=tids.length; i<ii; i++) {
|
||||
batch.sismember(schema.topics(tids[i]).read_by_uid, uid);
|
||||
batch.sismember(schema.topics(tids[i]).read_by_uid, uid);
|
||||
}
|
||||
|
||||
|
||||
batch.exec(function(err, hasRead) {
|
||||
callback(hasRead);
|
||||
});
|
||||
@@ -483,9 +537,9 @@ marked.setOptions({
|
||||
console.log(err);
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Topics.getTeasers = function(tids, callback) {
|
||||
var teasers = [];
|
||||
if (Array.isArray(tids)) {
|
||||
@@ -506,7 +560,10 @@ marked.setOptions({
|
||||
if (!err) {
|
||||
posts.getPostFields(pid, ['content', 'uid', 'timestamp'], function(postData) {
|
||||
|
||||
user.getUserFields(postData.uid, ['username', 'picture'], function(userData) {
|
||||
user.getUserFields(postData.uid, ['username', 'picture'], function(err, userData) {
|
||||
if(err)
|
||||
return callback(err, null);
|
||||
|
||||
var stripped = postData.content,
|
||||
timestamp = postData.timestamp;
|
||||
|
||||
@@ -532,34 +589,34 @@ marked.setOptions({
|
||||
type: 'error',
|
||||
timeout: 2000,
|
||||
title: 'Title too short',
|
||||
message: "Please enter a longer title. At least " + config.minimumTitleLength + " characters.",
|
||||
message: "Please enter a longer title. At least " + meta.config.minimumTitleLength + " characters.",
|
||||
alert_id: 'post_error'
|
||||
});
|
||||
}
|
||||
|
||||
Topics.post = function(uid, title, content, category_id, images, callback) {
|
||||
if (!category_id)
|
||||
if (!category_id)
|
||||
throw new Error('Attempted to post without a category_id');
|
||||
|
||||
if(content)
|
||||
|
||||
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 < config.minimumTitleLength) {
|
||||
} else if(!title || title.length < meta.config.minimumTitleLength) {
|
||||
callback(new Error('title-too-short'), null);
|
||||
return;
|
||||
} else if (!content || content.length < config.miminumPostLength) {
|
||||
} else if (!content || content.length < meta.config.miminumPostLength) {
|
||||
callback(new Error('content-too-short'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'lastposttime', function(lastposttime) {
|
||||
|
||||
if(Date.now() - lastposttime < config.postDelay) {
|
||||
user.getUserField(uid, 'lastposttime', function(err, lastposttime) {
|
||||
if (err) lastposttime = 0;
|
||||
if(Date.now() - lastposttime < meta.config.postDelay) {
|
||||
callback(new Error('too-many-posts'), null);
|
||||
return;
|
||||
}
|
||||
@@ -590,9 +647,9 @@ marked.setOptions({
|
||||
'postcount': 0,
|
||||
'locked': 0,
|
||||
'deleted': 0,
|
||||
'pinned': 0
|
||||
'pinned': 0
|
||||
});
|
||||
|
||||
|
||||
topicSearch.index(title, tid);
|
||||
RDB.set('topicslug:' + slug + ':tid', tid);
|
||||
|
||||
@@ -613,7 +670,6 @@ marked.setOptions({
|
||||
|
||||
posts.create(uid, tid, content, images, function(postData) {
|
||||
if (postData) {
|
||||
RDB.lpush(schema.topics(tid).posts, postData.pid);
|
||||
|
||||
// Auto-subscribe the post creator to the newly created topic
|
||||
threadTools.toggleFollow(tid, uid);
|
||||
@@ -634,16 +690,9 @@ marked.setOptions({
|
||||
Topics.getTopicField = function(tid, field, callback) {
|
||||
RDB.hget('topic:' + tid, field, callback);
|
||||
}
|
||||
|
||||
|
||||
Topics.getTopicFields = function(tid, fields, callback) {
|
||||
RDB.hmgetObject('topic:' + tid, fields, function(err, data) {
|
||||
if(err === null) {
|
||||
callback(data);
|
||||
}
|
||||
else {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
RDB.hmgetObject('topic:' + tid, fields, callback);
|
||||
}
|
||||
|
||||
Topics.setTopicField = function(tid, field, value) {
|
||||
@@ -661,10 +710,10 @@ marked.setOptions({
|
||||
}
|
||||
|
||||
Topics.updateTimestamp = function(tid, timestamp) {
|
||||
RDB.zadd(schema.topics().recent, timestamp, tid);
|
||||
RDB.zadd('topics:recent', timestamp, tid);
|
||||
Topics.setTopicField(tid, 'lastposttime', timestamp);
|
||||
}
|
||||
|
||||
|
||||
Topics.addPostToTopic = function(tid, pid) {
|
||||
RDB.rpush('tid:' + tid + ':posts', pid);
|
||||
}
|
||||
@@ -673,6 +722,18 @@ marked.setOptions({
|
||||
RDB.lrange('tid:' + tid + ':posts', 0, -1, callback);
|
||||
}
|
||||
|
||||
Topics.delete = function(tid) {
|
||||
Topics.setTopicField(tid, 'deleted', 1);
|
||||
RDB.zrem('topics:recent', tid);
|
||||
}
|
||||
|
||||
Topics.restore = function(tid) {
|
||||
Topics.setTopicField(tid, 'deleted', 0);
|
||||
Topics.getTopicField(tid, 'lastposttime', function(err, lastposttime) {
|
||||
RDB.zadd('topics:recent', lastposttime, tid);
|
||||
});
|
||||
}
|
||||
|
||||
Topics.reIndexTopic = function(tid, callback) {
|
||||
Topics.getPids(tid, function(err, pids) {
|
||||
if(err) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
var RDB = require('./redis.js'),
|
||||
async = require('async');
|
||||
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
user = require('./user');
|
||||
|
||||
|
||||
function upgradeCategory(cid, callback) {
|
||||
RDB.type('categories:'+ cid +':tid', function(err, type) {
|
||||
if (type === 'set') {
|
||||
RDB.smembers('categories:' + cid + ':tid', function(err, tids) {
|
||||
|
||||
|
||||
function moveTopic(tid, callback) {
|
||||
RDB.hget('topic:' + tid, 'timestamp', function(err, timestamp) {
|
||||
if(err)
|
||||
@@ -17,81 +18,83 @@ function upgradeCategory(cid, callback) {
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.each(tids, moveTopic, function(err) {
|
||||
if(!err) {
|
||||
console.log('renaming ' + cid);
|
||||
RDB.rename('temp_categories:' + cid + ':tid', 'categories:' + cid + ':tid');
|
||||
callback(null);
|
||||
}
|
||||
else
|
||||
else
|
||||
callback(err);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
} else {
|
||||
console.log('category already upgraded '+ cid);
|
||||
winston.info('category already upgraded '+ cid);
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function upgradeUser(uid, callback) {
|
||||
RDB.hmgetObject('user:' + uid, ['joindate', 'postcount', 'reputation'], function(err, userData) {
|
||||
user.getUserFields(uid, ['joindate', 'postcount', 'reputation'], function(err, userData) {
|
||||
if(err)
|
||||
return callback(err);
|
||||
|
||||
|
||||
RDB.zadd('users:joindate', userData.joindate, uid);
|
||||
RDB.zadd('users:postcount', userData.postcount, uid);
|
||||
RDB.zadd('users:reputation', userData.reputation, uid);
|
||||
|
||||
callback(null);
|
||||
|
||||
callback(null);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
exports.upgrade = function() {
|
||||
|
||||
console.log('upgrading nodebb now');
|
||||
|
||||
winston.info('upgrading nodebb now');
|
||||
|
||||
var schema = [
|
||||
function upgradeCategories(next) {
|
||||
console.log('upgrading categories');
|
||||
|
||||
winston.info('upgrading categories');
|
||||
|
||||
RDB.lrange('categories:cid', 0, -1, function(err, cids) {
|
||||
|
||||
|
||||
async.each(cids, upgradeCategory, function(err) {
|
||||
if(!err)
|
||||
next(null, 'upgraded categories');
|
||||
else
|
||||
if(!err) {
|
||||
winston.info('upgraded categories');
|
||||
next(null, null);
|
||||
} else {
|
||||
next(err, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
function upgradeUsers(next) {
|
||||
console.log('upgrading users');
|
||||
|
||||
winston.info('upgrading users');
|
||||
|
||||
RDB.lrange('userlist', 0, -1, function(err, uids) {
|
||||
|
||||
|
||||
async.each(uids, upgradeUser, function(err) {
|
||||
if(!err)
|
||||
next(null, 'upgraded users');
|
||||
else
|
||||
if(!err) {
|
||||
winston.info('upgraded users')
|
||||
next(null, null);
|
||||
} else {
|
||||
next(err, null);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
async.series(schema, function(err, results) {
|
||||
if(!err)
|
||||
console.log('upgrade complete');
|
||||
else
|
||||
console.log(err);
|
||||
|
||||
winston.info('upgrade complete');
|
||||
else
|
||||
winston.err(err);
|
||||
|
||||
process.exit();
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
140
src/user.js
140
src/user.js
@@ -2,7 +2,8 @@ var utils = require('./../public/src/utils.js'),
|
||||
RDB = require('./redis.js'),
|
||||
crypto = require('crypto'),
|
||||
emailjs = require('emailjs'),
|
||||
emailjsServer = emailjs.server.connect(config.mailer),
|
||||
meta = require('./meta.js'),
|
||||
emailjsServer = emailjs.server.connect(meta.config.mailer),
|
||||
bcrypt = require('bcrypt'),
|
||||
marked = require('marked'),
|
||||
notifications = require('./notifications.js'),
|
||||
@@ -41,14 +42,14 @@ var utils = require('./../public/src/utils.js'),
|
||||
} else next();
|
||||
}
|
||||
], function(err, results) {
|
||||
if (err) return callback(err, 0); // FIXME: Maintaining the 0 for backwards compatibility. Do we need this?
|
||||
if (err) return callback(err, null);
|
||||
|
||||
RDB.incr('global:next_user_id', function(err, uid) {
|
||||
RDB.handle(err);
|
||||
|
||||
var gravatar = User.createGravatarURLFromEmail(email);
|
||||
var timestamp = Date.now();
|
||||
|
||||
|
||||
RDB.hmset('user:'+uid, {
|
||||
'uid': uid,
|
||||
'username' : username,
|
||||
@@ -63,13 +64,15 @@ var utils = require('./../public/src/utils.js'),
|
||||
'picture': gravatar,
|
||||
'gravatarpicture' : gravatar,
|
||||
'uploadedpicture': '',
|
||||
'profileviews': 0,
|
||||
'reputation': 0,
|
||||
'postcount': 0,
|
||||
'lastposttime': 0,
|
||||
'administrator': (uid == 1) ? 1 : 0,
|
||||
'banned': 0,
|
||||
'showemail': 0
|
||||
});
|
||||
|
||||
|
||||
RDB.set('username:' + username + ':uid', uid);
|
||||
RDB.set('userslug:'+ userslug +':uid', uid);
|
||||
|
||||
@@ -87,7 +90,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
RDB.zadd('users:joindate', timestamp, uid);
|
||||
RDB.zadd('users:postcount', 0, uid);
|
||||
RDB.zadd('users:reputation', 0, uid);
|
||||
|
||||
|
||||
io.sockets.emit('user.latest', {userslug: userslug, username: username});
|
||||
|
||||
if (password !== undefined) {
|
||||
@@ -100,7 +103,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
User.delete = function(uid, callback) {
|
||||
RDB.exists('user:'+uid, function(err, exists) {
|
||||
if(exists === 1) {
|
||||
@@ -126,25 +129,21 @@ var utils = require('./../public/src/utils.js'),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
User.ban = function(uid, callback) {
|
||||
User.setUserField(uid, 'banned', 1, callback);
|
||||
}
|
||||
|
||||
User.unban = function(uid, callback) {
|
||||
User.setUserField(uid, 'banned', 0, callback);
|
||||
}
|
||||
|
||||
User.getUserField = function(uid, field, callback) {
|
||||
RDB.hget('user:' + uid, field, function(err, data) {
|
||||
if(err === null) {
|
||||
callback(data);
|
||||
} else {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
RDB.hget('user:' + uid, field, callback);
|
||||
}
|
||||
|
||||
User.getUserFields = function(uid, fields, callback) {
|
||||
RDB.hmgetObject('user:' + uid, fields, function(err, data) {
|
||||
if(err === null) {
|
||||
callback(data);
|
||||
} else {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
RDB.hmgetObject('user:' + uid, fields, callback);
|
||||
}
|
||||
|
||||
User.getMultipleUserFields = function(uids, fields, callback) {
|
||||
@@ -160,7 +159,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
});
|
||||
|
||||
function iterator(uid, callback) {
|
||||
User.getUserFields(uid, fields, function(userData) {
|
||||
User.getUserFields(uid, fields, function(err, userData) {
|
||||
if(err)
|
||||
return callback(err);
|
||||
returnData.push(userData);
|
||||
callback(null);
|
||||
});
|
||||
@@ -190,6 +191,12 @@ var utils = require('./../public/src/utils.js'),
|
||||
});
|
||||
}
|
||||
|
||||
User.filterBannedUsers = function(users) {
|
||||
return users.filter(function(user) {
|
||||
return (!user.banned || user.banned === '0');
|
||||
});
|
||||
}
|
||||
|
||||
User.updateProfile = function(uid, data, callback) {
|
||||
|
||||
var fields = ['email', 'fullname', 'website', 'location', 'birthday', 'signature'];
|
||||
@@ -199,30 +206,30 @@ var utils = require('./../public/src/utils.js'),
|
||||
if(data['signature'] !== undefined && data['signature'].length > 150) {
|
||||
next({error:'Signature can\'t be longer than 150 characters!'}, false);
|
||||
} else {
|
||||
next(null, true);
|
||||
}
|
||||
next(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isEmailAvailable(next) {
|
||||
if(!data['email']) {
|
||||
return next(null, true);
|
||||
}
|
||||
|
||||
User.getUserField(uid, 'email', function(email) {
|
||||
User.getUserField(uid, 'email', function(err, email) {
|
||||
if(email !== data['email']) {
|
||||
User.isEmailAvailable(data['email'], function(available) {
|
||||
if(!available) {
|
||||
if(!available) {
|
||||
next({error:'Email not available!'}, false);
|
||||
} else {
|
||||
next(null, true);
|
||||
next(null, true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next(null, true);
|
||||
next(null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.series([isSignatureValid, isEmailAvailable], function(err, results) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
@@ -245,8 +252,11 @@ var utils = require('./../public/src/utils.js'),
|
||||
if(field === 'email') {
|
||||
var gravatarpicture = User.createGravatarURLFromEmail(data[field]);
|
||||
User.setUserField(uid, 'gravatarpicture', gravatarpicture);
|
||||
User.getUserFields(uid, ['email', 'picture', 'uploadedpicture'], function(userData) {
|
||||
RDB.del('email:' + userData['email'] + ':uid');
|
||||
User.getUserFields(uid, ['email', 'picture', 'uploadedpicture'], function(err, userData) {
|
||||
if(err)
|
||||
return callback(err);
|
||||
|
||||
RDB.del('email:' + userData['email'] + ':uid');
|
||||
RDB.set('email:' + data['email'] + ':uid', uid);
|
||||
User.setUserField(uid, field, data[field]);
|
||||
if(userData.picture !== userData.uploadedpicture) {
|
||||
@@ -259,9 +269,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
return;
|
||||
} else if(field === 'signature') {
|
||||
data[field] = utils.strip_tags(data[field]);
|
||||
}
|
||||
}
|
||||
|
||||
User.setUserField(uid, field, data[field]);
|
||||
User.setUserField(uid, field, data[field]);
|
||||
|
||||
callback(null);
|
||||
} else {
|
||||
@@ -285,10 +295,10 @@ var utils = require('./../public/src/utils.js'),
|
||||
User.changePassword = function(uid, data, callback) {
|
||||
if(!utils.isPasswordValid(data.newPassword)) {
|
||||
callback({err:'Invalid password!'});
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
User.getUserField(uid, 'password', function(user_password) {
|
||||
User.getUserField(uid, 'password', function(err, user_password) {
|
||||
bcrypt.compare(data.currentPassword, user_password, function(err, res) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
@@ -309,8 +319,8 @@ var utils = require('./../public/src/utils.js'),
|
||||
});
|
||||
}
|
||||
|
||||
User.setUserField = function(uid, field, value) {
|
||||
RDB.hset('user:' + uid, field, value);
|
||||
User.setUserField = function(uid, field, value, callback) {
|
||||
RDB.hset('user:' + uid, field, value, callback);
|
||||
}
|
||||
|
||||
User.setUserFields = function(uid, data) {
|
||||
@@ -327,12 +337,12 @@ var utils = require('./../public/src/utils.js'),
|
||||
|
||||
User.getUsers = function(set, start, stop, callback) {
|
||||
var data = [];
|
||||
|
||||
|
||||
RDB.zrevrange(set, start, stop, function(err, uids) {
|
||||
if(err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
|
||||
|
||||
function iterator(uid, callback) {
|
||||
User.getUserData(uid, function(userData) {
|
||||
if(userData) {
|
||||
@@ -341,11 +351,10 @@ var utils = require('./../public/src/utils.js'),
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.eachSeries(uids, iterator, function(err) {
|
||||
callback(err, data);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -355,13 +364,13 @@ var utils = require('./../public/src/utils.js'),
|
||||
default: 'identicon',
|
||||
rating: 'pg'
|
||||
};
|
||||
|
||||
|
||||
if (!email) {
|
||||
email = '';
|
||||
options.forcedefault = 'y';
|
||||
}
|
||||
|
||||
return require('gravatar').url(email, options, https=global.nconf.get('https'));
|
||||
return require('gravatar').url(email, options, https=nconf.get('https'));
|
||||
}
|
||||
|
||||
User.hashPassword = function(password, callback) {
|
||||
@@ -370,7 +379,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
return;
|
||||
}
|
||||
|
||||
bcrypt.genSalt(config.bcrypt_rounds, function(err, salt) {
|
||||
bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) {
|
||||
bcrypt.hash(password, salt, function(err, hash) {
|
||||
callback(hash);
|
||||
});
|
||||
@@ -406,7 +415,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
User.incrementUserFieldBy(uid, 'postcount', 1, function(err, newpostcount) {
|
||||
RDB.zadd('users:postcount', newpostcount, uid);
|
||||
});
|
||||
|
||||
|
||||
User.setUserField(uid, 'lastposttime', timestamp);
|
||||
|
||||
User.sendPostNotificationToFollowers(uid, tid, pid);
|
||||
@@ -435,9 +444,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
}
|
||||
|
||||
User.sendConfirmationEmail = function (email) {
|
||||
if (global.config['email:host'] && global.config['email:port'] && global.config['email:from']) {
|
||||
if (meta.config['email:host'] && meta.config['email:port'] && meta.config['email:from']) {
|
||||
var confirm_code = utils.generateUUID(),
|
||||
confirm_link = config.url + 'confirm/' + confirm_code,
|
||||
confirm_link = nconf.get('url') + 'confirm/' + confirm_code,
|
||||
confirm_email = global.templates['emails/header'] + global.templates['emails/email_confirm'].parse({'CONFIRM_LINK': confirm_link}) + global.templates['emails/footer'],
|
||||
confirm_email_plaintext = global.templates['emails/email_confirm_plaintext'].parse({ 'CONFIRM_LINK': confirm_link });
|
||||
|
||||
@@ -451,10 +460,10 @@ var utils = require('./../public/src/utils.js'),
|
||||
RDB.set(confirm_key, email);
|
||||
RDB.expire(confirm_key, expiry_time);
|
||||
|
||||
// Send intro email w/ confirm code
|
||||
// Send intro email w/ confirm code
|
||||
var message = emailjs.message.create({
|
||||
text: confirm_email_plaintext,
|
||||
from: config.mailer.from,
|
||||
from: meta.config.mailer.from,
|
||||
to: email,
|
||||
subject: '[NodeBB] Registration Email Verification',
|
||||
attachment: [
|
||||
@@ -562,19 +571,19 @@ var utils = require('./../public/src/utils.js'),
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async.eachSeries(uids, iterator, function(err) {
|
||||
callback(returnData);
|
||||
});
|
||||
}
|
||||
|
||||
User.sendPostNotificationToFollowers = function(uid, tid, pid) {
|
||||
User.getUserField(uid, 'username', function(username) {
|
||||
User.getUserField(uid, 'username', function(err, username) {
|
||||
RDB.smembers('followers:' + uid, function(err, followers) {
|
||||
topics.getTopicField(tid, 'slug', function(err, slug) {
|
||||
var message = username + ' made a new post';
|
||||
|
||||
notifications.create(message, 5, global.nconf.get('url') + 'topic/' + slug + '#' + pid, 'notification_'+ Date.now(), function(nid) {
|
||||
notifications.create(message, 5, nconf.get('url') + 'topic/' + slug + '#' + pid, 'notification_'+ Date.now(), function(nid) {
|
||||
notifications.push(nid, followers);
|
||||
});
|
||||
});
|
||||
@@ -610,8 +619,9 @@ var utils = require('./../public/src/utils.js'),
|
||||
RDB.zrevrange('users:joindate', 0, 0, function(err, uid) {
|
||||
RDB.handle(err);
|
||||
|
||||
User.getUserFields(uid, ['username', 'userslug'], function(userData) {
|
||||
socket.emit('user.latest', {userslug: userData.userslug, username: userData.username});
|
||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||
if(!err && userData)
|
||||
socket.emit('user.latest', {userslug: userData.userslug, username: userData.username});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -642,14 +652,14 @@ var utils = require('./../public/src/utils.js'),
|
||||
}
|
||||
|
||||
function iterator(uid, callback) {
|
||||
User.getUserField(uid, 'username', function(username) {
|
||||
User.getUserField(uid, 'username', function(err, username) {
|
||||
usernames.push(username);
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
async.eachSeries(uids, iterator, function(err) {
|
||||
callback(usernames);
|
||||
callback(usernames);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -661,14 +671,14 @@ var utils = require('./../public/src/utils.js'),
|
||||
}
|
||||
|
||||
function iterator(uid, callback) {
|
||||
User.getUserField(uid, 'userslug', function(userslug) {
|
||||
User.getUserField(uid, 'userslug', function(err, userslug) {
|
||||
userslugs.push(userslug);
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
async.eachSeries(uids, iterator, function(err) {
|
||||
callback(userslugs);
|
||||
callback(userslugs);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -777,6 +787,7 @@ var utils = require('./../public/src/utils.js'),
|
||||
|
||||
User.reset = {
|
||||
validate: function(socket, code, callback) {
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
callback = null;
|
||||
}
|
||||
@@ -826,13 +837,13 @@ var utils = require('./../public/src/utils.js'),
|
||||
RDB.set('reset:' + reset_code + ':uid', uid);
|
||||
RDB.set('reset:' + reset_code + ':expiry', (60*60)+new Date()/1000|0); // Active for one hour
|
||||
|
||||
var reset_link = config.url + 'reset/' + reset_code,
|
||||
var reset_link = nconf.get('url') + 'reset/' + reset_code,
|
||||
reset_email = global.templates['emails/reset'].parse({'RESET_LINK': reset_link}),
|
||||
reset_email_plaintext = global.templates['emails/reset_plaintext'].parse({ 'RESET_LINK': reset_link });
|
||||
|
||||
var message = emailjs.message.create({
|
||||
text: reset_email_plaintext,
|
||||
from: config.mailer.from,
|
||||
from: meta.config.mailer?meta.config.mailer.from:'localhost@example.org',
|
||||
to: email,
|
||||
subject: 'Password Reset Requested',
|
||||
attachment: [
|
||||
@@ -869,14 +880,17 @@ var utils = require('./../public/src/utils.js'),
|
||||
});
|
||||
},
|
||||
commit: function(socket, code, password) {
|
||||
this.validate(code, function(validated) {
|
||||
this.validate(socket, code, function(validated) {
|
||||
if (validated) {
|
||||
RDB.get('reset:' + code + ':uid', function(err, uid) {
|
||||
if (err) {
|
||||
RDB.handle(err);
|
||||
}
|
||||
|
||||
User.setUserField(uid, 'password', password);
|
||||
User.hashPassword(password, function(hash) {
|
||||
User.setUserField(uid, 'password', hash);
|
||||
});
|
||||
|
||||
RDB.del('reset:' + code + ':uid');
|
||||
RDB.del('reset:' + code + ':expiry');
|
||||
|
||||
|
||||
107
src/webserver.js
107
src/webserver.js
@@ -5,12 +5,12 @@ var express = require('express'),
|
||||
RedisStore = require('connect-redis')(express),
|
||||
path = require('path'),
|
||||
redis = require('redis'),
|
||||
redisServer = redis.createClient(global.nconf.get('redis:port'), global.nconf.get('redis:host')),
|
||||
redisServer = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host')),
|
||||
marked = require('marked'),
|
||||
utils = require('../public/src/utils.js'),
|
||||
pkg = require('../package.json'),
|
||||
fs = require('fs'),
|
||||
|
||||
|
||||
user = require('./user.js'),
|
||||
categories = require('./categories.js'),
|
||||
posts = require('./posts.js'),
|
||||
@@ -26,7 +26,7 @@ var express = require('express'),
|
||||
|
||||
(function(app) {
|
||||
var templates = null;
|
||||
|
||||
|
||||
/**
|
||||
* `options` object requires: req, res
|
||||
* accepts: metaTags
|
||||
@@ -36,15 +36,15 @@ var express = require('express'),
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
|
||||
{ name: 'content-type', content: 'text/html; charset=UTF-8' },
|
||||
{ name: 'apple-mobile-web-app-capable', content: 'yes' },
|
||||
{ property: 'og:site_name', content: global.config.title || 'NodeBB' },
|
||||
{ property: 'og:site_name', content: meta.config.title || 'NodeBB' },
|
||||
],
|
||||
metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])),
|
||||
templateValues = {
|
||||
cssSrc: global.config['theme:src'] || global.nconf.get('relative_path') + '/vendor/bootstrap/css/bootstrap.min.css',
|
||||
title: global.config['title'] || 'NodeBB',
|
||||
browserTitle: global.config['title'] || 'NodeBB',
|
||||
cssSrc: meta.config['theme:src'] || nconf.get('relative_path') + '/vendor/bootstrap/css/bootstrap.min.css',
|
||||
title: meta.config['title'] || 'NodeBB',
|
||||
browserTitle: meta.config['title'] || 'NodeBB',
|
||||
csrf: options.res.locals.csrf_token,
|
||||
relative_path: global.nconf.get('relative_path'),
|
||||
relative_path: nconf.get('relative_path'),
|
||||
meta_tags: metaString
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ var express = require('express'),
|
||||
// Middlewares
|
||||
app.use(express.favicon(path.join(__dirname, '../', 'public', 'favicon.ico')));
|
||||
app.use(require('less-middleware')({ src: path.join(__dirname, '../', 'public') }));
|
||||
app.use(global.nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public')));
|
||||
app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public')));
|
||||
app.use(express.bodyParser()); // Puts POST vars in request.body
|
||||
app.use(express.cookieParser()); // If you want to parse cookies (res.cookies)
|
||||
app.use(express.compress());
|
||||
@@ -69,8 +69,11 @@ var express = require('express'),
|
||||
client: redisServer,
|
||||
ttl: 60*60*24*14
|
||||
}),
|
||||
secret: global.nconf.get('secret'),
|
||||
key: 'express.sid'
|
||||
secret: nconf.get('secret'),
|
||||
key: 'express.sid',
|
||||
cookie: {
|
||||
maxAge: 60*60*24*30 // 30 days
|
||||
}
|
||||
}));
|
||||
app.use(express.csrf());
|
||||
app.use(function(req, res, next) {
|
||||
@@ -83,11 +86,11 @@ var express = require('express'),
|
||||
}
|
||||
|
||||
auth.initialize(app);
|
||||
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
|
||||
global.nconf.set('https', req.secure);
|
||||
|
||||
|
||||
nconf.set('https', req.secure);
|
||||
|
||||
// Don't bother with session handling for API requests
|
||||
if (/^\/api\//.test(req.url)) return next();
|
||||
|
||||
@@ -100,7 +103,7 @@ var express = require('express'),
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
app.use(app.router);
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
@@ -108,41 +111,40 @@ var express = require('express'),
|
||||
|
||||
// respond with html page
|
||||
if (req.accepts('html')) {
|
||||
|
||||
//res.json('404', { url: req.url });
|
||||
res.redirect(global.nconf.get('relative_path') + '/404');
|
||||
res.redirect(nconf.get('relative_path') + '/404');
|
||||
return;
|
||||
}
|
||||
|
||||
// respond with json
|
||||
if (req.accepts('json')) {
|
||||
console.log('sending 404 json');
|
||||
res.send({ error: 'Not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// default to plain-text. send()
|
||||
res.type('txt').send('Not found');
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
|
||||
// we may use properties of the error object
|
||||
// here and next(err) appropriately, or if
|
||||
// we possibly recovered from the error, simply next().
|
||||
console.error(err.stack);
|
||||
|
||||
|
||||
res.status(err.status || 500);
|
||||
|
||||
|
||||
res.json('500', { error: err.message });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.create_route = function(url, tpl) { // to remove
|
||||
return '<script>templates.ready(function(){ajaxify.go("' + url + '", null, "' + tpl + '");});</script>';
|
||||
};
|
||||
|
||||
|
||||
app.namespace(global.nconf.get('relative_path'), function() {
|
||||
|
||||
app.namespace(nconf.get('relative_path'), function() {
|
||||
|
||||
auth.create_routes(app);
|
||||
admin.create_routes(app);
|
||||
@@ -150,20 +152,20 @@ var express = require('express'),
|
||||
installRoute.create_routes(app);
|
||||
testBed.create_routes(app);
|
||||
apiRoute.create_routes(app);
|
||||
|
||||
|
||||
|
||||
|
||||
// Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section)
|
||||
(function() {
|
||||
var routes = ['login', 'register', 'account', 'recent', 'unread', 'popular', 'active', '403', '404'];
|
||||
|
||||
|
||||
for (var i=0, ii=routes.length; i<ii; i++) {
|
||||
(function(route) {
|
||||
|
||||
|
||||
app.get('/' + route, function(req, res) {
|
||||
if ((route === 'login' || route ==='register') && (req.user && req.user.uid > 0)) {
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function(userslug) {
|
||||
res.redirect('/users/'+userslug);
|
||||
|
||||
user.getUserField(req.user.uid, 'userslug', function(err, userslug) {
|
||||
res.redirect('/users/'+userslug);
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -175,7 +177,7 @@ var express = require('express'),
|
||||
}(routes[i]));
|
||||
}
|
||||
}());
|
||||
|
||||
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
async.parallel({
|
||||
@@ -184,9 +186,9 @@ var express = require('express'),
|
||||
req: req,
|
||||
res: res,
|
||||
metaTags: [
|
||||
{ name: "title", content: global.config.title || 'NodeBB' },
|
||||
{ name: "description", content: global.config.description || '' },
|
||||
{ property: 'og:title', content: 'Index | ' + (global.config.title || 'NodeBB') },
|
||||
{ name: "title", content: meta.config.title || 'NodeBB' },
|
||||
{ name: "description", content: meta.config.description || '' },
|
||||
{ property: 'og:title', content: 'Index | ' + (meta.config.title || 'NodeBB') },
|
||||
{ property: "og:type", content: 'website' }
|
||||
]
|
||||
}, next);
|
||||
@@ -205,9 +207,10 @@ var express = require('express'),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.get('/topic/:topic_id/:slug?', function(req, res) {
|
||||
|
||||
var tid = req.params.topic_id;
|
||||
if (tid.match(/^\d+\.rss$/)) {
|
||||
fs.readFile('feeds/topics/' + tid, function (err, data) {
|
||||
@@ -215,7 +218,7 @@ var express = require('express'),
|
||||
res.type('text').send(404, "Unable to locate an rss feed at this location.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
res.type('xml').set('Content-Length', data.length).send(data);
|
||||
});
|
||||
return;
|
||||
@@ -224,13 +227,19 @@ var express = require('express'),
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), function(err, topicData) {
|
||||
if(topicData) {
|
||||
if(topicData.deleted === '1' && topicData.expose_tools === 0)
|
||||
return next(new Error('Topic deleted'), null);
|
||||
}
|
||||
|
||||
next(err, topicData);
|
||||
});
|
||||
},
|
||||
function(topicData, next) {
|
||||
var posts = topicData.posts.push(topicData.main_posts[0]),
|
||||
var posts = topicData.posts.push(topicData.main_posts[0]),
|
||||
lastMod = 0,
|
||||
timestamp;
|
||||
|
||||
for(var x=0,numPosts=topicData.posts.length;x<numPosts;x++) {
|
||||
timestamp = parseInt(topicData.posts[x].timestamp, 10);
|
||||
if (timestamp > lastMod) lastMod = timestamp;
|
||||
@@ -241,9 +250,9 @@ var express = require('express'),
|
||||
res: res,
|
||||
metaTags: [
|
||||
{ name: "title", content: topicData.topic_name },
|
||||
{ property: 'og:title', content: topicData.topic_name + ' | ' + (global.config.title || 'NodeBB') },
|
||||
{ property: 'og:title', content: topicData.topic_name + ' | ' + (meta.config.title || 'NodeBB') },
|
||||
{ property: "og:type", content: 'article' },
|
||||
{ property: "og:url", content: global.nconf.get('url') + 'topic/' + topicData.slug },
|
||||
{ property: "og:url", content: nconf.get('url') + 'topic/' + topicData.slug },
|
||||
{ property: 'og:image', content: topicData.main_posts[0].picture },
|
||||
{ property: "article:published_time", content: new Date(parseInt(topicData.main_posts[0].timestamp, 10)).toISOString() },
|
||||
{ property: 'article:modified_time', content: new Date(lastMod).toISOString() },
|
||||
@@ -271,7 +280,7 @@ var express = require('express'),
|
||||
|
||||
app.get('/category/:category_id/:slug?', function(req, res) {
|
||||
var cid = req.params.category_id;
|
||||
|
||||
|
||||
if (cid.match(/^\d+\.rss$/)) {
|
||||
fs.readFile('feeds/categories/' + cid, function (err, data) {
|
||||
if (err) {
|
||||
@@ -338,7 +347,7 @@ var express = require('express'),
|
||||
res.send( "User-agent: *\n" +
|
||||
"Disallow: \n" +
|
||||
"Disallow: /admin/\n" +
|
||||
"Sitemap: " + global.nconf.get('url') + "sitemap.xml");
|
||||
"Sitemap: " + nconf.get('url') + "sitemap.xml");
|
||||
});
|
||||
|
||||
app.get('/cid/:cid', function(req, res) {
|
||||
@@ -374,24 +383,24 @@ var express = require('express'),
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(
|
||||
header +
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("outgoing?url=' + req.query.url + '");});</script>' +
|
||||
'\n\t<script>templates.ready(function(){ajaxify.go("outgoing?url=' + encodeURIComponent(req.query.url) + '", null, null, true);});</script>' +
|
||||
templates['footer']
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/search', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route("search", null, "search") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/search/:term', function(req, res) {
|
||||
app.build_header({ req: req, res: res }, function(err, header) {
|
||||
res.send(header + app.create_route("search/"+req.params.term, null, "search") + templates['footer']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/reindex', function(req, res) {
|
||||
topics.reIndexAll(function(err) {
|
||||
if(err) {
|
||||
@@ -401,7 +410,7 @@ var express = require('express'),
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}(WebServer));
|
||||
|
||||
@@ -16,18 +16,19 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
async = require('async'),
|
||||
RedisStoreLib = require('connect-redis')(express),
|
||||
redis = require('redis'),
|
||||
redisServer = redis.createClient(global.nconf.get('redis:port'), global.nconf.get('redis:host')),
|
||||
redisServer = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host')),
|
||||
RedisStore = new RedisStoreLib({
|
||||
client: redisServer,
|
||||
ttl: 60*60*24*14
|
||||
}),
|
||||
socketCookieParser = express.cookieParser(global.nconf.get('secret')),
|
||||
socketCookieParser = express.cookieParser(nconf.get('secret')),
|
||||
admin = {
|
||||
'categories': require('./admin/categories.js'),
|
||||
'user': require('./admin/user.js')
|
||||
},
|
||||
plugins = require('./plugins');
|
||||
|
||||
plugins = require('./plugins'),
|
||||
winston = require('winston');
|
||||
|
||||
(function(io) {
|
||||
var users = {},
|
||||
userSockets = {},
|
||||
@@ -48,22 +49,22 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
|
||||
userSockets[uid] = userSockets[uid] || [];
|
||||
userSockets[uid].push(socket);
|
||||
|
||||
|
||||
if(uid) {
|
||||
socket.join('uid_' + uid);
|
||||
io.sockets.in('global').emit('api:user.isOnline', isUserOnline(uid));
|
||||
|
||||
user.getUserField(uid, 'username', function(username) {
|
||||
|
||||
user.getUserField(uid, 'username', function(err, username) {
|
||||
socket.emit('event:connect', {status: 1, username:username});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
|
||||
|
||||
var index = userSockets[uid].indexOf(socket);
|
||||
if(index !== -1) {
|
||||
userSockets[uid].splice(index, 1);
|
||||
@@ -73,17 +74,17 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
delete users[sessionID];
|
||||
if(uid)
|
||||
io.sockets.in('global').emit('api:user.isOnline', isUserOnline(uid));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for(var roomName in rooms) {
|
||||
|
||||
socket.leave(roomName);
|
||||
|
||||
if(rooms[roomName][socket.id]) {
|
||||
delete rooms[roomName][socket.id];
|
||||
}
|
||||
|
||||
updateRoomBrowsingText(roomName);
|
||||
}
|
||||
|
||||
updateRoomBrowsingText(roomName);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,7 +106,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
function getAnonymousCount(roomName) {
|
||||
var clients = io.sockets.clients(roomName);
|
||||
var anonCount = 0;
|
||||
|
||||
|
||||
for(var i=0; i<clients.length; ++i) {
|
||||
var hs = clients[i].handshake;
|
||||
|
||||
@@ -113,11 +114,11 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
++anonCount;
|
||||
}
|
||||
}
|
||||
return anonCount;
|
||||
return anonCount;
|
||||
}
|
||||
|
||||
var uids = getUidsInRoom(rooms[roomName]);
|
||||
|
||||
|
||||
var anonymousCount = getAnonymousCount(roomName);
|
||||
|
||||
function userList(users, anonymousCount, userCount) {
|
||||
@@ -126,8 +127,8 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
for (var i = 0, ii=users.length; i<ii; ++i) {
|
||||
usernames[i] = '<strong>' + '<a href="/users/'+users[i].userslug+'">' + users[i].username + '</a></strong>';
|
||||
}
|
||||
|
||||
var joiner = anonymousCount + userCount == 1 ? 'is' : 'are',
|
||||
|
||||
var joiner = anonymousCount + userCount == 1 ? 'is' : 'are',
|
||||
userList = anonymousCount > 0 ? usernames.concat(util.format('%d guest%s', anonymousCount, anonymousCount > 1 ? 's' : '')) : usernames,
|
||||
lastUser = userList.length > 1 ? ' and ' + userList.pop() : '';
|
||||
|
||||
@@ -145,18 +146,18 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
}
|
||||
|
||||
socket.on('event:enter_room', function(data) {
|
||||
|
||||
|
||||
if (data.leave !== null) {
|
||||
socket.leave(data.leave);
|
||||
}
|
||||
|
||||
|
||||
socket.join(data.enter);
|
||||
|
||||
rooms[data.enter] = rooms[data.enter] || {};
|
||||
|
||||
if (uid) {
|
||||
rooms[data.enter][socket.id] = uid;
|
||||
|
||||
|
||||
if (data.leave && rooms[data.leave] && rooms[data.leave][socket.id]) {
|
||||
delete rooms[data.leave][socket.id];
|
||||
}
|
||||
@@ -167,18 +168,20 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
|
||||
updateRoomBrowsingText(data.enter);
|
||||
|
||||
if (data.enter != 'admin')
|
||||
if (data.enter != 'admin')
|
||||
io.sockets.in('admin').emit('api:get_all_rooms', io.sockets.manager.rooms);
|
||||
|
||||
|
||||
});
|
||||
|
||||
// BEGIN: API calls (todo: organize)
|
||||
|
||||
socket.on('api:updateHeader', function(data) {
|
||||
if(uid) {
|
||||
user.getUserFields(uid, data.fields, function(fields) {
|
||||
fields.uid = uid;
|
||||
socket.emit('api:updateHeader', fields);
|
||||
user.getUserFields(uid, data.fields, function(err, fields) {
|
||||
if(!err && fields) {
|
||||
fields.uid = uid;
|
||||
socket.emit('api:updateHeader', fields);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
@@ -186,12 +189,12 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
uid:0,
|
||||
username: "Anonymous User",
|
||||
email: '',
|
||||
picture: require('gravatar').url('', {s:'24'}, https=global.nconf.get('https'))
|
||||
picture: require('gravatar').url('', {s:'24'}, https=nconf.get('https'))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
socket.on('user.exists', function(data) {
|
||||
user.exists(utils.slugify(data.username), function(exists){
|
||||
socket.emit('user.exists', {exists: exists});
|
||||
@@ -232,12 +235,12 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
|
||||
socket.on('api:user.get_online_users', function(data) {
|
||||
var returnData = [];
|
||||
|
||||
|
||||
for(var i=0; i<data.length; ++i) {
|
||||
var uid = data[i];
|
||||
if(isUserOnline(uid))
|
||||
returnData.push(uid);
|
||||
else
|
||||
else
|
||||
returnData.push(0);
|
||||
}
|
||||
socket.emit('api:user.get_online_users', returnData);
|
||||
@@ -250,30 +253,34 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
socket.on('api:user.changePassword', function(data, callback) {
|
||||
user.changePassword(uid, data, callback);
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:user.updateProfile', function(data, callback) {
|
||||
user.updateProfile(uid, data, callback);
|
||||
});
|
||||
|
||||
socket.on('api:user.changePicture', function(data, callback) {
|
||||
|
||||
|
||||
var type = data.type;
|
||||
|
||||
function updateHeader() {
|
||||
user.getUserFields(uid, ['picture'], function(fields) {
|
||||
fields.uid = uid;
|
||||
socket.emit('api:updateHeader', fields);
|
||||
callback(true);
|
||||
user.getUserFields(uid, ['picture'], function(err, fields) {
|
||||
if(!err && fields) {
|
||||
fields.uid = uid;
|
||||
socket.emit('api:updateHeader', fields);
|
||||
callback(true);
|
||||
} else {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(type === 'gravatar') {
|
||||
user.getUserField(uid, 'gravatarpicture', function(gravatar) {
|
||||
user.getUserField(uid, 'gravatarpicture', function(err, gravatar) {
|
||||
user.setUserField(uid, 'picture', gravatar);
|
||||
updateHeader();
|
||||
});
|
||||
} else if(type === 'uploaded') {
|
||||
user.getUserField(uid, 'uploadedpicture', function(uploadedpicture) {
|
||||
user.getUserField(uid, 'uploadedpicture', function(err, uploadedpicture) {
|
||||
user.setUserField(uid, 'picture', uploadedpicture);
|
||||
updateHeader();
|
||||
});
|
||||
@@ -283,7 +290,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
});
|
||||
|
||||
socket.on('api:user.follow', function(data, callback) {
|
||||
if(uid) {
|
||||
if(uid) {
|
||||
user.follow(uid, data.uid, callback);
|
||||
}
|
||||
});
|
||||
@@ -326,21 +333,21 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(result) {
|
||||
posts.getTopicPostStats(socket);
|
||||
|
||||
|
||||
socket.emit('event:alert', {
|
||||
title: 'Thank you for posting',
|
||||
message: 'You have successfully posted. Click here to view your post.',
|
||||
type: 'notify',
|
||||
timeout: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:topics.markAllRead', function(data, callback) {
|
||||
topics.markAllRead(uid, function(err, success) {
|
||||
if(!err && success) {
|
||||
@@ -361,7 +368,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
posts.reply(data.topic_id, uid, data.content, data.images, function(err, result) {
|
||||
if(err) {
|
||||
if(err.message === 'content-too-short') {
|
||||
@@ -378,20 +385,20 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(result) {
|
||||
|
||||
|
||||
posts.getTopicPostStats(socket);
|
||||
|
||||
|
||||
socket.emit('event:alert', {
|
||||
title: 'Reply Successful',
|
||||
message: 'You have successfully replied. Click here to view your reply.',
|
||||
type: 'notify',
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -477,9 +484,9 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
postTools.restore(uid, data.pid);
|
||||
});
|
||||
|
||||
socket.on('api:notifications.get', function(data) {
|
||||
socket.on('api:notifications.get', function(data, callback) {
|
||||
user.notifications.get(uid, function(notifs) {
|
||||
socket.emit('api:notifications.get', notifs);
|
||||
callback(notifs);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -499,44 +506,61 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('getChatMessages', function(data, callback) {
|
||||
var touid = data.touid;
|
||||
require('./messaging').getMessages(uid, touid, function(err, messages) {
|
||||
if(err)
|
||||
return callback(null);
|
||||
|
||||
callback(messages);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('sendChatMessage', function(data) {
|
||||
|
||||
var touid = data.touid;
|
||||
if(touid === uid || uid === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(userSockets[touid]) {
|
||||
var msg = utils.strip_tags(data.message),
|
||||
numSockets = userSockets[touid].length;
|
||||
|
||||
user.getUserField(uid, 'username', function(username) {
|
||||
user.getUserField(uid, 'username', function(err, username) {
|
||||
var finalMessage = username + ' says : ' + msg;
|
||||
|
||||
for(var x=0;x<numSockets;x++) {
|
||||
userSockets[touid][x].emit('chatMessage', {fromuid:uid, username:username, message:finalMessage});
|
||||
}
|
||||
|
||||
notifications.create(finalMessage, 5, '#', 'notification_'+uid+'_'+touid, function(nid) {
|
||||
|
||||
notifications.create(finalMessage, 5, '#', 'notification_' + uid + '_' + touid, function(nid) {
|
||||
notifications.push(nid, [touid], function(success) {
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
require('./messaging').addMessage(uid, touid, msg, function(err, message) {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:config.get', function(data) {
|
||||
meta.config.get(function(config) {
|
||||
meta.configs.get(function(config) {
|
||||
socket.emit('api:config.get', config);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:config.set', function(data) {
|
||||
meta.config.set(data.key, data.value, function(err) {
|
||||
meta.configs.set(data.key, data.value, function(err) {
|
||||
if (!err) socket.emit('api:config.set', { status: 'ok' });
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:config.remove', function(key) {
|
||||
meta.config.remove(key);
|
||||
meta.configs.remove(key);
|
||||
});
|
||||
|
||||
socket.on('api:composer.push', function(data) {
|
||||
@@ -544,7 +568,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
if (parseInt(data.tid) > 0) {
|
||||
topics.getTopicData(data.tid, function(topicData) {
|
||||
|
||||
if (data.body)
|
||||
if (data.body)
|
||||
topicData.body = data.body;
|
||||
|
||||
socket.emit('api:composer.push', {
|
||||
@@ -554,14 +578,16 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
});
|
||||
});
|
||||
} else if (parseInt(data.cid) > 0) {
|
||||
user.getUserFields(uid, ['username', 'picture'], function(userData) {
|
||||
socket.emit('api:composer.push', {
|
||||
tid: 0,
|
||||
cid: data.cid,
|
||||
username: userData.username,
|
||||
picture: userData.picture,
|
||||
title: undefined
|
||||
});
|
||||
user.getUserFields(uid, ['username', 'picture'], function(err, userData) {
|
||||
if(!err && userData) {
|
||||
socket.emit('api:composer.push', {
|
||||
tid: 0,
|
||||
cid: data.cid,
|
||||
username: userData.username,
|
||||
picture: userData.picture,
|
||||
title: undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (parseInt(data.pid) > 0) {
|
||||
async.parallel([
|
||||
@@ -627,18 +653,18 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
});
|
||||
|
||||
socket.on('api:topic.loadMore', function(data, callback) {
|
||||
var start = data.after,
|
||||
var start = data.after,
|
||||
end = start + 9;
|
||||
|
||||
|
||||
topics.getTopicPosts(data.tid, start, end, uid, function(posts) {
|
||||
callback({posts:posts});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:category.loadMore', function(data, callback) {
|
||||
var start = data.after,
|
||||
end = start + 9;
|
||||
|
||||
|
||||
categories.getCategoryTopics(data.cid, start, end, uid, function(topics) {
|
||||
callback({topics:topics});
|
||||
});
|
||||
@@ -652,11 +678,11 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
callback(latestTopics);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:topics.loadMoreUnreadTopics', function(data, callback) {
|
||||
var start = data.after,
|
||||
end = start + 9;
|
||||
|
||||
|
||||
topics.getUnreadTopics(uid, start, end, function(unreadTopics) {
|
||||
callback(unreadTopics);
|
||||
});
|
||||
@@ -665,14 +691,14 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
socket.on('api:users.loadMore', function(data, callback) {
|
||||
var start = data.after,
|
||||
end = start + 19;
|
||||
|
||||
|
||||
user.getUsers(data.set, start, end, function(err, data) {
|
||||
if(err) {
|
||||
console.log(err);
|
||||
winston.err(err);
|
||||
} else {
|
||||
callback({users:data});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:admin.topics.getMore', function(data, callback) {
|
||||
@@ -684,13 +710,13 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
socket.on('api:admin.categories.update', function(data) {
|
||||
admin.categories.update(data, socket);
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:admin.user.makeAdmin', function(theirid) {
|
||||
if(uid && uid > 0) {
|
||||
admin.user.makeAdmin(uid, theirid, socket);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
socket.on('api:admin.user.removeAdmin', function(theirid) {
|
||||
if(uid && uid > 0) {
|
||||
admin.user.removeAdmin(uid, theirid, socket);
|
||||
@@ -703,14 +729,26 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:admin.user.banUser', function(theirid) {
|
||||
if(uid && uid > 0) {
|
||||
admin.user.banUser(uid, theirid, socket);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:admin.user.unbanUser', function(theirid) {
|
||||
if(uid && uid > 0) {
|
||||
admin.user.unbanUser(uid, theirid, socket);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:admin.user.search', function(username) {
|
||||
if(uid && uid > 0) {
|
||||
user.search(username, function(data) {
|
||||
socket.emit('api:admin.user.search', data);
|
||||
});
|
||||
}
|
||||
else
|
||||
} else {
|
||||
socket.emit('api:admin.user.search', null);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:admin.themes.getInstalled', function(callback) {
|
||||
@@ -726,10 +764,10 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
||||
});
|
||||
|
||||
socket.on('api:meta.buildTitle', function(text, callback) {
|
||||
meta.title.build(text, uid, function(err, title) {
|
||||
callback(title);
|
||||
meta.title.build(text, uid, function(err, title, numNotifications) {
|
||||
callback(title, numNotifications);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}(SocketIO));
|
||||
|
||||
Reference in New Issue
Block a user