mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-16 21:40:23 +01:00
Compare commits
369 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27fce2363d | ||
|
|
01340c87bd | ||
|
|
77c2f551d3 | ||
|
|
e6bb66705d | ||
|
|
88154c3ebf | ||
|
|
1c80a1bad5 | ||
|
|
79c52dfe84 | ||
|
|
411ba3542c | ||
|
|
fa9636a62a | ||
|
|
69fefc0625 | ||
|
|
e598ffa993 | ||
|
|
d99577ffb2 | ||
|
|
40108f92c9 | ||
|
|
a42b30fd40 | ||
|
|
37497fc5a0 | ||
|
|
9bea23bbfe | ||
|
|
4e39c50144 | ||
|
|
7e50bcba0c | ||
|
|
f81c583d86 | ||
|
|
5647d55147 | ||
|
|
4ce6ac5af9 | ||
|
|
d770963b69 | ||
|
|
625b96ba73 | ||
|
|
37877ed531 | ||
|
|
1f2ef2a7e4 | ||
|
|
885eec79c3 | ||
|
|
aa1994be67 | ||
|
|
4552e6286e | ||
|
|
d7856bcd4f | ||
|
|
7ea852fae3 | ||
|
|
c67c37bb20 | ||
|
|
7d1aa02fd1 | ||
|
|
2309ab2002 | ||
|
|
b87840d4c9 | ||
|
|
b5c22c7ff7 | ||
|
|
3b3e8348e4 | ||
|
|
259ad42b31 | ||
|
|
51cb33bccc | ||
|
|
2a8a62a253 | ||
|
|
82ae80090e | ||
|
|
1d6135150f | ||
|
|
41f98d29b7 | ||
|
|
7b5a6bd3c9 | ||
|
|
fe15366524 | ||
|
|
1cbbb3873f | ||
|
|
5e1ab7989a | ||
|
|
0e5724cd2c | ||
|
|
89e9d56dee | ||
|
|
58f9c2c18d | ||
|
|
1ec6726459 | ||
|
|
6b4520e526 | ||
|
|
98f20564de | ||
|
|
4da819b02b | ||
|
|
dc90db74c0 | ||
|
|
c5bc2dd64f | ||
|
|
bbb045698d | ||
|
|
d9e364cd86 | ||
|
|
b179991be4 | ||
|
|
05de4870b0 | ||
|
|
01f0131f5d | ||
|
|
d69847c54e | ||
|
|
cc78f6f155 | ||
|
|
1e2100902c | ||
|
|
4353a9da25 | ||
|
|
e480b1bace | ||
|
|
5a96f5f64b | ||
|
|
7296b701fa | ||
|
|
a21d91d870 | ||
|
|
6931695e64 | ||
|
|
e12d02f29c | ||
|
|
404865c32e | ||
|
|
75879c47c5 | ||
|
|
f946918176 | ||
|
|
6ca3df2431 | ||
|
|
c8ec095d99 | ||
|
|
0179a55ee4 | ||
|
|
04ed1df0ef | ||
|
|
de66ee1a89 | ||
|
|
e8c4bda984 | ||
|
|
7074b75b9d | ||
|
|
857756f636 | ||
|
|
e4c62200de | ||
|
|
89ec677d54 | ||
|
|
8ff656430d | ||
|
|
db22394976 | ||
|
|
ef5548a749 | ||
|
|
14c3bb7d63 | ||
|
|
1a415b60be | ||
|
|
85fa68bd92 | ||
|
|
e41ca491ff | ||
|
|
bb5962cda0 | ||
|
|
2de878821b | ||
|
|
5bea2999ef | ||
|
|
67e9bf74a7 | ||
|
|
ed42012058 | ||
|
|
9b7c9e4a81 | ||
|
|
85daacdf7a | ||
|
|
17ea41fdae | ||
|
|
ffd2a18837 | ||
|
|
5d7f38f99f | ||
|
|
e762267e03 | ||
|
|
a71870de28 | ||
|
|
7eba0b85f4 | ||
|
|
30a45ee78e | ||
|
|
ca9cd36067 | ||
|
|
1d5a208896 | ||
|
|
60e2938b58 | ||
|
|
89540172b2 | ||
|
|
bbb716723f | ||
|
|
842cd17979 | ||
|
|
e0e32efd26 | ||
|
|
a127fcd056 | ||
|
|
c614af2cd9 | ||
|
|
c2abff6e6d | ||
|
|
4444d2ee6a | ||
|
|
463bc1374c | ||
|
|
d9a60fc2ef | ||
|
|
22a3b227a3 | ||
|
|
12f3f1a45c | ||
|
|
84d4c2944c | ||
|
|
0a9b918c75 | ||
|
|
8ddf200ce7 | ||
|
|
207ff98211 | ||
|
|
1fb09a9c8c | ||
|
|
67f9b22c86 | ||
|
|
8b5cf0c696 | ||
|
|
26d9cc56d3 | ||
|
|
930a9c8bca | ||
|
|
51a9bd9e56 | ||
|
|
ac12bd0b8f | ||
|
|
d7651d1504 | ||
|
|
7e1f996079 | ||
|
|
1af4a9abb4 | ||
|
|
ed7c9348b7 | ||
|
|
ccf4ed1235 | ||
|
|
b22ee67612 | ||
|
|
b5a953b16c | ||
|
|
7613f02eff | ||
|
|
78a3dd68ea | ||
|
|
afc0e25b26 | ||
|
|
82e14eef35 | ||
|
|
81e9c9807f | ||
|
|
02e2b53a1d | ||
|
|
babe9b6f54 | ||
|
|
52f198481b | ||
|
|
252187f1fe | ||
|
|
99812c33e7 | ||
|
|
97c5f6009d | ||
|
|
5e15f8683e | ||
|
|
fba1f7ae05 | ||
|
|
865e5ae3a3 | ||
|
|
1af98835b1 | ||
|
|
6eadf67add | ||
|
|
f3f280d008 | ||
|
|
aecbe6d316 | ||
|
|
63419d7ca9 | ||
|
|
3480a1d60e | ||
|
|
190712e250 | ||
|
|
a662330b1b | ||
|
|
04ee1d137d | ||
|
|
27f421587e | ||
|
|
f9f0bd8685 | ||
|
|
53b12f50a7 | ||
|
|
63d49463da | ||
|
|
549017d035 | ||
|
|
45feef5884 | ||
|
|
24592cc696 | ||
|
|
c640c550fd | ||
|
|
0565b7b8c8 | ||
|
|
1691c74727 | ||
|
|
f6be3eacfc | ||
|
|
790df903ac | ||
|
|
25e6f72921 | ||
|
|
ab1015b11e | ||
|
|
55e990f71d | ||
|
|
40a8150519 | ||
|
|
87744302ba | ||
|
|
17083dc5e6 | ||
|
|
b29616fbd8 | ||
|
|
468688615f | ||
|
|
f4faee4283 | ||
|
|
62c85274a3 | ||
|
|
db2917193e | ||
|
|
705571de8c | ||
|
|
2ee29683a7 | ||
|
|
51395dda91 | ||
|
|
9babef0095 | ||
|
|
e9545c9a7f | ||
|
|
619214e462 | ||
|
|
31b600686a | ||
|
|
bccc4e8019 | ||
|
|
7039a4d762 | ||
|
|
7b7f0115e5 | ||
|
|
24e79b3f4e | ||
|
|
5c70b2b307 | ||
|
|
65a8de7845 | ||
|
|
44d2297546 | ||
|
|
a41280707e | ||
|
|
a8f2fd66ae | ||
|
|
7e8ddbadfb | ||
|
|
80aeb3677d | ||
|
|
f92bbdaefa | ||
|
|
fc73f16425 | ||
|
|
c1f47f536d | ||
|
|
9166c82dc2 | ||
|
|
3decc8b9b1 | ||
|
|
bac5da30e7 | ||
|
|
baf379c6d7 | ||
|
|
e9b6cdb37a | ||
|
|
9d36d2c749 | ||
|
|
5945ab1a0a | ||
|
|
40319a66ff | ||
|
|
754aef8a84 | ||
|
|
0613b530e8 | ||
|
|
5e9819b96f | ||
|
|
b46e334a40 | ||
|
|
af49845ae6 | ||
|
|
4cb8241334 | ||
|
|
530e6cb20e | ||
|
|
e0adc03588 | ||
|
|
e48f6e6d9b | ||
|
|
04ff4df5d7 | ||
|
|
d789e96d79 | ||
|
|
45761fd48b | ||
|
|
cb6a47a5d9 | ||
|
|
67dbdfd80d | ||
|
|
dee99c1752 | ||
|
|
a0c7e187f5 | ||
|
|
938503bd56 | ||
|
|
33bda6fd16 | ||
|
|
929336cb57 | ||
|
|
883aca038b | ||
|
|
77e03dc18d | ||
|
|
230ed1ab11 | ||
|
|
b31fa856d0 | ||
|
|
98b97b9898 | ||
|
|
41cf7c6814 | ||
|
|
da3a2f436c | ||
|
|
569a7178d7 | ||
|
|
7e7497c3bd | ||
|
|
905c78d5a3 | ||
|
|
37b1cb009a | ||
|
|
56586e1fda | ||
|
|
2040fcdba4 | ||
|
|
d24b57ae86 | ||
|
|
db72102de7 | ||
|
|
e8801a75f3 | ||
|
|
918826ff48 | ||
|
|
fa2fe5c941 | ||
|
|
8ef2761f53 | ||
|
|
9375369b88 | ||
|
|
ce77c82b0c | ||
|
|
1e39ae2f1e | ||
|
|
c143894547 | ||
|
|
37450ff00c | ||
|
|
ca9c468edd | ||
|
|
d6570d1496 | ||
|
|
bf677522a9 | ||
|
|
fd89f71fc0 | ||
|
|
83477ece18 | ||
|
|
32990794ce | ||
|
|
4b5bae4f9b | ||
|
|
2b07917020 | ||
|
|
338acb8fc2 | ||
|
|
2a4b228e19 | ||
|
|
c6c3ab94b1 | ||
|
|
8671516b95 | ||
|
|
5710ab47ae | ||
|
|
e3b0eb29f1 | ||
|
|
795594b6a6 | ||
|
|
e91da53d9e | ||
|
|
f807df84d8 | ||
|
|
7f32d5741d | ||
|
|
30c7113bd8 | ||
|
|
a63732027f | ||
|
|
13d8f51f6a | ||
|
|
5d48ed5fb4 | ||
|
|
4b89b3e2ed | ||
|
|
1ee24517e3 | ||
|
|
64e35c734e | ||
|
|
08130e8088 | ||
|
|
0c5937805b | ||
|
|
d315829eaf | ||
|
|
6dad1c3bbb | ||
|
|
3577c11c89 | ||
|
|
8326c223ab | ||
|
|
73de5f78fe | ||
|
|
4e59b85073 | ||
|
|
9af26db57a | ||
|
|
8e4ca8e474 | ||
|
|
79de3976bf | ||
|
|
4b80f13373 | ||
|
|
6210c6dbf4 | ||
|
|
97592eede6 | ||
|
|
73dafa6aff | ||
|
|
6c3e121b6c | ||
|
|
95ee7fb49f | ||
|
|
b3f73eace1 | ||
|
|
07a497362a | ||
|
|
afa078d00c | ||
|
|
3fd7d9a604 | ||
|
|
80a0d2d8d8 | ||
|
|
27f4fdd179 | ||
|
|
d1a94a91c2 | ||
|
|
69a31dcdd9 | ||
|
|
a2a6bf87f7 | ||
|
|
bd1e95b655 | ||
|
|
278f9bfc03 | ||
|
|
a3a8950afd | ||
|
|
f95913e623 | ||
|
|
8e4b51fba4 | ||
|
|
84915a1843 | ||
|
|
4b0e915698 | ||
|
|
b2e81b5d17 | ||
|
|
ef47f3fd15 | ||
|
|
f88f72abd2 | ||
|
|
715c14b78d | ||
|
|
4af7da3451 | ||
|
|
63ff572076 | ||
|
|
129af904f6 | ||
|
|
8cc71d2b47 | ||
|
|
0b299b2fe7 | ||
|
|
b1cef5f73d | ||
|
|
42067ce53c | ||
|
|
2bdf12fb67 | ||
|
|
c00b138bf2 | ||
|
|
1155eaf1f1 | ||
|
|
52f2e193d6 | ||
|
|
f5619a9b29 | ||
|
|
057608bac0 | ||
|
|
57465eb277 | ||
|
|
01e04d60a9 | ||
|
|
be8d9be832 | ||
|
|
07d07020f0 | ||
|
|
7c1f7e7a23 | ||
|
|
a3f6fee41f | ||
|
|
0414ec7f83 | ||
|
|
12af2a7ff6 | ||
|
|
4d6881fa65 | ||
|
|
019e8e0d14 | ||
|
|
763bd775c4 | ||
|
|
585e07bc79 | ||
|
|
b911d8d075 | ||
|
|
d583122c64 | ||
|
|
3968877b1e | ||
|
|
46f03de9f6 | ||
|
|
0e18ec022c | ||
|
|
64117ab613 | ||
|
|
038e04dee6 | ||
|
|
b49c7b8609 | ||
|
|
948949c571 | ||
|
|
f173a79a0d | ||
|
|
994791add6 | ||
|
|
d177e71b46 | ||
|
|
fcab1501f8 | ||
|
|
eb022220f4 | ||
|
|
f48687528e | ||
|
|
9007f9de9e | ||
|
|
55d84d0f9b | ||
|
|
90b4d688f8 | ||
|
|
aacd42f4bc | ||
|
|
51d7dda5a7 | ||
|
|
1c32acf7b6 | ||
|
|
22c73f3c12 | ||
|
|
9613ea9018 | ||
|
|
3d4802ac68 | ||
|
|
59c9bdb3a5 | ||
|
|
d7953eb779 | ||
|
|
f5c4f98834 |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,7 +1,3 @@
|
||||
#################
|
||||
## npm
|
||||
#################
|
||||
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
@@ -9,11 +5,10 @@ config.json
|
||||
public/src/nodebb.min.js
|
||||
public/config.json
|
||||
public/css/*.css
|
||||
public/themes/*
|
||||
!/public/themes/vanilla
|
||||
!/public/themes/cerulean
|
||||
!/public/themes/modern
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
plugins/*
|
||||
.project
|
||||
*.swp
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
provision.sh
|
||||
11
README.md
11
README.md
@@ -1,15 +1,16 @@
|
||||
|
||||
# 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.
|
||||
|
||||
* [NodeBB Homepage](http://www.nodebb.org/ "NodeBB")
|
||||
* [Demo & Meta Discussion](http://try.nodebb.org)
|
||||
* [Wiki Guides](https://github.com/designcreateplay/NodeBB/wiki) - includes setup for other platforms
|
||||
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
|
||||
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
|
||||
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
@@ -64,4 +65,4 @@ NodeBB can also be started with helper programs, such as `supervisor` and `forev
|
||||
|
||||
## Upgrading NodeBB
|
||||
|
||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB)
|
||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB)
|
||||
|
||||
86
app.js
86
app.js
@@ -55,7 +55,7 @@
|
||||
winston.info('');
|
||||
|
||||
|
||||
if (fs.existsSync(__dirname + '/config.json') && (!nconf.get('setup') && !nconf.get('upgrade'))) {
|
||||
if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('upgrade') && fs.existsSync(__dirname + '/config.json')) {
|
||||
// Load server-side configs
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
@@ -66,7 +66,7 @@
|
||||
nconf.set('upload_url', nconf.get('url') + 'uploads/');
|
||||
|
||||
winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
|
||||
winston.info('NodeBB instance bound to: ' + (nconf.get('bind_address') || 'Any address'));
|
||||
winston.info('NodeBB instance bound to: ' + ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? 'Any address (0.0.0.0)' : nconf.get('bind_address')));
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('Base Configuration OK.');
|
||||
@@ -86,34 +86,45 @@
|
||||
webserver = require('./src/webserver.js'),
|
||||
SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket']}),
|
||||
websockets = require('./src/websockets.js'),
|
||||
plugins = require('./src/plugins'); // Don't remove this - plugins initializes itself
|
||||
posts = require('./src/posts.js'),
|
||||
plugins = require('./src/plugins'), // Don't remove this - plugins initializes itself
|
||||
Notifications = require('./src/notifications'),
|
||||
Upgrade = require('./src/upgrade');
|
||||
|
||||
websockets.init(SocketIO);
|
||||
Upgrade.check(function(schema_ok) {
|
||||
if (schema_ok || nconf.get('check-schema') === false) {
|
||||
websockets.init(SocketIO);
|
||||
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
|
||||
translator.loadServer();
|
||||
translator.loadServer();
|
||||
|
||||
// todo: replace below with read directory code, derp.
|
||||
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',
|
||||
var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false;
|
||||
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
]);
|
||||
// todo: replace below with read directory code, derp.
|
||||
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',
|
||||
|
||||
templates.ready(webserver.init);
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
], customTemplates);
|
||||
|
||||
|
||||
templates.ready(webserver.init);
|
||||
|
||||
Notifications.init();
|
||||
} else {
|
||||
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
|
||||
winston.warn(' node app --upgrade');
|
||||
winston.warn('To ignore this error (not recommended):');
|
||||
winston.warn(' node app --no-check-schema')
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
} else if (nconf.get('upgrade')) {
|
||||
meta = require('./src/meta.js');
|
||||
|
||||
meta.configs.init(function () {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
} else {
|
||||
} else if (nconf.get('setup') || !fs.existsSync(__dirname + '/config.json')) {
|
||||
// New install, ask setup questions
|
||||
if (nconf.get('setup')) {
|
||||
winston.info('NodeBB Setup Triggered via Command Line');
|
||||
@@ -121,6 +132,10 @@
|
||||
winston.warn('Configuration not found, starting NodeBB setup');
|
||||
}
|
||||
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
});
|
||||
|
||||
var install = require('./src/install');
|
||||
|
||||
winston.info('Welcome to NodeBB!');
|
||||
@@ -131,10 +146,29 @@
|
||||
if (err) {
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
} else {
|
||||
winston.info('NodeBB Setup Completed.');
|
||||
winston.info('NodeBB Setup Completed. Run \'node app\' to manually start your NodeBB server.');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
}());
|
||||
|
||||
} else if (nconf.get('upgrade')) {
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
});
|
||||
meta = require('./src/meta.js');
|
||||
|
||||
meta.configs.init(function () {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
} else/* if (nconf.get('help') */{
|
||||
winston.info('Usage: node app [options] [arguments]');
|
||||
winston.info(' [NODE_ENV=development | NODE_ENV=production] node app [--start] [arguments]');
|
||||
winston.info('');
|
||||
winston.info('Options:');
|
||||
winston.info(' --help displays this usage information');
|
||||
winston.info(' --setup configure your environment and setup NodeBB');
|
||||
winston.info(' --upgrade upgrade NodeBB, first read: github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB');
|
||||
winston.info(' --start manually start NodeBB (default when no options are given)');
|
||||
};
|
||||
}());
|
||||
|
||||
2
feeds/categories/.gitignore
vendored
2
feeds/categories/.gitignore
vendored
@@ -1 +1 @@
|
||||
*.rss
|
||||
*.rss
|
||||
2
feeds/topics/.gitignore
vendored
2
feeds/topics/.gitignore
vendored
@@ -1 +1 @@
|
||||
*.rss
|
||||
*.rss
|
||||
@@ -3,72 +3,84 @@
|
||||
"name": "Announcements",
|
||||
"description": "Announcements regarding our community",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-bullhorn"
|
||||
"icon" : "icon-bullhorn",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"name": "General Discussion",
|
||||
"description": "A place to talk about whateeeever you want",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-comment"
|
||||
"icon" : "icon-comment",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Development",
|
||||
"description": "NodeBB development news and announcements",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-github"
|
||||
"icon" : "icon-github",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"name": "Blogs",
|
||||
"description": "Blog posts from individual members",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-pencil"
|
||||
"icon" : "icon-pencil",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"name": "Feature Requests",
|
||||
"description": "Got a feature request you'd like to see? Give us a shout here.",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-lightbulb"
|
||||
"icon" : "icon-lightbulb",
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"name": "Bug Reports",
|
||||
"description": "Having trouble with NodeBB? Let us know...",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-cogs"
|
||||
"icon" : "icon-cogs",
|
||||
"order": 6
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Plugins",
|
||||
"description": "Enhance your NodeBB with plugins!",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-plus-sign"
|
||||
"icon" : "icon-plus-sign",
|
||||
"order": 7
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Link Exchange",
|
||||
"description": "Link exchange",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-exchange"
|
||||
"icon" : "icon-exchange",
|
||||
"order": 8
|
||||
},
|
||||
{
|
||||
"name": "News",
|
||||
"description": "News from around the world",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-globe"
|
||||
"icon" : "icon-globe",
|
||||
"order": 9
|
||||
},
|
||||
{
|
||||
"name": "Movies",
|
||||
"description": "Discuss the latest movies here",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-film"
|
||||
"icon" : "icon-film",
|
||||
"order": 10
|
||||
},
|
||||
{
|
||||
"name": "Games",
|
||||
"description": "Discuss the latest games here",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-screenshot"
|
||||
"icon" : "icon-screenshot",
|
||||
"order": 11
|
||||
},
|
||||
{
|
||||
"name": "Random",
|
||||
"description": "Anything and (almost) everything welcome!",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-beer"
|
||||
"icon" : "icon-beer",
|
||||
"order": 12
|
||||
}
|
||||
]
|
||||
69
mocks/redismock.js
Normal file
69
mocks/redismock.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Redis Mock - wrapper for redis.js, makes system use separate test db, instead of production
|
||||
* ATTENTION: testing db is flushed before every use!
|
||||
*/
|
||||
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var RedisDB,
|
||||
redis = require('redis'),
|
||||
utils = require('./../public/src/utils.js'),
|
||||
path = require('path'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
errorText;
|
||||
|
||||
|
||||
nconf.file({ file: path.join(__dirname, '../config.json') });
|
||||
|
||||
var testDbConfig = nconf.get('redis_test'),
|
||||
productionDbConfig = nconf.get('redis');
|
||||
if(!testDbConfig){
|
||||
errorText = 'redis_test database is not defined';
|
||||
winston.info(
|
||||
"\n===========================================================\n"+
|
||||
"Please, add parameters for test database in config.json\n"+
|
||||
"For example:\n"+
|
||||
'"redis_test": {' + '\n' +
|
||||
' "host": "127.0.0.1",' + '\n' +
|
||||
' "port": "6379",' + '\n' +
|
||||
' "password": "",' + '\n' +
|
||||
' "database": "1"' + '\n' +
|
||||
'}\n'+
|
||||
"==========================================================="
|
||||
);
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
if( testDbConfig.database === productionDbConfig.database &&
|
||||
testDbConfig.host === productionDbConfig.host &&
|
||||
testDbConfig.port === productionDbConfig.port
|
||||
){
|
||||
errorText = 'redis_test database has the same config as production db';
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
nconf.set('redis',testDbConfig);
|
||||
|
||||
RedisDB = require('../src/redis.js');
|
||||
|
||||
|
||||
//Clean up
|
||||
RedisDB.send_command('flushdb', [], function(error){
|
||||
if(error){
|
||||
winston.error(error);
|
||||
throw new Error(error);
|
||||
} else {
|
||||
winston.info('redis_test db flushed');
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: data seeding, if needed at all
|
||||
|
||||
|
||||
module.exports = RedisDB;
|
||||
|
||||
}(module));
|
||||
35
package.json
35
package.json
@@ -2,13 +2,16 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPLv3 or later",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "0.0.7",
|
||||
"version": "0.1.0",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/designcreateplay/NodeBB/"
|
||||
},
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "mocha ./tests"
|
||||
},
|
||||
"dependencies": {
|
||||
"socket.io": "~0.9.16",
|
||||
"redis": "0.8.3",
|
||||
@@ -30,20 +33,25 @@
|
||||
"gravatar": "1.0.6",
|
||||
"nconf": "~0.6.7",
|
||||
"sitemap": "~0.6.0",
|
||||
"cheerio": "~0.12.0",
|
||||
"request": "~2.25.0",
|
||||
"reds": "~0.2.4",
|
||||
"winston": "~0.7.2",
|
||||
"nodebb-plugin-mentions": "~0.1.0",
|
||||
"nodebb-plugin-markdown": "~0.1.0",
|
||||
"rss": "~0.2.0",
|
||||
"prompt": "~0.2.11",
|
||||
"uglify-js": "~2.4.0",
|
||||
"validator": "~1.5.1"
|
||||
"validator": "~1.5.1",
|
||||
"nodebb-plugin-mentions": "~0.1.14",
|
||||
"nodebb-plugin-markdown": "~0.1.8",
|
||||
"nodebb-theme-vanilla": "designcreateplay/nodebb-theme-vanilla",
|
||||
"nodebb-theme-cerulean": "0.0.6",
|
||||
"cron": "~1.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"hiredis": "~0.1.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.13.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/designcreateplay/NodeBB/issues"
|
||||
},
|
||||
@@ -53,15 +61,22 @@
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Andrew Rodrigues",
|
||||
"email": "andrew@designcreateplay.com"
|
||||
"email": "andrew@designcreateplay.com",
|
||||
"url": "https://github.com/psychobunny"
|
||||
},
|
||||
{
|
||||
"name": "Julian Lam",
|
||||
"email": "julian@designcreateplay.com"
|
||||
"email": "julian@designcreateplay.com",
|
||||
"url": "https://github.com/julianlam"
|
||||
},
|
||||
{
|
||||
"name": "Barış Soner Uşaklı",
|
||||
"email": "baris@designcreateplay.com"
|
||||
"email": "baris@designcreateplay.com",
|
||||
"url": "https://github.com/barisusakli"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Darqui",
|
||||
"url": "https://github.com/adarqui"
|
||||
},
|
||||
{
|
||||
"name": "Damian Bushong",
|
||||
@@ -70,6 +85,10 @@
|
||||
{
|
||||
"name": "Matt Smith",
|
||||
"url": "https://github.com/soimafreak"
|
||||
},
|
||||
{
|
||||
"name": "Quinton Marchi",
|
||||
"url": "https://github.com/iamcardinal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
0
plugins/.gitignore
vendored
0
plugins/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
@import "../themes/cerulean/cerulean.less";
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"chat.chatting_with": "Chat with <span id='chat-with-name'></span>",
|
||||
"chat.placeholder": "type chat message, here press enter to send",
|
||||
"chat.placeholder": "type chat message here, press enter to send",
|
||||
"chat.send": "Send",
|
||||
"stats.online": "Online",
|
||||
"stats.users": "Users",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"logout": "Logout",
|
||||
"logout.title": "You are now logged out.",
|
||||
"logout.message": "You have successfully logged out of NodeBB",
|
||||
"header.admin": "Admin",
|
||||
"header.recent": "Recent",
|
||||
"header.unread": "Unread",
|
||||
"header.users": "Users",
|
||||
|
||||
3
public/language/en/notifications.json
Normal file
3
public/language/en/notifications.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Notifications"
|
||||
}
|
||||
5
public/language/en/recent.json
Normal file
5
public/language/en/recent.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"day": "Day",
|
||||
"week": "Week",
|
||||
"month": "Month"
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
"thread_tools.move": "Move Thread",
|
||||
"thread_tools.delete": "Delete Thread",
|
||||
"load_categories": "Loading Categories",
|
||||
"disabled_categories_note": "Disabled Categories are greyed out",
|
||||
"confirm_move": "Move",
|
||||
"favourites.not_logged_in.title": "Not Logged In",
|
||||
"favourites.not_logged_in.message": "Please log in in order to favourite this post"
|
||||
|
||||
@@ -23,7 +23,8 @@ var ajaxify = {};
|
||||
|
||||
window.onpopstate = function (event) {
|
||||
// "quiet": If set to true, will not call pushState
|
||||
if (event !== null && event.state && event.state.url !== undefined) ajaxify.go(event.state.url, null, null, true);
|
||||
if (event !== null && event.state && event.state.url !== undefined)
|
||||
ajaxify.go(event.state.url, null, null, true);
|
||||
};
|
||||
|
||||
var pagination;
|
||||
@@ -76,14 +77,17 @@ var ajaxify = {};
|
||||
templates.flush();
|
||||
templates.load_template(function () {
|
||||
exec_body_scripts(content);
|
||||
require(['forum/' + tpl_url], function(script) {
|
||||
if (script && script.init) script.init();
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
||||
app.process_page();
|
||||
|
||||
app.process_page();
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
||||
if (window.location.hash)
|
||||
hash = window.location.hash;
|
||||
if (hash)
|
||||
@@ -114,7 +118,12 @@ var ajaxify = {};
|
||||
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:')
|
||||
return;
|
||||
|
||||
app.previousUrl = window.location.href;
|
||||
if(!window.location.pathname.match(/\/(403|404)$/g))
|
||||
app.previousUrl = window.location.href;
|
||||
|
||||
if (this.getAttribute('data-ajaxify') == 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e.ctrlKey && e.which === 1) {
|
||||
if (this.host === window.location.host) {
|
||||
@@ -127,8 +136,11 @@ var ajaxify = {};
|
||||
}
|
||||
} else if (window.location.pathname !== '/outgoing') {
|
||||
// External Link
|
||||
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
|
||||
e.preventDefault();
|
||||
|
||||
if (config.useOutgoingLinksPage == true) {
|
||||
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -165,7 +177,7 @@ var ajaxify = {};
|
||||
|
||||
var scripts = [],
|
||||
script,
|
||||
children_nodes = $(body_el).children(),
|
||||
children_nodes = $(body_el).find('script'),
|
||||
child,
|
||||
i;
|
||||
|
||||
@@ -186,4 +198,4 @@ var ajaxify = {};
|
||||
}
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
}(jQuery));
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
var socket,
|
||||
config,
|
||||
app = {};
|
||||
app = {
|
||||
'username': null,
|
||||
'uid': null
|
||||
};
|
||||
|
||||
(function () {
|
||||
var showWelcomeMessage = false;
|
||||
|
||||
|
||||
app.loadConfig = function() {
|
||||
|
||||
$.ajax({
|
||||
@@ -17,13 +21,20 @@ var socket,
|
||||
socket.socket.connect();
|
||||
}, 200);
|
||||
} else {
|
||||
socket = io.connect(config.socket.address);
|
||||
var max_reconnection_attemps = 5;
|
||||
var reconnection_delay = 200;
|
||||
socket = io.connect(RELATIVE_PATH, {
|
||||
'max reconnection attempts': max_reconnection_attemps,
|
||||
'reconnection delay': reconnection_delay
|
||||
});
|
||||
|
||||
var reconnecting = false,
|
||||
reconnectEl, reconnectTimer;
|
||||
|
||||
socket.on('event:connect', function (data) {
|
||||
app.username = data.username;
|
||||
app.uid = data.uid;
|
||||
|
||||
app.showLoginMessage();
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
@@ -53,7 +64,13 @@ var socket,
|
||||
socket.socket.connect();
|
||||
});
|
||||
|
||||
socket.on('reconnecting', function (data) {
|
||||
socket.on('reconnecting', function (data, attempt) {
|
||||
if(attempt == max_reconnection_attemps) {
|
||||
socket.socket.reconnectionAttempts = 0;
|
||||
socket.socket.reconnectionDelay = reconnection_delay;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reconnectEl) reconnectEl = $('#reconnect');
|
||||
reconnecting = true;
|
||||
|
||||
@@ -97,6 +114,17 @@ var socket,
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('event:banned', function() {
|
||||
app.alert({
|
||||
title: 'Banned',
|
||||
message: 'You are banned you will be logged out!',
|
||||
type: 'warning',
|
||||
timeout: 1000
|
||||
});
|
||||
|
||||
setTimeout(app.logout, 1000);
|
||||
});
|
||||
|
||||
app.enter_room('global');
|
||||
}
|
||||
},
|
||||
@@ -104,6 +132,14 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
app.logout = function() {
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: $('#csrf_token').val()
|
||||
}, function() {
|
||||
window.location.reload(false);
|
||||
});
|
||||
}
|
||||
|
||||
// takes a string like 1000 and returns 1,000
|
||||
app.addCommas = function (text) {
|
||||
return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
|
||||
@@ -130,6 +166,7 @@ var socket,
|
||||
var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime());
|
||||
|
||||
var alert = $('#' + alert_id);
|
||||
var title = params.title || '';
|
||||
|
||||
function startTimeout(div, timeout) {
|
||||
var timeoutId = setTimeout(function () {
|
||||
@@ -142,7 +179,7 @@ var socket,
|
||||
}
|
||||
|
||||
if (alert.length > 0) {
|
||||
alert.find('strong').html(params.title);
|
||||
alert.find('strong').html(title);
|
||||
alert.find('p').html(params.message);
|
||||
alert.attr('class', "alert toaster-alert " + "alert-" + params.type);
|
||||
|
||||
@@ -155,7 +192,7 @@ var socket,
|
||||
p = document.createElement('p');
|
||||
|
||||
p.innerHTML = params.message;
|
||||
strong.innerHTML = params.title;
|
||||
strong.innerHTML = title;
|
||||
|
||||
div.className = "alert toaster-alert " + "alert-" + params.type;
|
||||
|
||||
@@ -240,16 +277,10 @@ var socket,
|
||||
}
|
||||
|
||||
app.process_page = function () {
|
||||
|
||||
// here is where all modules' onNavigate should be called, I think.
|
||||
require(['mobileMenu'], function (mobileMenu) {
|
||||
mobileMenu.onNavigate();
|
||||
});
|
||||
|
||||
app.populate_online_users();
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
var path = window.location.pathname,
|
||||
parts = path.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('#main-nav li').removeClass('active');
|
||||
@@ -266,7 +297,7 @@ var socket,
|
||||
}
|
||||
|
||||
$('span.timeago').timeago();
|
||||
|
||||
$('.post-content img').addClass('img-responsive');
|
||||
|
||||
setTimeout(function () {
|
||||
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
|
||||
@@ -308,11 +339,15 @@ var socket,
|
||||
chatModal = chat.getModal(touid);
|
||||
}
|
||||
chat.load(chatModal.attr('UUID'));
|
||||
chat.center(chatModal);
|
||||
});
|
||||
}
|
||||
|
||||
app.createNewPosts = function (data) {
|
||||
data.posts[0].display_moderator_tools = 'none';
|
||||
if (data.posts[0].uid !== app.uid) {
|
||||
data.posts[0].display_moderator_tools = 'none';
|
||||
}
|
||||
|
||||
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var uniqueid = new Date().getTime(),
|
||||
@@ -332,6 +367,7 @@ var socket,
|
||||
app.populate_online_users();
|
||||
app.addCommasToNumbers();
|
||||
$('span.timeago').timeago();
|
||||
$('.post-content img').addClass('img-responsive');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,85 +1,92 @@
|
||||
(function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
isFollowing = templates.get('isFollowing');
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Account = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
var username = $('.account-username a').html();
|
||||
app.enter_room('user/' + theirid);
|
||||
Account.init = function() {
|
||||
header.init();
|
||||
|
||||
app.addCommasToNumbers();
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
isFollowing = templates.get('isFollowing');
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
$(document).ready(function() {
|
||||
var username = $('.account-username a').html();
|
||||
app.enter_room('user/' + theirid);
|
||||
|
||||
if (yourid !== theirid) {
|
||||
if (isFollowing) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
} else {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
} else {
|
||||
followBtn.hide();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
app.addCommasToNumbers();
|
||||
|
||||
followBtn.on('click', function() {
|
||||
socket.emit('api:user.follow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
|
||||
if (yourid !== theirid && yourid !== "0") {
|
||||
if (isFollowing) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
app.alertSuccess('You are now following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error following' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
unfollowBtn.on('click', function() {
|
||||
socket.emit('api:user.unfollow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
} else {
|
||||
followBtn.hide();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
|
||||
followBtn.on('click', function() {
|
||||
socket.emit('api:user.follow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
app.alertSuccess('You are now following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error following' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.user-recent-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
unfollowBtn.on('click', function() {
|
||||
socket.emit('api:user.unfollow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.user-recent-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
|
||||
socket.on('api:user.isOnline', Account.handleUserOnline);
|
||||
|
||||
socket.emit('api:user.isOnline', theirid, Account.handleUserOnline);
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
|
||||
$('.user-recent-posts').prepend(html);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
Account.handleUserOnline = function(data) {
|
||||
var onlineStatus = $('.account-online-status');
|
||||
|
||||
function handleUserOnline(data) {
|
||||
if (data.online) {
|
||||
onlineStatus.find('span span').text('online');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle');
|
||||
} else {
|
||||
onlineStatus.find('span span').text('offline');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle-blank');
|
||||
}
|
||||
if (data.online) {
|
||||
onlineStatus.find('span span').text('online');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle');
|
||||
} else {
|
||||
onlineStatus.find('span span').text('offline');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle-blank');
|
||||
}
|
||||
};
|
||||
|
||||
socket.on('api:user.isOnline', handleUserOnline);
|
||||
|
||||
socket.emit('api:user.isOnline', theirid, handleUserOnline);
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
|
||||
$('.user-recent-posts').prepend(html);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
return Account;
|
||||
});
|
||||
@@ -1,88 +1,186 @@
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
define(['forum/accountheader', 'uploader'], function(header, uploader) {
|
||||
var AccountEdit = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
AccountEdit.init = function() {
|
||||
header.init();
|
||||
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
|
||||
var selectedImageType = '';
|
||||
|
||||
$('#uploadForm').submit(function() {
|
||||
status('uploading the file ...');
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
$('#upload-progress-bar').css('width', '0%');
|
||||
$('#upload-progress-box').show();
|
||||
$('#upload-progress-box').removeClass('hide');
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
email: $('#inputEmail').val(),
|
||||
fullname: $('#inputFullname').val(),
|
||||
website: $('#inputWebsite').val(),
|
||||
birthday: $('#inputBirthday').val(),
|
||||
location: $('#inputLocation').val(),
|
||||
signature: $('#inputSignature').val()
|
||||
};
|
||||
|
||||
if (!$('#userPhotoInput').val()) {
|
||||
error('select an image to upload!');
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this).find('#imageUploadCsrf').val($('#csrf_token').val());
|
||||
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
|
||||
error: function(xhr) {
|
||||
error('Error: ' + xhr.status);
|
||||
},
|
||||
|
||||
uploadProgress: function(event, position, total, percent) {
|
||||
console.log(percent);
|
||||
$('#upload-progress-bar').css('width', percent + '%');
|
||||
},
|
||||
|
||||
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
error(response.error);
|
||||
return;
|
||||
socket.emit('api:user.updateProfile', userData, function(err, data) {
|
||||
if (data.success) {
|
||||
app.alertSuccess('Your profile has been updated successfully!');
|
||||
if (data.picture) {
|
||||
$('#user-current-picture').attr('src', data.picture);
|
||||
$('#user_label img').attr('src', data.picture);
|
||||
}
|
||||
if (data.gravatarpicture) {
|
||||
$('#user-gravatar-picture').attr('src', data.gravatarpicture);
|
||||
gravatarPicture = data.gravatarpicture;
|
||||
}
|
||||
} else {
|
||||
app.alertError('There was an error updating your profile! ' + err.error);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
var imageUrlOnServer = response.path;
|
||||
$('#changePictureBtn').on('click', function() {
|
||||
selectedImageType = '';
|
||||
AccountEdit.updateImages();
|
||||
|
||||
$('#change-picture-modal').modal('show');
|
||||
$('#change-picture-modal').removeClass('hide');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
selectedImageType = 'gravatar';
|
||||
});
|
||||
|
||||
$('#uploaded-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').hide();
|
||||
$('#uploaded-box .icon-ok').show();
|
||||
selectedImageType = 'uploaded';
|
||||
});
|
||||
|
||||
$('#savePictureChangesBtn').on('click', function() {
|
||||
$('#change-picture-modal').modal('hide');
|
||||
|
||||
if (selectedImageType) {
|
||||
AccountEdit.changeUserPicture(selectedImageType);
|
||||
|
||||
if (selectedImageType == 'gravatar')
|
||||
$('#user-current-picture').attr('src', gravatarPicture);
|
||||
else if (selectedImageType == 'uploaded')
|
||||
$('#user-current-picture').attr('src', uploadedPicture);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('#upload-picture-modal').on('hide', function() {
|
||||
$('#userPhotoInput').val('');
|
||||
});
|
||||
|
||||
$('#uploadPictureBtn').on('click', function() {
|
||||
|
||||
$('#change-picture-modal').modal('hide');
|
||||
uploader.open(config.relative_path + '/user/uploadpicture', function(imageUrlOnServer) {
|
||||
$('#user-current-picture').attr('src', imageUrlOnServer);
|
||||
$('#user-uploaded-picture').attr('src', imageUrlOnServer);
|
||||
|
||||
uploadedPicture = imageUrlOnServer;
|
||||
|
||||
setTimeout(function() {
|
||||
hideAlerts();
|
||||
$('#upload-picture-modal').modal('hide');
|
||||
}, 750);
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
success('File uploaded successfully!');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
function hideAlerts() {
|
||||
$('#alert-status').addClass('hide');
|
||||
$('#alert-success').addClass('hide');
|
||||
$('#alert-error').addClass('hide');
|
||||
$('#upload-progress-box').addClass('hide');
|
||||
}
|
||||
(function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var password = $('#inputNewPassword');
|
||||
var password_confirm = $('#inputNewPasswordAgain');
|
||||
var passwordvalid = false;
|
||||
var passwordsmatch = false;
|
||||
|
||||
function status(message) {
|
||||
hideAlerts();
|
||||
$('#alert-status').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function success(message) {
|
||||
hideAlerts();
|
||||
$('#alert-success').text(message).removeClass('hide');
|
||||
}
|
||||
function onPasswordChanged() {
|
||||
passwordvalid = utils.isPasswordValid(password.val());
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
password_notify.html('Password too short');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else if (!passwordvalid) {
|
||||
password_notify.html('Invalid password');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else {
|
||||
password_notify.html('OK!');
|
||||
password_notify.attr('class', 'alert alert-success');
|
||||
password_notify.removeClass('hide');
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
hideAlerts();
|
||||
$('#alert-error').text(message).removeClass('hide');
|
||||
}
|
||||
onPasswordConfirmChanged();
|
||||
}
|
||||
|
||||
function changeUserPicture(type) {
|
||||
function onPasswordConfirmChanged() {
|
||||
if (password_notify.hasClass('alert-danger') || !password_confirm.val()) {
|
||||
password_confirm_notify.addClass('hide');
|
||||
return;
|
||||
}
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
password_confirm_notify.html('Passwords must match!');
|
||||
password_confirm_notify.attr('class', 'alert alert-danger');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
password_confirm_notify.html('OK!');
|
||||
password_confirm_notify.attr('class', 'alert alert-success');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
password.on('blur', onPasswordChanged);
|
||||
password_confirm.on('blur', onPasswordConfirmChanged);
|
||||
|
||||
$('#changePasswordBtn').on('click', function() {
|
||||
|
||||
if (passwordvalid && passwordsmatch && currentPassword.val()) {
|
||||
socket.emit('api:user.changePassword', {
|
||||
'currentPassword': currentPassword.val(),
|
||||
'newPassword': password.val()
|
||||
}, function(err) {
|
||||
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
password_notify.addClass('hide');
|
||||
password_confirm_notify.addClass('hide');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if (err) {
|
||||
app.alertError(err.error);
|
||||
return;
|
||||
}
|
||||
|
||||
app.alertSuccess('Your password is updated!');
|
||||
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
}());
|
||||
};
|
||||
|
||||
|
||||
AccountEdit.changeUserPicture = function(type) {
|
||||
var userData = {
|
||||
type: type
|
||||
};
|
||||
@@ -94,40 +192,10 @@ $(document).ready(function() {
|
||||
});
|
||||
}
|
||||
|
||||
var selectedImageType = '';
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
email: $('#inputEmail').val(),
|
||||
fullname: $('#inputFullname').val(),
|
||||
website: $('#inputWebsite').val(),
|
||||
birthday: $('#inputBirthday').val(),
|
||||
location: $('#inputLocation').val(),
|
||||
signature: $('#inputSignature').val()
|
||||
};
|
||||
|
||||
socket.emit('api:user.updateProfile', userData, function(err, data) {
|
||||
if (data.success) {
|
||||
app.alertSuccess('Your profile has been updated successfully!');
|
||||
if (data.picture) {
|
||||
$('#user-current-picture').attr('src', data.picture);
|
||||
$('#user_label img').attr('src', data.picture);
|
||||
}
|
||||
if (data.gravatarpicture) {
|
||||
$('#user-gravatar-picture').attr('src', data.gravatarpicture);
|
||||
gravatarPicture = data.gravatarpicture;
|
||||
}
|
||||
} else {
|
||||
app.alertError('There was an error updating your profile! ' + err.error);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
function updateImages() {
|
||||
AccountEdit.updateImages = function() {
|
||||
var currentPicture = $('#user-current-picture').attr('src');
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
|
||||
if (gravatarPicture) {
|
||||
$('#user-gravatar-picture').attr('src', gravatarPicture);
|
||||
@@ -153,139 +221,5 @@ $(document).ready(function() {
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
}
|
||||
|
||||
|
||||
$('#changePictureBtn').on('click', function() {
|
||||
selectedImageType = '';
|
||||
updateImages();
|
||||
|
||||
$('#change-picture-modal').modal('show');
|
||||
$('#change-picture-modal').removeClass('hide');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
selectedImageType = 'gravatar';
|
||||
});
|
||||
|
||||
$('#uploaded-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').hide();
|
||||
$('#uploaded-box .icon-ok').show();
|
||||
selectedImageType = 'uploaded';
|
||||
});
|
||||
|
||||
$('#savePictureChangesBtn').on('click', function() {
|
||||
$('#change-picture-modal').modal('hide');
|
||||
|
||||
if (selectedImageType) {
|
||||
changeUserPicture(selectedImageType);
|
||||
|
||||
if (selectedImageType == 'gravatar')
|
||||
$('#user-current-picture').attr('src', gravatarPicture);
|
||||
else if (selectedImageType == 'uploaded')
|
||||
$('#user-current-picture').attr('src', uploadedPicture);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('#upload-picture-modal').on('hide', function() {
|
||||
$('#userPhotoInput').val('');
|
||||
});
|
||||
|
||||
$('#uploadPictureBtn').on('click', function() {
|
||||
|
||||
$('#change-picture-modal').modal('hide');
|
||||
$('#upload-picture-modal').modal('show');
|
||||
$('#upload-picture-modal').removeClass('hide');
|
||||
|
||||
hideAlerts();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#pictureUploadSubmitBtn').on('click', function() {
|
||||
$('#uploadForm').submit();
|
||||
});
|
||||
|
||||
(function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var password = $('#inputNewPassword');
|
||||
var password_confirm = $('#inputNewPasswordAgain');
|
||||
var passwordvalid = false;
|
||||
var passwordsmatch = false;
|
||||
|
||||
|
||||
function onPasswordChanged() {
|
||||
passwordvalid = utils.isPasswordValid(password.val());
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
password_notify.html('Password too short');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else if (!passwordvalid) {
|
||||
password_notify.html('Invalid password');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else {
|
||||
password_notify.html('OK!');
|
||||
password_notify.attr('class', 'alert alert-success');
|
||||
password_notify.removeClass('hide');
|
||||
}
|
||||
|
||||
onPasswordConfirmChanged();
|
||||
}
|
||||
|
||||
function onPasswordConfirmChanged() {
|
||||
if (password_notify.hasClass('alert-danger') || !password_confirm.val()) {
|
||||
password_confirm_notify.addClass('hide');
|
||||
return;
|
||||
}
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
password_confirm_notify.html('Passwords must match!');
|
||||
password_confirm_notify.attr('class', 'alert alert-danger');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
password_confirm_notify.html('OK!');
|
||||
password_confirm_notify.attr('class', 'alert alert-success');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
password.on('blur', onPasswordChanged);
|
||||
password_confirm.on('blur', onPasswordConfirmChanged);
|
||||
|
||||
$('#changePasswordBtn').on('click', function() {
|
||||
|
||||
if (passwordvalid && passwordsmatch && currentPassword.val()) {
|
||||
socket.emit('api:user.changePassword', {
|
||||
'currentPassword': currentPassword.val(),
|
||||
'newPassword': password.val()
|
||||
}, function(err) {
|
||||
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
password_notify.addClass('hide');
|
||||
password_confirm_notify.addClass('hide');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if (err) {
|
||||
app.alertError(err.error);
|
||||
return;
|
||||
}
|
||||
|
||||
app.alertSuccess('Your password is updated!');
|
||||
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
}());
|
||||
return AccountEdit;
|
||||
});
|
||||
@@ -1,24 +1,11 @@
|
||||
(function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid');
|
||||
define(function() {
|
||||
var AccountHeader = {};
|
||||
|
||||
AccountHeader.init = function() {
|
||||
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="/user/' + userslug + '/settings">settings</a></span>\
|
||||
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
|
||||
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
|
||||
</div>');
|
||||
|
||||
$('.account-username-box').append(links);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
createMenu();
|
||||
AccountHeader.createMenu();
|
||||
|
||||
var editLink = $('#editLink');
|
||||
var settingsLink = $('#settingsLink');
|
||||
@@ -37,6 +24,20 @@
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}());
|
||||
AccountHeader.createMenu = function() {
|
||||
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="/user/' + userslug + '/settings">settings</a></span>\
|
||||
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
|
||||
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
|
||||
</div>');
|
||||
|
||||
$('.account-username-box').append(links);
|
||||
}
|
||||
|
||||
return AccountHeader;
|
||||
});
|
||||
@@ -1,19 +1,25 @@
|
||||
$(document).ready(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var AccountSettings = {};
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
AccountSettings.init = function() {
|
||||
header.init();
|
||||
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
|
||||
};
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
socket.emit('api:user.saveSettings', settings, function(success) {
|
||||
if (success) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
}
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
|
||||
};
|
||||
|
||||
socket.emit('api:user.saveSettings', settings, function(success) {
|
||||
if (success) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
return AccountSettings;
|
||||
});
|
||||
|
||||
@@ -1,139 +1,163 @@
|
||||
var modified_categories = {};
|
||||
define(function() {
|
||||
var Categories = {};
|
||||
|
||||
function modified(el) {
|
||||
var cid = $(el).parents('li').attr('data-cid');
|
||||
Categories.init = function() {
|
||||
var modified_categories = {};
|
||||
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid][$(el).attr('data-name')] = $(el).val();
|
||||
}
|
||||
|
||||
function save() {
|
||||
socket.emit('api:admin.categories.update', modified_categories);
|
||||
modified_categories = {};
|
||||
}
|
||||
|
||||
function select_icon(el) {
|
||||
var selected = el.attr('class').replace(' icon-2x', '');
|
||||
jQuery('#icons .selected').removeClass('selected');
|
||||
if (selected)
|
||||
jQuery('#icons .' + selected).parent().addClass('selected');
|
||||
|
||||
|
||||
bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) {
|
||||
if (confirm) {
|
||||
var iconClass = jQuery('.bootbox .selected').children(':first').attr('class');
|
||||
el.attr('class', iconClass + ' icon-2x');
|
||||
el.val(iconClass);
|
||||
|
||||
modified(el);
|
||||
function modified(el) {
|
||||
var cid = $(el).parents('li').attr('data-cid');
|
||||
if(cid) {
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid][$(el).attr('data-name')] = $(el).val();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() { //bootbox was rewritten for BS3 and I had to add this timeout for the previous code to work. TODO: to look into
|
||||
jQuery('.bootbox .col-md-3').on('click', function() {
|
||||
jQuery('.bootbox .selected').removeClass('selected');
|
||||
jQuery(this).addClass('selected');
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
function save() {
|
||||
socket.emit('api:admin.categories.update', modified_categories);
|
||||
modified_categories = {};
|
||||
}
|
||||
|
||||
function select_icon(el) {
|
||||
var selected = el.attr('class').replace(' icon-2x', '');
|
||||
jQuery('#icons .selected').removeClass('selected');
|
||||
if (selected)
|
||||
jQuery('#icons .' + selected).parent().addClass('selected');
|
||||
|
||||
|
||||
function update_blockclass(el) {
|
||||
el.parentNode.parentNode.className = 'entry-row ' + el.value;
|
||||
}
|
||||
bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) {
|
||||
if (confirm) {
|
||||
var iconClass = jQuery('.bootbox .selected').children(':first').attr('class');
|
||||
el.attr('class', iconClass + ' icon-2x');
|
||||
el.val(iconClass);
|
||||
el.attr('value', iconClass);
|
||||
|
||||
jQuery('#entry-container').sortable();
|
||||
jQuery('.blockclass').each(function() {
|
||||
jQuery(this).val(this.getAttribute('data-value'));
|
||||
});
|
||||
modified(el);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//DRY Failure. this needs to go into an ajaxify onready style fn. Currently is copy pasted into every single function so after ACP is off the ground fix asap
|
||||
(function() {
|
||||
function showCreateCategoryModal() {
|
||||
$('#new-category-modal').modal();
|
||||
}
|
||||
|
||||
function createNewCategory() {
|
||||
var category = {
|
||||
name: $('#inputName').val(),
|
||||
description: $('#inputDescription').val(),
|
||||
icon: $('#new-category-modal i').attr('value'),
|
||||
blockclass: $('#inputBlockclass').val()
|
||||
};
|
||||
|
||||
socket.emit('api:admin.categories.create', category, function(err, data) {
|
||||
if (!err) {
|
||||
app.alert({
|
||||
alert_id: 'category_created',
|
||||
title: 'Created',
|
||||
message: 'Category successfully created!',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
setTimeout(function() { //bootbox was rewritten for BS3 and I had to add this timeout for the previous code to work. TODO: to look into
|
||||
jQuery('.bootbox .col-md-3').on('click', function() {
|
||||
jQuery('.bootbox .selected').removeClass('selected');
|
||||
jQuery(this).addClass('selected');
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
var html = templates.prepare(templates['admin/categories'].blocks['categories']).parse({
|
||||
categories: [data]
|
||||
});
|
||||
$('#entry-container').append(html);
|
||||
|
||||
$('#new-category-modal').modal('hide');
|
||||
function update_blockclass(el) {
|
||||
el.parentNode.parentNode.className = 'entry-row ' + el.value;
|
||||
}
|
||||
|
||||
function updateCategoryOrders() {
|
||||
var categories = $('.admin-categories #entry-container').children();
|
||||
for(var i=0; i<categories.length; ++i) {
|
||||
var input = $(categories[i]).find('input[data-name="order"]');
|
||||
|
||||
input.val(i+1).attr('data-value', i+1);
|
||||
modified(input);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery('#entry-container').sortable({
|
||||
stop: function( event, ui ) {
|
||||
updateCategoryOrders();
|
||||
}
|
||||
});
|
||||
}
|
||||
jQuery('.blockclass').each(function() {
|
||||
jQuery(this).val(this.getAttribute('data-value'));
|
||||
});
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
function showCreateCategoryModal() {
|
||||
$('#new-category-modal').modal();
|
||||
}
|
||||
|
||||
function createNewCategory() {
|
||||
var category = {
|
||||
name: $('#inputName').val(),
|
||||
description: $('#inputDescription').val(),
|
||||
icon: $('#new-category-modal i').val(),
|
||||
blockclass: $('#inputBlockclass').val()
|
||||
};
|
||||
|
||||
socket.emit('api:admin.categories.create', category, function(err, data) {
|
||||
if (!err) {
|
||||
app.alert({
|
||||
alert_id: 'category_created',
|
||||
title: 'Created',
|
||||
message: 'Category successfully created!',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
var html = templates.prepare(templates['admin/categories'].blocks['categories']).parse({
|
||||
categories: [data]
|
||||
});
|
||||
$('#entry-container').append(html);
|
||||
|
||||
$('#new-category-modal').modal('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#save').on('click', save);
|
||||
jQuery('#addNew').on('click', showCreateCategoryModal);
|
||||
jQuery('#create-category-btn').on('click', createNewCategory);
|
||||
|
||||
jQuery('#entry-container').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
jQuery('#new-category-modal').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
jQuery('.blockclass').on('change', function(ev) {
|
||||
update_blockclass(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.category_name, .category_description, .blockclass').on('change', function(ev) {
|
||||
modified(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.entry-row button').each(function(index, element) {
|
||||
var disabled = $(element).attr('data-disabled');
|
||||
if (disabled == "0" || disabled == "")
|
||||
$(element).html('Disable');
|
||||
else
|
||||
$(element).html('Enable');
|
||||
|
||||
});
|
||||
|
||||
jQuery('#entry-container').on('click', '.disable-btn', function(ev) {
|
||||
var btn = jQuery(this);
|
||||
var categoryRow = btn.parents('li');
|
||||
var cid = categoryRow.attr('data-cid');
|
||||
|
||||
var disabled = btn.html() == "Disable" ? "1" : "0";
|
||||
categoryRow.remove();
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid]['disabled'] = disabled;
|
||||
|
||||
save();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#save').on('click', save);
|
||||
jQuery('#addNew').on('click', showCreateCategoryModal);
|
||||
jQuery('#create-category-btn').on('click', createNewCategory);
|
||||
|
||||
jQuery('#entry-container').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
jQuery('.blockclass').on('change', function(ev) {
|
||||
update_blockclass(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.category_name, .category_description, .blockclass').on('change', function(ev) {
|
||||
modified(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.entry-row button').each(function(index, element) {
|
||||
var disabled = $(element).attr('data-disabled');
|
||||
if (disabled == "0" || disabled == "")
|
||||
$(element).html('Disable');
|
||||
else
|
||||
$(element).html('Enable');
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
jQuery('.entry-row button').on('click', function(ev) {
|
||||
var btn = jQuery(this);
|
||||
var categoryRow = btn.parents('li');
|
||||
var cid = categoryRow.attr('data-cid');
|
||||
|
||||
var disabled = btn.html() == "Disable" ? "1" : "0";
|
||||
categoryRow.remove();
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid]['disabled'] = disabled;
|
||||
|
||||
save();
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
return Categories;
|
||||
});
|
||||
@@ -1,121 +1,42 @@
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
jQuery('document').ready(function() {
|
||||
// On menu click, change "active" state
|
||||
var menuEl = document.querySelector('.sidebar-nav'),
|
||||
liEls = menuEl.querySelectorAll('li')
|
||||
parentEl = null;
|
||||
|
||||
nodebb_admin.config = undefined;
|
||||
|
||||
nodebb_admin.prepare = function() {
|
||||
// Come back in 500ms if the config isn't ready yet
|
||||
if (nodebb_admin.config === undefined) {
|
||||
setTimeout(function() {
|
||||
nodebb_admin.prepare();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the fields on the page from the config
|
||||
var fields = document.querySelectorAll('#content [data-field]'),
|
||||
numFields = fields.length,
|
||||
saveBtn = document.getElementById('save'),
|
||||
x, key, inputType;
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
inputType = fields[x].getAttribute('type');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
if (nodebb_admin.config[key]) {
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
fields[x].value = nodebb_admin.config[key];
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
fields[x].checked = nodebb_admin.config[key] === '1' ? true : false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
if (nodebb_admin.config[key]) fields[x].value = nodebb_admin.config[key];
|
||||
menuEl.addEventListener('click', function(e) {
|
||||
parentEl = e.target.parentNode;
|
||||
if (parentEl.nodeName === 'LI') {
|
||||
for (var x = 0, numLis = liEls.length; x < numLis; x++) {
|
||||
if (liEls[x] !== parentEl) jQuery(liEls[x]).removeClass('active');
|
||||
else jQuery(parentEl).addClass('active');
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
saveBtn.addEventListener('click', function(e) {
|
||||
var key, value;
|
||||
e.preventDefault();
|
||||
socket.once('api:config.get', function(config) {
|
||||
app.config = config;
|
||||
});
|
||||
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
inputType = fields[x].getAttribute('type');
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'number':
|
||||
value = fields[x].value;
|
||||
break;
|
||||
socket.emit('api:config.get');
|
||||
|
||||
case 'checkbox':
|
||||
value = fields[x].checked ? '1' : '0';
|
||||
break;
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
value = fields[x].value;
|
||||
}
|
||||
|
||||
socket.emit('api:config.set', {
|
||||
key: key,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
socket.on('api:config.set', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Saved',
|
||||
message: 'Your changes to the NodeBB configuration have been saved.',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Not Saved',
|
||||
message: 'NodeBB encountered a problem saving your changes',
|
||||
type: 'danger'
|
||||
});
|
||||
}
|
||||
|
||||
nodebb_admin.remove = function(key) {
|
||||
socket.emit('api:config.remove', key);
|
||||
}
|
||||
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
// On menu click, change "active" state
|
||||
var menuEl = document.querySelector('.sidebar-nav'),
|
||||
liEls = menuEl.querySelectorAll('li')
|
||||
parentEl = null;
|
||||
|
||||
menuEl.addEventListener('click', function(e) {
|
||||
parentEl = e.target.parentNode;
|
||||
if (parentEl.nodeName === 'LI') {
|
||||
for (var x = 0, numLis = liEls.length; x < numLis; x++) {
|
||||
if (liEls[x] !== parentEl) jQuery(liEls[x]).removeClass('active');
|
||||
else jQuery(parentEl).addClass('active');
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
socket.once('api:config.get', function(config) {
|
||||
nodebb_admin.config = config;
|
||||
});
|
||||
|
||||
socket.emit('api:config.get');
|
||||
|
||||
socket.on('api:config.set', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Saved',
|
||||
message: 'Your changes to the NodeBB configuration have been saved.',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Not Saved',
|
||||
message: 'NodeBB encountered a problem saving your changes',
|
||||
type: 'danger'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
});
|
||||
@@ -1,194 +1,202 @@
|
||||
$(document).ready(function() {
|
||||
var createEl = document.getElementById('create'),
|
||||
createModal = $('#create-modal'),
|
||||
createSubmitBtn = document.getElementById('create-modal-go'),
|
||||
createNameEl = $('#create-group-name'),
|
||||
detailsModal = $('#group-details-modal'),
|
||||
detailsSearch = detailsModal.find('#group-details-search'),
|
||||
searchResults = detailsModal.find('#group-details-search-results'),
|
||||
groupMembersEl = detailsModal.find('ul.current_members'),
|
||||
detailsModalSave = detailsModal.find('.btn-primary'),
|
||||
searchDelay = undefined,
|
||||
listEl = $('#groups-list');
|
||||
define(function() {
|
||||
var Groups = {};
|
||||
|
||||
createEl.addEventListener('click', function() {
|
||||
createModal.modal('show');
|
||||
setTimeout(function() {
|
||||
createNameEl.focus();
|
||||
}, 250);
|
||||
}, false);
|
||||
Groups.init = function() {
|
||||
var createEl = document.getElementById('create'),
|
||||
createModal = $('#create-modal'),
|
||||
createSubmitBtn = document.getElementById('create-modal-go'),
|
||||
createNameEl = $('#create-group-name'),
|
||||
detailsModal = $('#group-details-modal'),
|
||||
detailsSearch = detailsModal.find('#group-details-search'),
|
||||
searchResults = detailsModal.find('#group-details-search-results'),
|
||||
groupMembersEl = detailsModal.find('ul.current_members'),
|
||||
detailsModalSave = detailsModal.find('.btn-primary'),
|
||||
searchDelay = undefined,
|
||||
listEl = $('#groups-list');
|
||||
|
||||
createSubmitBtn.addEventListener('click', function() {
|
||||
var submitObj = {
|
||||
name: createNameEl.val(),
|
||||
description: $('#create-group-desc').val()
|
||||
},
|
||||
errorEl = $('#create-modal-error'),
|
||||
errorText;
|
||||
createEl.addEventListener('click', function() {
|
||||
createModal.modal('show');
|
||||
setTimeout(function() {
|
||||
createNameEl.focus();
|
||||
}, 250);
|
||||
}, false);
|
||||
|
||||
socket.emit('api:groups.create', submitObj, function(err, data) {
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case 'group-exists':
|
||||
errorText = '<strong>Please choose another name</strong><p>There seems to be a group with this name already.</p>';
|
||||
break;
|
||||
case 'name-too-short':
|
||||
errorText = '<strong>Please specify a grou name</strong><p>A group name is required for administrative purposes.</p>';
|
||||
break;
|
||||
default:
|
||||
errorText = '<strong>Uh-Oh</strong><p>There was a problem creating your group. Please try again later!</p>';
|
||||
break;
|
||||
createSubmitBtn.addEventListener('click', function() {
|
||||
var submitObj = {
|
||||
name: createNameEl.val(),
|
||||
description: $('#create-group-desc').val()
|
||||
},
|
||||
errorEl = $('#create-modal-error'),
|
||||
errorText;
|
||||
|
||||
socket.emit('api:groups.create', submitObj, function(err, data) {
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case 'group-exists':
|
||||
errorText = '<strong>Please choose another name</strong><p>There seems to be a group with this name already.</p>';
|
||||
break;
|
||||
case 'name-too-short':
|
||||
errorText = '<strong>Please specify a grou name</strong><p>A group name is required for administrative purposes.</p>';
|
||||
break;
|
||||
default:
|
||||
errorText = '<strong>Uh-Oh</strong><p>There was a problem creating your group. Please try again later!</p>';
|
||||
break;
|
||||
}
|
||||
|
||||
errorEl.html(errorText).removeClass('hide');
|
||||
} else {
|
||||
createModal.modal('hide');
|
||||
errorEl.addClass('hide');
|
||||
createNameEl.val('');
|
||||
ajaxify.go('admin/groups');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
errorEl.html(errorText).removeClass('hide');
|
||||
} else {
|
||||
createModal.modal('hide');
|
||||
errorEl.addClass('hide');
|
||||
createNameEl.val('');
|
||||
ajaxify.go('admin/groups');
|
||||
listEl.on('click', 'button[data-action]', function() {
|
||||
var action = this.getAttribute('data-action'),
|
||||
gid = $(this).parents('li[data-gid]').attr('data-gid');
|
||||
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
bootbox.confirm('Are you sure you wish to delete this group?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:groups.delete', gid, function(err, data) {
|
||||
if (data === 'OK') ajaxify.go('admin/groups');
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'members':
|
||||
socket.emit('api:groups.get', gid, function(err, groupObj) {
|
||||
var formEl = detailsModal.find('form'),
|
||||
nameEl = formEl.find('#change-group-name'),
|
||||
descEl = formEl.find('#change-group-desc'),
|
||||
memberIcon = document.createElement('li'),
|
||||
numMembers = groupObj.members.length,
|
||||
membersFrag = document.createDocumentFragment(),
|
||||
memberIconImg, x;
|
||||
|
||||
|
||||
nameEl.val(groupObj.name);
|
||||
descEl.val(groupObj.description);
|
||||
|
||||
// Member list
|
||||
memberIcon.innerHTML = '<img /><span></span>';
|
||||
memberIconImg = memberIcon.querySelector('img');
|
||||
memberIconLabel = memberIcon.querySelector('span');
|
||||
if (numMembers > 0) {
|
||||
for (x = 0; x < numMembers; x++) {
|
||||
memberIconImg.src = groupObj.members[x].picture;
|
||||
memberIconLabel.innerHTML = groupObj.members[x].username;
|
||||
memberIcon.setAttribute('data-uid', groupObj.members[x].uid);
|
||||
membersFrag.appendChild(memberIcon.cloneNode(true));
|
||||
}
|
||||
groupMembersEl.html('');
|
||||
groupMembersEl[0].appendChild(membersFrag);
|
||||
}
|
||||
|
||||
detailsModal.attr('data-gid', groupObj.gid);
|
||||
detailsModal.modal('show');
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
listEl.on('click', 'button[data-action]', function() {
|
||||
var action = this.getAttribute('data-action'),
|
||||
gid = $(this).parents('li[data-gid]').attr('data-gid');
|
||||
detailsSearch.on('keyup', function() {
|
||||
var searchEl = this;
|
||||
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
bootbox.confirm('Are you sure you wish to delete this group?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:groups.delete', gid, function(err, data) {
|
||||
if (data === 'OK') ajaxify.go('admin/groups');
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'members':
|
||||
socket.emit('api:groups.get', gid, function(err, groupObj) {
|
||||
var formEl = detailsModal.find('form'),
|
||||
nameEl = formEl.find('#change-group-name'),
|
||||
descEl = formEl.find('#change-group-desc'),
|
||||
memberIcon = document.createElement('li'),
|
||||
numMembers = groupObj.members.length,
|
||||
membersFrag = document.createDocumentFragment(),
|
||||
memberIconImg, x;
|
||||
if (searchDelay) clearTimeout(searchDelay);
|
||||
|
||||
searchDelay = setTimeout(function() {
|
||||
var searchText = searchEl.value,
|
||||
resultsEl = document.getElementById('group-details-search-results'),
|
||||
foundUser = document.createElement('li'),
|
||||
foundUserImg, foundUserLabel;
|
||||
|
||||
nameEl.val(groupObj.name);
|
||||
descEl.val(groupObj.description);
|
||||
foundUser.innerHTML = '<img /><span></span>';
|
||||
foundUserImg = foundUser.getElementsByTagName('img')[0];
|
||||
foundUserLabel = foundUser.getElementsByTagName('span')[0];
|
||||
|
||||
// Member list
|
||||
memberIcon.innerHTML = '<img /><span></span>';
|
||||
memberIconImg = memberIcon.querySelector('img');
|
||||
memberIconLabel = memberIcon.querySelector('span');
|
||||
if (numMembers > 0) {
|
||||
for (x = 0; x < numMembers; x++) {
|
||||
memberIconImg.src = groupObj.members[x].picture;
|
||||
memberIconLabel.innerHTML = groupObj.members[x].username;
|
||||
memberIcon.setAttribute('data-uid', groupObj.members[x].uid);
|
||||
membersFrag.appendChild(memberIcon.cloneNode(true));
|
||||
socket.emit('api:admin.user.search', searchText, function(err, results) {
|
||||
if (!err && results && results.length > 0) {
|
||||
var numResults = results.length,
|
||||
resultsSlug = document.createDocumentFragment(),
|
||||
x;
|
||||
if (numResults > 4) numResults = 4;
|
||||
for (x = 0; x < numResults; x++) {
|
||||
foundUserImg.src = results[x].picture;
|
||||
foundUserLabel.innerHTML = results[x].username;
|
||||
foundUser.setAttribute('title', results[x].username);
|
||||
foundUser.setAttribute('data-uid', results[x].uid);
|
||||
resultsSlug.appendChild(foundUser.cloneNode(true));
|
||||
}
|
||||
groupMembersEl.html('');
|
||||
groupMembersEl[0].appendChild(membersFrag);
|
||||
}
|
||||
|
||||
detailsModal.attr('data-gid', groupObj.gid);
|
||||
detailsModal.modal('show');
|
||||
resultsEl.innerHTML = '';
|
||||
resultsEl.appendChild(resultsSlug);
|
||||
} else resultsEl.innerHTML = '<li>No Users Found</li>';
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
detailsSearch.on('keyup', function() {
|
||||
var searchEl = this;
|
||||
|
||||
if (searchDelay) clearTimeout(searchDelay);
|
||||
|
||||
searchDelay = setTimeout(function() {
|
||||
var searchText = searchEl.value,
|
||||
resultsEl = document.getElementById('group-details-search-results'),
|
||||
foundUser = document.createElement('li'),
|
||||
foundUserImg, foundUserLabel;
|
||||
|
||||
foundUser.innerHTML = '<img /><span></span>';
|
||||
foundUserImg = foundUser.getElementsByTagName('img')[0];
|
||||
foundUserLabel = foundUser.getElementsByTagName('span')[0];
|
||||
|
||||
socket.emit('api:admin.user.search', searchText, function(err, results) {
|
||||
if (!err && results && results.length > 0) {
|
||||
var numResults = results.length,
|
||||
resultsSlug = document.createDocumentFragment(),
|
||||
x;
|
||||
if (numResults > 4) numResults = 4;
|
||||
for (x = 0; x < numResults; x++) {
|
||||
foundUserImg.src = results[x].picture;
|
||||
foundUserLabel.innerHTML = results[x].username;
|
||||
foundUser.setAttribute('title', results[x].username);
|
||||
foundUser.setAttribute('data-uid', results[x].uid);
|
||||
resultsSlug.appendChild(foundUser.cloneNode(true));
|
||||
}
|
||||
|
||||
resultsEl.innerHTML = '';
|
||||
resultsEl.appendChild(resultsSlug);
|
||||
} else resultsEl.innerHTML = '<li>No Users Found</li>';
|
||||
});
|
||||
}, 200);
|
||||
});
|
||||
|
||||
searchResults.on('click', 'li[data-uid]', function() {
|
||||
var userLabel = this,
|
||||
uid = parseInt(this.getAttribute('data-uid')),
|
||||
gid = detailsModal.attr('data-gid'),
|
||||
members = [];
|
||||
|
||||
groupMembersEl.find('li[data-uid]').each(function() {
|
||||
members.push(parseInt(this.getAttribute('data-uid')));
|
||||
}, 200);
|
||||
});
|
||||
|
||||
if (members.indexOf(uid) === -1) {
|
||||
socket.emit('api:groups.join', {
|
||||
searchResults.on('click', 'li[data-uid]', function() {
|
||||
var userLabel = this,
|
||||
uid = parseInt(this.getAttribute('data-uid')),
|
||||
gid = detailsModal.attr('data-gid'),
|
||||
members = [];
|
||||
|
||||
groupMembersEl.find('li[data-uid]').each(function() {
|
||||
members.push(parseInt(this.getAttribute('data-uid')));
|
||||
});
|
||||
|
||||
if (members.indexOf(uid) === -1) {
|
||||
socket.emit('api:groups.join', {
|
||||
gid: gid,
|
||||
uid: uid
|
||||
}, function(err, data) {
|
||||
if (!err) {
|
||||
groupMembersEl.append(userLabel.cloneNode(true));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
groupMembersEl.on('click', 'li[data-uid]', function() {
|
||||
var uid = this.getAttribute('data-uid'),
|
||||
gid = detailsModal.attr('data-gid');
|
||||
|
||||
socket.emit('api:groups.leave', {
|
||||
gid: gid,
|
||||
uid: uid
|
||||
}, function(err, data) {
|
||||
if (!err) {
|
||||
groupMembersEl.append(userLabel.cloneNode(true));
|
||||
groupMembersEl.find('li[data-uid="' + uid + '"]').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
groupMembersEl.on('click', 'li[data-uid]', function() {
|
||||
var uid = this.getAttribute('data-uid'),
|
||||
gid = detailsModal.attr('data-gid');
|
||||
|
||||
socket.emit('api:groups.leave', {
|
||||
gid: gid,
|
||||
uid: uid
|
||||
}, function(err, data) {
|
||||
if (!err) {
|
||||
groupMembersEl.find('li[data-uid="' + uid + '"]').remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
detailsModalSave.on('click', function() {
|
||||
var formEl = detailsModal.find('form'),
|
||||
nameEl = formEl.find('#change-group-name'),
|
||||
descEl = formEl.find('#change-group-desc'),
|
||||
gid = detailsModal.attr('data-gid');
|
||||
detailsModalSave.on('click', function() {
|
||||
var formEl = detailsModal.find('form'),
|
||||
nameEl = formEl.find('#change-group-name'),
|
||||
descEl = formEl.find('#change-group-desc'),
|
||||
gid = detailsModal.attr('data-gid');
|
||||
|
||||
socket.emit('api:groups.update', {
|
||||
gid: gid,
|
||||
values: {
|
||||
name: nameEl.val(),
|
||||
description: descEl.val()
|
||||
}
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
detailsModal.modal('hide');
|
||||
ajaxify.go('admin/groups');
|
||||
}
|
||||
socket.emit('api:groups.update', {
|
||||
gid: gid,
|
||||
values: {
|
||||
name: nameEl.val(),
|
||||
description: descEl.val()
|
||||
}
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
detailsModal.on('hidden.bs.modal', function() {
|
||||
ajaxify.go('admin/groups');
|
||||
});
|
||||
detailsModal.modal('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return Groups;
|
||||
});
|
||||
@@ -1,25 +1,28 @@
|
||||
define(function() {
|
||||
var Admin = {};
|
||||
|
||||
(function() {
|
||||
Admin.init = function() {
|
||||
ajaxify.register_events(['api:get_all_rooms']);
|
||||
socket.on('api:get_all_rooms', function(data) {
|
||||
|
||||
ajaxify.register_events(['api:get_all_rooms']);
|
||||
socket.on('api:get_all_rooms', function(data) {
|
||||
var active_users = document.getElementById('active_users'),
|
||||
total = 0;
|
||||
active_users.innerHTML = '';
|
||||
|
||||
var active_users = document.getElementById('active_users'),
|
||||
total = 0;
|
||||
active_users.innerHTML = '';
|
||||
|
||||
for (var room in data) {
|
||||
if (room !== '') {
|
||||
var count = data[room].length;
|
||||
total += count;
|
||||
active_users.innerHTML = active_users.innerHTML + "<div class='alert alert-success'><strong>" + room + "</strong> " + count + " active user" + (count > 1 ? "s" : "") + "</div>";
|
||||
for (var room in data) {
|
||||
if (room !== '') {
|
||||
var count = data[room].length;
|
||||
total += count;
|
||||
active_users.innerHTML = active_users.innerHTML + "<div class='alert alert-success'><strong>" + room + "</strong> " + count + " active user" + (count > 1 ? "s" : "") + "</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('connections').innerHTML = total;
|
||||
});
|
||||
document.getElementById('connections').innerHTML = total;
|
||||
});
|
||||
|
||||
app.enter_room('admin');
|
||||
socket.emit('api:get_all_rooms');
|
||||
app.enter_room('admin');
|
||||
socket.emit('api:get_all_rooms');
|
||||
};
|
||||
|
||||
}());
|
||||
return Admin;
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
var nodebb_admin = nodebb_admin || {};
|
||||
|
||||
(function() {
|
||||
var plugins = {
|
||||
define(function() {
|
||||
var Plugins = {
|
||||
init: function() {
|
||||
var pluginsList = $('.plugins'),
|
||||
numPlugins = pluginsList[0].querySelectorAll('li').length,
|
||||
@@ -31,8 +29,5 @@ var nodebb_admin = nodebb_admin || {};
|
||||
}
|
||||
};
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
nodebb_admin.plugins = plugins;
|
||||
nodebb_admin.plugins.init();
|
||||
});
|
||||
})();
|
||||
return Plugins;
|
||||
});
|
||||
88
public/src/forum/admin/settings.js
Normal file
88
public/src/forum/admin/settings.js
Normal file
@@ -0,0 +1,88 @@
|
||||
define(['uploader'], function(uploader) {
|
||||
var Settings = {};
|
||||
|
||||
Settings.init = function() {
|
||||
Settings.prepare();
|
||||
};
|
||||
|
||||
Settings.prepare = function() {
|
||||
// Come back in 125ms if the config isn't ready yet
|
||||
if (!app.config) {
|
||||
setTimeout(function() {
|
||||
Settings.prepare();
|
||||
}, 125);
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the fields on the page from the config
|
||||
var fields = document.querySelectorAll('#content [data-field]'),
|
||||
numFields = fields.length,
|
||||
saveBtn = document.getElementById('save'),
|
||||
x, key, inputType;
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
inputType = fields[x].getAttribute('type');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
if (app.config[key]) {
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
fields[x].value = app.config[key];
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
fields[x].checked = app.config[key] === '1' ? true : false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
if (app.config[key]) fields[x].value = app.config[key];
|
||||
}
|
||||
}
|
||||
|
||||
saveBtn.addEventListener('click', function(e) {
|
||||
var key, value;
|
||||
e.preventDefault();
|
||||
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
inputType = fields[x].getAttribute('type');
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'number':
|
||||
value = fields[x].value;
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
value = fields[x].checked ? '1' : '0';
|
||||
break;
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
value = fields[x].value;
|
||||
}
|
||||
|
||||
socket.emit('api:config.set', {
|
||||
key: key,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#uploadLogoBtn').on('click', function() {
|
||||
|
||||
uploader.open(config.relative_path + '/admin/uploadlogo', function(image) {
|
||||
$('#logoUrl').val(image);
|
||||
});
|
||||
|
||||
uploader.hideAlerts();
|
||||
});
|
||||
};
|
||||
|
||||
Settings.remove = function(key) {
|
||||
socket.emit('api:config.remove', key);
|
||||
};
|
||||
|
||||
return Settings;
|
||||
});
|
||||
@@ -1,13 +1,113 @@
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
define(function() {
|
||||
var Themes = {};
|
||||
|
||||
var themes = {};
|
||||
Themes.init = function() {
|
||||
var scriptEl = document.createElement('script');
|
||||
scriptEl.src = 'http://api.bootswatch.com/3/?callback=bootswatchListener';
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
themes.render = function(bootswatch) {
|
||||
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
|
||||
installedThemeContainer = document.querySelector('#installed_themes'),
|
||||
themeEvent = function(e) {
|
||||
if (e.target.hasAttribute('data-action')) {
|
||||
switch (e.target.getAttribute('data-action')) {
|
||||
case 'preview':
|
||||
var cssSrc = $(e.target).parents('li').attr('data-css'),
|
||||
cssEl = document.getElementById('base-theme');
|
||||
|
||||
cssEl.href = cssSrc;
|
||||
break;
|
||||
case 'use':
|
||||
var parentEl = $(e.target).parents('li'),
|
||||
themeType = parentEl.attr('data-type'),
|
||||
cssSrc = parentEl.attr('data-css'),
|
||||
themeId = parentEl.attr('data-theme');
|
||||
|
||||
socket.emit('api:admin.theme.set', {
|
||||
type: themeType,
|
||||
id: themeId,
|
||||
src: cssSrc
|
||||
}, function(err) {
|
||||
app.alert({
|
||||
alert_id: 'admin:theme',
|
||||
type: 'success',
|
||||
title: 'Theme Changed',
|
||||
message: 'You have successfully changed your NodeBB\'s theme. Please restart to see the changes.',
|
||||
timeout: 2500
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
bootstrapThemeContainer.addEventListener('click', themeEvent);
|
||||
installedThemeContainer.addEventListener('click', themeEvent);
|
||||
|
||||
var revertEl = document.getElementById('revert_theme');
|
||||
revertEl.addEventListener('click', function() {
|
||||
bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:admin.theme.set', {
|
||||
type: 'local',
|
||||
id: 'nodebb-theme-cerulean'
|
||||
}, function(err) {
|
||||
app.alert({
|
||||
alert_id: 'admin:theme',
|
||||
type: 'success',
|
||||
title: 'Theme Changed',
|
||||
message: 'You have successfully reverted your NodeBB back to it\'s default theme. Please restart to see the changes.',
|
||||
timeout: 3500
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
|
||||
// Installed Themes
|
||||
socket.emit('api:admin.themes.getInstalled', function(themes) {
|
||||
var instListEl = document.getElementById('installed_themes'),
|
||||
themeFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li');
|
||||
liEl.setAttribute('data-type', 'local');
|
||||
|
||||
if (themes.length > 0) {
|
||||
for (var x = 0, numThemes = themes.length; x < numThemes; x++) {
|
||||
liEl.setAttribute('data-theme', themes[x].id);
|
||||
liEl.innerHTML = '<img src="' + (themes[x].screenshot ? '/css/previews/' + themes[x].id : RELATIVE_PATH + '/images/themes/default.png') + '" />' +
|
||||
'<div>' +
|
||||
'<div class="pull-right">' +
|
||||
'<button class="btn btn-primary" data-action="use">Use</button> ' +
|
||||
'<button class="btn btn-default" data-action="preview">Preview</button>' +
|
||||
'</div>' +
|
||||
'<h4>' + themes[x].name + '</h4>' +
|
||||
'<p>' +
|
||||
themes[x].description +
|
||||
(themes[x].url ? ' (<a href="' + themes[x].url + '">Homepage</a>)' : '') +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'<div class="clear">';
|
||||
themeFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
// No themes found
|
||||
liEl.className = 'no-themes';
|
||||
liEl.innerHTML = 'No installed themes found';
|
||||
themeFrag.appendChild(liEl);
|
||||
}
|
||||
|
||||
instListEl.innerHTML = '';
|
||||
instListEl.appendChild(themeFrag);
|
||||
});
|
||||
}
|
||||
|
||||
Themes.render = function(bootswatch) {
|
||||
var themeFrag = document.createDocumentFragment(),
|
||||
themeEl = document.createElement('li'),
|
||||
themeContainer = document.querySelector('#bootstrap_themes'),
|
||||
numThemes = bootswatch.themes.length;
|
||||
|
||||
themeEl.setAttribute('data-type', 'bootswatch');
|
||||
|
||||
for (var x = 0; x < numThemes; x++) {
|
||||
var theme = bootswatch.themes[x];
|
||||
themeEl.setAttribute('data-css', theme.cssMin);
|
||||
@@ -28,91 +128,5 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
themeContainer.appendChild(themeFrag);
|
||||
}
|
||||
|
||||
nodebb_admin.themes = themes;
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
|
||||
|
||||
(function() {
|
||||
var scriptEl = document.createElement('script');
|
||||
scriptEl.src = 'http://api.bootswatch.com/3/?callback=nodebb_admin.themes.render';
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
|
||||
installedThemeContainer = document.querySelector('#installed_themes'),
|
||||
themeEvent = function(e) {
|
||||
if (e.target.hasAttribute('data-action')) {
|
||||
switch (e.target.getAttribute('data-action')) {
|
||||
case 'preview':
|
||||
var cssSrc = $(e.target).parents('li').attr('data-css'),
|
||||
cssEl = document.getElementById('base-theme');
|
||||
|
||||
cssEl.href = cssSrc;
|
||||
break;
|
||||
case 'use':
|
||||
var parentEl = $(e.target).parents('li'),
|
||||
cssSrc = parentEl.attr('data-css'),
|
||||
cssName = parentEl.attr('data-theme');
|
||||
socket.emit('api:config.set', {
|
||||
key: 'theme:id',
|
||||
value: 'bootswatch:' + cssName
|
||||
});
|
||||
socket.emit('api:config.set', {
|
||||
key: 'theme:src',
|
||||
value: cssSrc
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
bootstrapThemeContainer.addEventListener('click', themeEvent);
|
||||
installedThemeContainer.addEventListener('click', themeEvent);
|
||||
|
||||
var revertEl = document.getElementById('revert_theme');
|
||||
revertEl.addEventListener('click', function() {
|
||||
bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) {
|
||||
if (confirm) {
|
||||
nodebb_admin.remove('theme:id');
|
||||
nodebb_admin.remove('theme:src');
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
|
||||
// Installed Themes
|
||||
socket.emit('api:admin.themes.getInstalled', function(themes) {
|
||||
var instListEl = document.getElementById('installed_themes'),
|
||||
themeFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li');
|
||||
|
||||
if (themes.length > 0) {
|
||||
for (var x = 0, numThemes = themes.length; x < numThemes; x++) {
|
||||
liEl.setAttribute('data-theme', themes[x].id);
|
||||
liEl.setAttribute('data-css', themes[x].src);
|
||||
liEl.innerHTML = '<img src="' + themes[x].screenshot + '" />' +
|
||||
'<div>' +
|
||||
'<div class="pull-right">' +
|
||||
'<button class="btn btn-primary" data-action="use">Use</button> ' +
|
||||
'<button class="btn btn-default" data-action="preview">Preview</button>' +
|
||||
'</div>' +
|
||||
'<h4>' + themes[x].name + '</h4>' +
|
||||
'<p>' +
|
||||
themes[x].description +
|
||||
(themes[x].url ? ' (<a href="' + themes[x].url + '">Homepage</a>)' : '') +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'<div class="clear">';
|
||||
themeFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
// No themes found
|
||||
liEl.className = 'no-themes';
|
||||
liEl.innerHTML = 'No installed themes found';
|
||||
themeFrag.appendChild(liEl);
|
||||
}
|
||||
|
||||
instListEl.innerHTML = '';
|
||||
instListEl.appendChild(themeFrag);
|
||||
});
|
||||
})();
|
||||
return Themes;
|
||||
});
|
||||
@@ -1,127 +1,153 @@
|
||||
$(document).ready(function() {
|
||||
var topicsListEl = document.querySelector('.topics'),
|
||||
loadMoreEl = document.getElementById('topics_loadmore');
|
||||
define(function() {
|
||||
var Topics = {};
|
||||
|
||||
$(topicsListEl).on('click', '[data-action]', function() {
|
||||
var $this = $(this),
|
||||
action = this.getAttribute('data-action'),
|
||||
tid = $this.parents('[data-tid]').attr('data-tid');
|
||||
Topics.init = function() {
|
||||
var topicsListEl = document.querySelector('.topics'),
|
||||
loadMoreEl = document.getElementById('topics_loadmore');
|
||||
|
||||
switch (action) {
|
||||
case 'pin':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.pin', {
|
||||
tid: tid
|
||||
this.resolveButtonStates();
|
||||
|
||||
$(topicsListEl).on('click', '[data-action]', function() {
|
||||
var $this = $(this),
|
||||
action = this.getAttribute('data-action'),
|
||||
tid = $this.parents('[data-tid]').attr('data-tid');
|
||||
|
||||
switch (action) {
|
||||
case 'pin':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.pin', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.unpin', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
case 'lock':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.lock', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.unlock', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.delete', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.restore', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
loadMoreEl.addEventListener('click', function() {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
var topics = document.querySelectorAll('.topics li[data-tid]'),
|
||||
lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
||||
|
||||
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics';
|
||||
socket.emit('api:admin.topics.getMore', {
|
||||
limit: 10,
|
||||
after: lastTid
|
||||
}, function(topics) {
|
||||
var btnEl = document.getElementById('topics_loadmore');
|
||||
|
||||
topics = JSON.parse(topics);
|
||||
if (topics.length > 0) {
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
topicsListEl = document.querySelector('.topics');
|
||||
|
||||
// Fix relative paths
|
||||
html = html.replace(/\{relative_path\}/g, RELATIVE_PATH);
|
||||
|
||||
topicsListEl.innerHTML += html;
|
||||
|
||||
Topics.resolveButtonStates();
|
||||
|
||||
btnEl.innerHTML = 'Load More Topics';
|
||||
$('span.timeago').timeago();
|
||||
} else {
|
||||
// Exhausted all topics
|
||||
btnEl.className += ' disabled';
|
||||
btnEl.innerHTML = 'No more topics';
|
||||
}
|
||||
});
|
||||
else socket.emit('api:topic.unpin', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
case 'lock':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.lock', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.unlock', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.delete', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.restore', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
}
|
||||
}, false);
|
||||
|
||||
socket.on('api:topic.pin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unpin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.lock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unlock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.delete', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
$(btnEl).siblings('[data-action="lock"]').addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.restore', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
$(btnEl).siblings('[data-action="lock"]').removeClass('active');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Topics.resolveButtonStates = function() {
|
||||
// Resolve proper button state for all topics
|
||||
var topicsListEl = document.querySelector('.topics'),
|
||||
topicEls = topicsListEl.querySelectorAll('li'),
|
||||
numTopics = topicEls.length;
|
||||
for (var x = 0; x < numTopics; x++) {
|
||||
if (topicEls[x].getAttribute('data-pinned') === '1') {
|
||||
topicEls[x].querySelector('[data-action="pin"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-pinned');
|
||||
}
|
||||
if (topicEls[x].getAttribute('data-locked') === '1') {
|
||||
topicEls[x].querySelector('[data-action="lock"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-locked');
|
||||
}
|
||||
if (topicEls[x].getAttribute('data-deleted') === '1') {
|
||||
topicEls[x].querySelector('[data-action="delete"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-deleted');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loadMoreEl.addEventListener('click', function() {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
var topics = document.querySelectorAll('.topics li[data-tid]'),
|
||||
lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
||||
|
||||
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics';
|
||||
socket.emit('api:admin.topics.getMore', {
|
||||
limit: 10,
|
||||
after: lastTid
|
||||
}, function(topics) {
|
||||
var btnEl = document.getElementById('topics_loadmore');
|
||||
|
||||
topics = JSON.parse(topics);
|
||||
if (topics.length > 0) {
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
topicsListEl = document.querySelector('.topics');
|
||||
|
||||
topicsListEl.innerHTML += html;
|
||||
btnEl.innerHTML = 'Load More Topics';
|
||||
} else {
|
||||
// Exhausted all topics
|
||||
btnEl.className += ' disabled';
|
||||
btnEl.innerHTML = 'No more topics';
|
||||
}
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Resolve proper button state for all topics
|
||||
var topicEls = topicsListEl.querySelectorAll('li'),
|
||||
numTopics = topicEls.length;
|
||||
for (var x = 0; x < numTopics; x++) {
|
||||
if (topicEls[x].getAttribute('data-pinned') === '1') topicEls[x].querySelector('[data-action="pin"]').className += ' active';
|
||||
if (topicEls[x].getAttribute('data-locked') === '1') topicEls[x].querySelector('[data-action="lock"]').className += ' active';
|
||||
if (topicEls[x].getAttribute('data-deleted') === '1') topicEls[x].querySelector('[data-action="delete"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-pinned');
|
||||
topicEls[x].removeAttribute('data-locked');
|
||||
topicEls[x].removeAttribute('data-deleted');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.pin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unpin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.lock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unlock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.delete', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.restore', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
return Topics;
|
||||
});
|
||||
@@ -1,170 +1,174 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Users = {};
|
||||
|
||||
var yourid = templates.get('yourid');
|
||||
Users.init = function() {
|
||||
var yourid = templates.get('yourid');
|
||||
|
||||
function isUserAdmin(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-admin') !== "0");
|
||||
}
|
||||
function isUserAdmin(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
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 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');
|
||||
}
|
||||
function getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
}
|
||||
|
||||
function updateUserButtons() {
|
||||
jQuery('.ban-btn').each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var uid = getUID(banBtn);
|
||||
if (isUserAdmin(banBtn) || uid === yourid)
|
||||
banBtn.addClass('disabled');
|
||||
else if (isUserBanned(banBtn))
|
||||
banBtn.addClass('btn-warning');
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initUsers() {
|
||||
|
||||
updateUserButtons();
|
||||
|
||||
$('#users-container').on('click', '.ban-btn', 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);
|
||||
function updateUserButtons() {
|
||||
jQuery('.ban-btn').each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var uid = getUID(banBtn);
|
||||
if (isUserAdmin(banBtn) || uid === yourid)
|
||||
banBtn.addClass('disabled');
|
||||
else if (isUserBanned(banBtn))
|
||||
banBtn.addClass('btn-warning');
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
parent.attr('data-banned', 0);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initUsers() {
|
||||
|
||||
updateUserButtons();
|
||||
|
||||
$('#users-container').on('click', '.ban-btn', 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) {
|
||||
if (confirm) {
|
||||
socket.emit('api:admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
|
||||
var timeoutId = 0,
|
||||
loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#search-user').on('keyup', function() {
|
||||
if (timeoutId !== 0) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = 0;
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(function() {
|
||||
var username = $('#search-user').val();
|
||||
|
||||
jQuery('.icon-spinner').removeClass('none');
|
||||
socket.emit('api:admin.user.search', username);
|
||||
|
||||
}, 250);
|
||||
});
|
||||
|
||||
initUsers();
|
||||
|
||||
socket.removeAllListeners('api:admin.user.search');
|
||||
|
||||
socket.on('api:admin.user.search', function(data) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('.users');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
jQuery('.icon-spinner').addClass('none');
|
||||
|
||||
if (data && data.length === 0) {
|
||||
$('#user-notfound-notify').html('User not found!')
|
||||
.show()
|
||||
.addClass('label-danger')
|
||||
.removeClass('label-success');
|
||||
} else {
|
||||
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
$('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!')
|
||||
.show()
|
||||
.addClass('label-success')
|
||||
.removeClass('label-danger');
|
||||
}
|
||||
|
||||
initUsers();
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: users
|
||||
});
|
||||
$('#users-container').append(html);
|
||||
updateUserButtons();
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if (active === 'latest') {
|
||||
set = 'users:joindate';
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if (active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
}
|
||||
|
||||
if (set) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: $('#users-container').children().length
|
||||
}, function(data) {
|
||||
if (data.users.length) {
|
||||
onUsersLoaded(data.users);
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
|
||||
var timeoutId = 0,
|
||||
loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#search-user').on('keyup', function() {
|
||||
if (timeoutId !== 0) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = 0;
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(function() {
|
||||
var username = $('#search-user').val();
|
||||
|
||||
jQuery('.icon-spinner').removeClass('none');
|
||||
socket.emit('api:admin.user.search', username);
|
||||
|
||||
}, 250);
|
||||
});
|
||||
|
||||
initUsers();
|
||||
|
||||
socket.removeAllListeners('api:admin.user.search');
|
||||
|
||||
socket.on('api:admin.user.search', function(data) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('.users');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
jQuery('.icon-spinner').addClass('none');
|
||||
|
||||
if (data && data.length === 0) {
|
||||
$('#user-notfound-notify').html('User not found!')
|
||||
.show()
|
||||
.addClass('label-danger')
|
||||
.removeClass('label-success');
|
||||
} else {
|
||||
$('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!')
|
||||
.show()
|
||||
.addClass('label-success')
|
||||
.removeClass('label-danger');
|
||||
}
|
||||
|
||||
initUsers();
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: users
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
$('#users-container').append(html);
|
||||
updateUserButtons();
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if (active === 'latest') {
|
||||
set = 'users:joindate';
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if (active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
}
|
||||
|
||||
if (set) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: $('#users-container').children().length
|
||||
}, function(data) {
|
||||
if (data.users.length) {
|
||||
onUsersLoaded(data.users);
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
return Users;
|
||||
});
|
||||
@@ -1,47 +1,92 @@
|
||||
(function () {
|
||||
var cid = templates.get('category_id'),
|
||||
room = 'category_' + cid,
|
||||
twitterEl = document.getElementById('twitter-intent'),
|
||||
facebookEl = document.getElementById('facebook-share'),
|
||||
googleEl = document.getElementById('google-share'),
|
||||
twitter_url = templates.get('twitter-intent-url'),
|
||||
facebook_url = templates.get('facebook-share-url'),
|
||||
google_url = templates.get('google-share-url'),
|
||||
loadingMoreTopics = false;
|
||||
define(function () {
|
||||
var Category = {};
|
||||
|
||||
app.enter_room(room);
|
||||
Category.init = function() {
|
||||
var cid = templates.get('category_id'),
|
||||
room = 'category_' + cid,
|
||||
twitterEl = jQuery('#twitter-intent'),
|
||||
facebookEl = jQuery('#facebook-share'),
|
||||
googleEl = jQuery('#google-share'),
|
||||
twitter_url = templates.get('twitter-intent-url'),
|
||||
facebook_url = templates.get('facebook-share-url'),
|
||||
google_url = templates.get('google-share-url'),
|
||||
loadingMoreTopics = false;
|
||||
|
||||
twitterEl.addEventListener('click', function () {
|
||||
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
facebookEl.addEventListener('click', function () {
|
||||
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
googleEl.addEventListener('click', function () {
|
||||
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
app.enter_room(room);
|
||||
|
||||
var new_post = document.getElementById('new_post');
|
||||
new_post.onclick = function () {
|
||||
require(['composer'], function (cmp) {
|
||||
cmp.push(0, cid);
|
||||
twitterEl.on('click', function () {
|
||||
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
||||
return false;
|
||||
});
|
||||
facebookEl.on('click', function () {
|
||||
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
|
||||
return false;
|
||||
});
|
||||
googleEl.on('click', function () {
|
||||
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic'
|
||||
]);
|
||||
$('#new_post').on('click', function () {
|
||||
require(['composer'], function (cmp) {
|
||||
cmp.push(0, cid);
|
||||
});
|
||||
});
|
||||
|
||||
function onNewTopic(data) {
|
||||
ajaxify.register_events([
|
||||
'event:new_topic'
|
||||
]);
|
||||
|
||||
socket.on('event:new_topic', Category.onNewTopic);
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
socket.on('api:categories.getRecentReplies', function (posts) {
|
||||
if (!posts || posts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
||||
li.setAttribute('data-pid', posts[i].pid);
|
||||
|
||||
|
||||
li.innerHTML = '<a href="/user/' + posts[i].userslug + '"><img title="' + posts[i].username + '" style="width: 48px; height: 48px; /*temporary*/" class="img-rounded" src="' + posts[i].picture + '" class="" /></a>' +
|
||||
'<a href="/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">' +
|
||||
'<strong><span>'+ posts[i].username + '</span></strong>' +
|
||||
'<p>' +
|
||||
posts[i].content +
|
||||
'</p>' +
|
||||
'</a>' +
|
||||
'<span class="timeago pull-right" title="' + posts[i].relativeTime + '"></span>';
|
||||
|
||||
frag.appendChild(li.cloneNode(true));
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
$('#category_recent_replies span.timeago').timeago();
|
||||
});
|
||||
|
||||
$(window).off('scroll').on('scroll', function (ev) {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
Category.loadMoreTopics(cid);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Category.onNewTopic = function(data) {
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: [data]
|
||||
}),
|
||||
topic = $(html),
|
||||
container = $('#topics-container'),
|
||||
topics = $('#topics-container').children(),
|
||||
topics = $('#topics-container').children('.category-item'),
|
||||
numTopics = topics.length;
|
||||
|
||||
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
|
||||
@@ -60,44 +105,29 @@
|
||||
topic.hide().fadeIn('slow');
|
||||
}
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
|
||||
|
||||
addActiveUser(data);
|
||||
|
||||
$('#topics-container span.timeago').timeago();
|
||||
}
|
||||
|
||||
socket.on('event:new_topic', onNewTopic);
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
socket.on('api:categories.getRecentReplies', function (posts) {
|
||||
if (!posts || posts.length === 0) {
|
||||
return;
|
||||
function addActiveUser(data) {
|
||||
var activeUser = $('.category-sidebar .active-users').find('a[data-uid="' + data.uid + '"]');
|
||||
if(!activeUser.length) {
|
||||
var newUser = templates.prepare(templates['category'].blocks['active_users']).parse({
|
||||
active_users: [{
|
||||
uid: data.uid,
|
||||
username: data.username,
|
||||
userslug: data.userslug,
|
||||
picture: data.teaser_userpicture
|
||||
}]
|
||||
});
|
||||
$(newUser).appendTo($('.category-sidebar .active-users'));
|
||||
}
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
||||
li.setAttribute('data-pid', posts[i].pid);
|
||||
|
||||
|
||||
li.innerHTML = '<a href="/user/' + posts[i].userslug + '"><img title="' + posts[i].username + '" style="width: 48px; height: 48px; /*temporary*/" class="img-rounded" src="' + posts[i].picture + '" class="" /></a>' +
|
||||
'<a href="/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">' +
|
||||
'<p>' +
|
||||
posts[i].content +
|
||||
'</p>' +
|
||||
'<p class="meta"><strong>' + posts[i].username + '</strong></span> -<span class="timeago" title="' + posts[i].relativeTime + '"></span></p>' +
|
||||
'</a>';
|
||||
|
||||
frag.appendChild(li.cloneNode(true));
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
$('#category_recent_replies span.timeago').timeago();
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
Category.onTopicsLoaded = function(topics) {
|
||||
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
@@ -113,26 +143,18 @@
|
||||
}
|
||||
|
||||
|
||||
function loadMoreTopics(cid) {
|
||||
Category.loadMoreTopics = function(cid) {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:category.loadMore', {
|
||||
cid: cid,
|
||||
after: $('#topics-container').children().length
|
||||
after: $('#topics-container').children('.category-item').length
|
||||
}, function (data) {
|
||||
if (data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
Category.onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function (ev) {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics(cid);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
return Category;
|
||||
});
|
||||
@@ -1,7 +1,13 @@
|
||||
(function() {
|
||||
$(document).ready(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var AccountHeader = {};
|
||||
|
||||
AccountHeader.init = function() {
|
||||
header.init();
|
||||
|
||||
$('.user-favourite-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
});
|
||||
}());
|
||||
};
|
||||
|
||||
return AccountHeader;
|
||||
});
|
||||
@@ -1,18 +1,20 @@
|
||||
(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Followers = {};
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followersCount = templates.get('followersCount');
|
||||
Followers.init = function() {
|
||||
header.init();
|
||||
|
||||
$(document).ready(function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followersCount = templates.get('followersCount');
|
||||
|
||||
if (parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
if (parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
app.addCommasToNumbers();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
}());
|
||||
return Followers;
|
||||
});
|
||||
@@ -1,40 +1,17 @@
|
||||
(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Following = {};
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followingCount = templates.get('followingCount');
|
||||
Following.init = function() {
|
||||
header.init();
|
||||
|
||||
$(document).ready(function() {
|
||||
var followingCount = templates.get('followingCount');
|
||||
|
||||
if (parseInt(followingCount, 10) === 0) {
|
||||
$('#no-following-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
if (yourid !== theirid) {
|
||||
$('.unfollow-btn').hide();
|
||||
} else {
|
||||
$('.unfollow-btn').on('click', function() {
|
||||
var unfollowBtn = $(this);
|
||||
var followingUid = $(this).attr('followingUid');
|
||||
|
||||
socket.emit('api:user.unfollow', {
|
||||
uid: followingUid
|
||||
}, function(success) {
|
||||
var username = unfollowBtn.attr('data-username');
|
||||
if (success) {
|
||||
unfollowBtn.parent().remove();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
app.addCommasToNumbers();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
}());
|
||||
return Following;
|
||||
});
|
||||
@@ -1,25 +1,4 @@
|
||||
(function() {
|
||||
var stats_users = document.getElementById('stats_users'),
|
||||
stats_topics = document.getElementById('stats_topics'),
|
||||
stats_posts = document.getElementById('stats_posts'),
|
||||
stats_online = document.getElementById('stats_online'),
|
||||
user_label = document.getElementById('user_label');
|
||||
|
||||
socket.emit('user.count', {});
|
||||
socket.on('user.count', function(data) {
|
||||
stats_users.innerHTML = data.count;
|
||||
});
|
||||
|
||||
socket.emit('post.stats');
|
||||
socket.on('post.stats', function(data) {
|
||||
stats_topics.innerHTML = data.topics;
|
||||
stats_posts.innerHTML = data.posts;
|
||||
});
|
||||
|
||||
socket.emit('api:user.active.get');
|
||||
socket.on('api:user.active.get', function(data) {
|
||||
stats_online.innerHTML = data.users;
|
||||
});
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
@@ -55,23 +34,16 @@
|
||||
$('#search-button').show();
|
||||
|
||||
var userLabel = loggedInMenu.find('#user_label');
|
||||
|
||||
if (userLabel.length) {
|
||||
if (data['userslug'])
|
||||
userLabel.attr('href', '/user/' + data['userslug']);
|
||||
userLabel.find('#user-profile-link').attr('href', '/user/' + data['userslug']);
|
||||
if (data['picture'])
|
||||
userLabel.find('img').attr('src', data['picture']);
|
||||
if (data['username'])
|
||||
userLabel.find('span').html(data['username']);
|
||||
|
||||
$('#logout-link').on('click', function() {
|
||||
var csrf_token = $('#csrf_token').val();
|
||||
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: csrf_token
|
||||
}, function() {
|
||||
window.location = RELATIVE_PATH + '/';
|
||||
});
|
||||
});
|
||||
$('#logout-link').on('click', app.logout);
|
||||
}
|
||||
} else {
|
||||
$('#search-button').hide();
|
||||
@@ -84,7 +56,7 @@
|
||||
|
||||
}
|
||||
|
||||
$('#main-nav a,#right-menu a').off('click').on('click', function() {
|
||||
$('#main-nav a,#user-control-list a,#logged-out-menu .dropdown-menu a').off('click').on('click', function() {
|
||||
if($('.navbar .navbar-collapse').hasClass('in'))
|
||||
$('.navbar-header button').click();
|
||||
});
|
||||
@@ -105,6 +77,7 @@
|
||||
numUnread = data.unread.length,
|
||||
x;
|
||||
notifList.innerHTML = '';
|
||||
console.log(data);
|
||||
if ((data.read.length + data.unread.length) > 0) {
|
||||
for (x = 0; x < numUnread; x++) {
|
||||
notifEl.setAttribute('data-nid', data.unread[x].nid);
|
||||
@@ -119,9 +92,17 @@
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
notifEl.className = 'no-notifs';
|
||||
notifEl.innerHTML = '<a>You have no notifications</a>';
|
||||
notifFrag.appendChild(notifEl);
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
|
||||
// Add dedicated link to /notifications
|
||||
notifEl.removeAttribute('data-nid');
|
||||
notifEl.className = 'pagelink';
|
||||
notifEl.innerHTML = '<a href="' + RELATIVE_PATH + '/notifications">See all Notifications</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
|
||||
notifList.appendChild(notifFrag);
|
||||
|
||||
if (data.unread.length > 0) notifIcon.className = 'icon-circle active';
|
||||
|
||||
30
public/src/forum/home.js
Normal file
30
public/src/forum/home.js
Normal file
@@ -0,0 +1,30 @@
|
||||
define(function() {
|
||||
var home = {};
|
||||
|
||||
home.init = function() {
|
||||
|
||||
ajaxify.register_events([
|
||||
'user.count',
|
||||
'post.stats',
|
||||
'api:user.active.get'
|
||||
]);
|
||||
|
||||
socket.emit('user.count', {});
|
||||
socket.on('user.count', function(data) {
|
||||
$('#stats_users').html(utils.makeNumberHumanReadable(data.count)).attr('title', data.count);
|
||||
});
|
||||
|
||||
socket.emit('post.stats');
|
||||
socket.on('post.stats', function(data) {
|
||||
$('#stats_topics').html(utils.makeNumberHumanReadable(data.topics)).attr('title', data.topics);
|
||||
$('#stats_posts').html(utils.makeNumberHumanReadable(data.posts)).attr('title', data.posts);
|
||||
});
|
||||
|
||||
socket.emit('api:user.active.get');
|
||||
socket.on('api:user.active.get', function(data) {
|
||||
$('#stats_online').html(data.users);
|
||||
});
|
||||
}
|
||||
|
||||
return home;
|
||||
});
|
||||
@@ -1,60 +1,69 @@
|
||||
(function() {
|
||||
// Alternate Logins
|
||||
var altLoginEl = document.querySelector('.alt-logins');
|
||||
altLoginEl.addEventListener('click', function(e) {
|
||||
var target;
|
||||
switch (e.target.nodeName) {
|
||||
case 'LI':
|
||||
target = e.target;
|
||||
break;
|
||||
case 'I':
|
||||
target = e.target.parentNode;
|
||||
break;
|
||||
}
|
||||
if (target) {
|
||||
document.location.href = target.getAttribute('data-url');
|
||||
}
|
||||
});
|
||||
define(function() {
|
||||
var Login = {};
|
||||
|
||||
$('#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) {
|
||||
if (!data.success) {
|
||||
$('#login-error-notify').show();
|
||||
} else {
|
||||
$('#login-error-notify').hide();
|
||||
if(app.previousUrl.indexOf('/reset/') != -1)
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
else
|
||||
window.location.replace(app.previousUrl + "?loggedin");
|
||||
|
||||
app.loadConfig();
|
||||
}
|
||||
},
|
||||
error: function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').show();
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 2000
|
||||
Login.init = function() {
|
||||
// Alternate Logins
|
||||
var altLoginEl = document.querySelector('.alt-logins');
|
||||
altLoginEl.addEventListener('click', function(e) {
|
||||
var target;
|
||||
switch (e.target.nodeName) {
|
||||
case 'LI':
|
||||
target = e.target;
|
||||
break;
|
||||
case 'I':
|
||||
target = e.target.parentNode;
|
||||
break;
|
||||
}
|
||||
if (target) {
|
||||
document.location.href = target.getAttribute('data-url');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
$('#login').on('click', function() {
|
||||
var loginData = {
|
||||
'username': $('#username').val(),
|
||||
'password': $('#password').val(),
|
||||
'_csrf': $('#csrf-token').val()
|
||||
};
|
||||
|
||||
$('#login-error-notify button').on('click', function() {
|
||||
$('#login-error-notify').hide();
|
||||
return false;
|
||||
});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
if (!data.success) {
|
||||
$('#login-error-notify').show();
|
||||
} else {
|
||||
$('#login-error-notify').hide();
|
||||
|
||||
document.querySelector('#content input').focus();
|
||||
}());
|
||||
if(!app.previousUrl) { app.previousUrl = '/'; }
|
||||
|
||||
if(app.previousUrl.indexOf('/reset/') != -1)
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
else
|
||||
window.location.replace(app.previousUrl + "?loggedin");
|
||||
|
||||
app.loadConfig();
|
||||
}
|
||||
},
|
||||
error: function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').show();
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#login-error-notify button').on('click', function() {
|
||||
$('#login-error-notify').hide();
|
||||
return false;
|
||||
});
|
||||
|
||||
document.querySelector('#content input').focus();
|
||||
};
|
||||
|
||||
return Login;
|
||||
});
|
||||
|
||||
31
public/src/forum/notifications.js
Normal file
31
public/src/forum/notifications.js
Normal file
@@ -0,0 +1,31 @@
|
||||
define(function() {
|
||||
var Notifications = {};
|
||||
|
||||
Notifications.init = function() {
|
||||
var listEl = $('.notifications-list'),
|
||||
markAllReadEl = document.getElementById('mark-all-notifs-read');
|
||||
|
||||
$('span.timeago').timeago();
|
||||
|
||||
// Allow the user to click anywhere in the LI
|
||||
listEl.on('click', 'li', function(e) {
|
||||
this.querySelector('a').click();
|
||||
});
|
||||
|
||||
// Mark all as read button
|
||||
$(markAllReadEl).click(function() {
|
||||
socket.emit('api:notifications.mark_all_read', {}, function() {
|
||||
ajaxify.go('notifications');
|
||||
app.alert({
|
||||
alert_id: "notifications:mark_all_read",
|
||||
title: "All Notifications Read",
|
||||
message: "Successfully marked all notifications read",
|
||||
type: 'success',
|
||||
timeout: 2500
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Notifications;
|
||||
});
|
||||
@@ -1,55 +1,86 @@
|
||||
(function() {
|
||||
var loadingMoreTopics = false;
|
||||
define(function() {
|
||||
var Recent = {};
|
||||
|
||||
app.enter_room('recent_posts');
|
||||
Recent.newTopicCount = 0;
|
||||
Recent.newPostCount = 0;
|
||||
Recent.loadingMoreTopics = false;
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
var active = '';
|
||||
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
Recent.init = function() {
|
||||
app.enter_room('recent_posts');
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
function getActiveSection() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
return active;
|
||||
}
|
||||
|
||||
});
|
||||
active = getActiveSection();
|
||||
|
||||
function updateAlertText() {
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).addClass('hide');
|
||||
});
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++Recent.newTopicCount;
|
||||
Recent.updateAlertText();
|
||||
|
||||
});
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++Recent.newPostCount;
|
||||
Recent.updateAlertText();
|
||||
});
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !Recent.loadingMoreTopics) {
|
||||
Recent.loadMoreTopics();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Recent.updateAlertText = function() {
|
||||
var text = '';
|
||||
|
||||
if (newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if (newTopicCount === 1)
|
||||
if (Recent.newTopicCount > 1)
|
||||
text = 'There are ' + Recent.newTopicCount + ' new topics';
|
||||
else if (Recent.newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if (newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if (newPostCount === 1)
|
||||
if (Recent.newPostCount > 1)
|
||||
text += ' and ' + Recent.newPostCount + ' new posts.';
|
||||
else if (Recent.newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
|
||||
text += ' Click here to reload.';
|
||||
|
||||
$('#new-topics-alert').html(text).fadeIn('slow');
|
||||
$('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow');
|
||||
}
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++newPostCount;
|
||||
updateAlertText();
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
Recent.onTopicsLoaded = function(topics) {
|
||||
|
||||
var html = templates.prepare(templates['recent'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
@@ -59,27 +90,21 @@
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
$('span.timeago').timeago();
|
||||
}
|
||||
|
||||
function loadMoreTopics() {
|
||||
loadingMoreTopics = true;
|
||||
Recent.loadMoreTopics = function() {
|
||||
Recent.loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreRecentTopics', {
|
||||
after: $('#topics-container').children().length
|
||||
after: $('#topics-container').children('li').length,
|
||||
term: active
|
||||
}, function(data) {
|
||||
if (data.topics && data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
Recent.onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
Recent.loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
return Recent;
|
||||
});
|
||||
|
||||
@@ -1,154 +1,159 @@
|
||||
(function() {
|
||||
var username = $('#username'),
|
||||
password = $('#password'),
|
||||
password_confirm = $('#password-confirm'),
|
||||
register = $('#register'),
|
||||
emailEl = $('#email'),
|
||||
username_notify = $('#username-notify'),
|
||||
email_notify = $('#email-notify'),
|
||||
password_notify = $('#password-notify'),
|
||||
password_confirm_notify = $('#password-confirm-notify'),
|
||||
validationError = false,
|
||||
successIcon = '<i class="icon icon-ok"></i>';
|
||||
define(function() {
|
||||
var Register = {};
|
||||
|
||||
$('#referrer').val(app.previousUrl);
|
||||
Register.init = function() {
|
||||
var username = $('#username'),
|
||||
password = $('#password'),
|
||||
password_confirm = $('#password-confirm'),
|
||||
register = $('#register'),
|
||||
emailEl = $('#email'),
|
||||
username_notify = $('#username-notify'),
|
||||
email_notify = $('#email-notify'),
|
||||
password_notify = $('#password-notify'),
|
||||
password_confirm_notify = $('#password-confirm-notify'),
|
||||
validationError = false,
|
||||
successIcon = '<i class="icon icon-ok"></i>';
|
||||
|
||||
function showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
}
|
||||
$('#referrer').val(app.previousUrl);
|
||||
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
function validateEmail() {
|
||||
if (!emailEl.val()) {
|
||||
function showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!utils.isEmailValid(emailEl.val())) {
|
||||
showError(email_notify, 'Invalid email address.');
|
||||
} else
|
||||
socket.emit('user.email.exists', {
|
||||
email: emailEl.val()
|
||||
});
|
||||
}
|
||||
|
||||
emailEl.on('blur', function() {
|
||||
validateEmail();
|
||||
});
|
||||
|
||||
function validateUsername() {
|
||||
if (!username.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
if (username.val().length < config.minimumUsernameLength) {
|
||||
showError(username_notify, 'Username too short!');
|
||||
} else if (username.val().length > config.maximumUsernameLength) {
|
||||
showError(username_notify, 'Username too long!');
|
||||
} else if (!utils.isUserNameValid(username.val())) {
|
||||
showError(username_notify, 'Invalid username!');
|
||||
} else {
|
||||
socket.emit('user.exists', {
|
||||
username: username.val()
|
||||
});
|
||||
}
|
||||
}
|
||||
function validateEmail() {
|
||||
if (!emailEl.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
username.on('keyup', function() {
|
||||
jQuery('#yourUsername').html(this.value.length > 0 ? this.value : 'username');
|
||||
});
|
||||
username.on('blur', function() {
|
||||
validateUsername();
|
||||
});
|
||||
|
||||
function validatePassword() {
|
||||
if (!password.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
if (!utils.isEmailValid(emailEl.val())) {
|
||||
showError(email_notify, 'Invalid email address.');
|
||||
} else
|
||||
socket.emit('user.email.exists', {
|
||||
email: emailEl.val()
|
||||
});
|
||||
}
|
||||
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
showError(password_notify, 'Password too short!');
|
||||
} else if (!utils.isPasswordValid(password.val())) {
|
||||
showError(password_notify, 'Invalid password!');
|
||||
} else {
|
||||
showSuccess(password_notify, successIcon);
|
||||
emailEl.on('blur', function() {
|
||||
validateEmail();
|
||||
});
|
||||
|
||||
function validateUsername() {
|
||||
if (!username.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.val().length < config.minimumUsernameLength) {
|
||||
showError(username_notify, 'Username too short!');
|
||||
} else if (username.val().length > config.maximumUsernameLength) {
|
||||
showError(username_notify, 'Username too long!');
|
||||
} else if (!utils.isUserNameValid(username.val()) || !utils.slugify(username.val())) {
|
||||
showError(username_notify, 'Invalid username!');
|
||||
} else {
|
||||
socket.emit('user.exists', {
|
||||
username: username.val()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val() && password_confirm.val() !== '') {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
}
|
||||
}
|
||||
username.on('keyup', function() {
|
||||
jQuery('#yourUsername').html(this.value.length > 0 ? this.value : 'username');
|
||||
});
|
||||
username.on('blur', function() {
|
||||
validateUsername();
|
||||
});
|
||||
|
||||
$(password).on('blur', function() {
|
||||
validatePassword();
|
||||
});
|
||||
function validatePassword() {
|
||||
if (!password.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
function validatePasswordConfirm() {
|
||||
if (!password.val() || password_notify.hasClass('alert-error')) {
|
||||
return;
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
showError(password_notify, 'Password too short!');
|
||||
} else if (!utils.isPasswordValid(password.val())) {
|
||||
showError(password_notify, 'Invalid password!');
|
||||
} else {
|
||||
showSuccess(password_notify, successIcon);
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val() && password_confirm.val() !== '') {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
}
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
} else {
|
||||
showSuccess(password_confirm_notify, successIcon);
|
||||
$(password).on('blur', function() {
|
||||
validatePassword();
|
||||
});
|
||||
|
||||
function validatePasswordConfirm() {
|
||||
if (!password.val() || password_notify.hasClass('alert-error')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
} else {
|
||||
showSuccess(password_confirm_notify, successIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(password_confirm).on('blur', function() {
|
||||
validatePasswordConfirm();
|
||||
});
|
||||
$(password_confirm).on('blur', function() {
|
||||
validatePasswordConfirm();
|
||||
});
|
||||
|
||||
ajaxify.register_events(['user.exists', 'user.email.exists']);
|
||||
ajaxify.register_events(['user.exists', 'user.email.exists']);
|
||||
|
||||
socket.on('user.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(username_notify, 'Username already taken!');
|
||||
} else {
|
||||
showSuccess(username_notify, successIcon);
|
||||
socket.on('user.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(username_notify, 'Username already taken!');
|
||||
} else {
|
||||
showSuccess(username_notify, successIcon);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(email_notify, 'Email address already taken!');
|
||||
} else {
|
||||
showSuccess(email_notify, successIcon);
|
||||
}
|
||||
});
|
||||
|
||||
// Alternate Logins
|
||||
$('.alt-logins li').on('click', function(e) {
|
||||
document.location.href = $(this).attr('data-url');
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
validationError = false;
|
||||
|
||||
validateEmail();
|
||||
validateUsername();
|
||||
validatePassword();
|
||||
validatePasswordConfirm();
|
||||
|
||||
return validationError;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(email_notify, 'Email address already taken!');
|
||||
} else {
|
||||
showSuccess(email_notify, successIcon);
|
||||
}
|
||||
});
|
||||
register.on('click', function(e) {
|
||||
if (validateForm()) e.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
// Alternate Logins
|
||||
$('.alt-logins li').on('click', function(e) {
|
||||
document.location.href = $(this).attr('data-url');
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
validationError = false;
|
||||
|
||||
validateEmail();
|
||||
validateUsername();
|
||||
validatePassword();
|
||||
validatePasswordConfirm();
|
||||
|
||||
return validationError;
|
||||
}
|
||||
|
||||
register.on('click', function(e) {
|
||||
if (validateForm()) e.preventDefault();
|
||||
});
|
||||
|
||||
}());
|
||||
return Register;
|
||||
});
|
||||
@@ -1,41 +1,47 @@
|
||||
(function() {
|
||||
var inputEl = document.getElementById('email'),
|
||||
errorEl = document.getElementById('error'),
|
||||
errorTextEl = errorEl.querySelector('p');
|
||||
define(function() {
|
||||
var ResetPassword = {};
|
||||
|
||||
document.getElementById('reset').onclick = function() {
|
||||
if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) {
|
||||
socket.emit('user:reset.send', {
|
||||
email: inputEl.value
|
||||
});
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
errorTextEl.innerHTML = 'Please enter a valid email';
|
||||
}
|
||||
ResetPassword.init = function() {
|
||||
var inputEl = document.getElementById('email'),
|
||||
errorEl = document.getElementById('error'),
|
||||
errorTextEl = errorEl.querySelector('p');
|
||||
|
||||
document.getElementById('reset').onclick = function() {
|
||||
if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) {
|
||||
socket.emit('user:reset.send', {
|
||||
email: inputEl.value
|
||||
});
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
errorTextEl.innerHTML = 'Please enter a valid email';
|
||||
}
|
||||
};
|
||||
|
||||
ajaxify.register_events(['user.send_reset']);
|
||||
|
||||
socket.on('user.send_reset', function(data) {
|
||||
var submitEl = document.getElementById('reset');
|
||||
|
||||
if (data.status === 'ok') {
|
||||
jQuery('#error').hide();
|
||||
jQuery('#success').show();
|
||||
jQuery('#success p').html('An email has been dispatched to "' + data.email + '" with instructions on setting a new password.');
|
||||
inputEl.value = '';
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
switch (data.message) {
|
||||
case 'invalid-email':
|
||||
errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.';
|
||||
break;
|
||||
case 'send-failed':
|
||||
errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ajaxify.register_events(['user.send_reset']);
|
||||
|
||||
socket.on('user.send_reset', function(data) {
|
||||
var submitEl = document.getElementById('reset');
|
||||
|
||||
if (data.status === 'ok') {
|
||||
jQuery('#error').hide();
|
||||
jQuery('#success').show();
|
||||
jQuery('#success p').html('An email has been dispatched to "' + data.email + '" with instructions on setting a new password.');
|
||||
inputEl.value = '';
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
switch (data.message) {
|
||||
case 'invalid-email':
|
||||
errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.';
|
||||
break;
|
||||
case 'send-failed':
|
||||
errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}());
|
||||
return ResetPassword;
|
||||
});
|
||||
@@ -1,52 +1,58 @@
|
||||
(function() {
|
||||
var reset_code = templates.get('reset_code');
|
||||
define(function() {
|
||||
var ResetCode = {};
|
||||
|
||||
var resetEl = document.getElementById('reset'),
|
||||
password = document.getElementById('password'),
|
||||
repeat = document.getElementById('repeat'),
|
||||
noticeEl = document.getElementById('notice');
|
||||
ResetCode.init = function() {
|
||||
var reset_code = templates.get('reset_code');
|
||||
|
||||
resetEl.addEventListener('click', function() {
|
||||
if (password.value.length < 6) {
|
||||
$('#error').hide();
|
||||
noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
|
||||
noticeEl.querySelector('p').innerHTML = 'The password entered is too short, please pick a different password.';
|
||||
noticeEl.style.display = 'block';
|
||||
} else if (password.value !== repeat.value) {
|
||||
$('#error').hide();
|
||||
noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
|
||||
noticeEl.querySelector('p').innerHTML = 'The two passwords you\'ve entered do not match.';
|
||||
noticeEl.style.display = 'block';
|
||||
} else {
|
||||
socket.emit('user:reset.commit', {
|
||||
code: reset_code,
|
||||
password: password.value
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
var resetEl = document.getElementById('reset'),
|
||||
password = document.getElementById('password'),
|
||||
repeat = document.getElementById('repeat'),
|
||||
noticeEl = document.getElementById('notice');
|
||||
|
||||
// Enable the form if the code is valid
|
||||
socket.emit('user:reset.valid', {
|
||||
code: reset_code
|
||||
});
|
||||
resetEl.addEventListener('click', function() {
|
||||
if (password.value.length < 6) {
|
||||
$('#error').hide();
|
||||
noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
|
||||
noticeEl.querySelector('p').innerHTML = 'The password entered is too short, please pick a different password.';
|
||||
noticeEl.style.display = 'block';
|
||||
} else if (password.value !== repeat.value) {
|
||||
$('#error').hide();
|
||||
noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
|
||||
noticeEl.querySelector('p').innerHTML = 'The two passwords you\'ve entered do not match.';
|
||||
noticeEl.style.display = 'block';
|
||||
} else {
|
||||
socket.emit('user:reset.commit', {
|
||||
code: reset_code,
|
||||
password: password.value
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Enable the form if the code is valid
|
||||
socket.emit('user:reset.valid', {
|
||||
code: reset_code
|
||||
});
|
||||
|
||||
|
||||
ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
|
||||
socket.on('user:reset.valid', function(data) {
|
||||
if ( !! data.valid) resetEl.disabled = false;
|
||||
else {
|
||||
var formEl = document.getElementById('reset-form');
|
||||
// Show error message
|
||||
$('#error').show();
|
||||
formEl.parentNode.removeChild(formEl);
|
||||
}
|
||||
})
|
||||
ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
|
||||
socket.on('user:reset.valid', function(data) {
|
||||
if ( !! data.valid) resetEl.disabled = false;
|
||||
else {
|
||||
var formEl = document.getElementById('reset-form');
|
||||
// Show error message
|
||||
$('#error').show();
|
||||
formEl.parentNode.removeChild(formEl);
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('user:reset.commit', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
$('#error').hide();
|
||||
$('#notice').hide();
|
||||
$('#success').show();
|
||||
}
|
||||
});
|
||||
}());
|
||||
socket.on('user:reset.commit', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
$('#error').hide();
|
||||
$('#notice').hide();
|
||||
$('#success').show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return ResetCode;
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Search = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init = function() {
|
||||
var searchQuery = $('#topics-container').attr('data-search-query');
|
||||
|
||||
$('.search-result-text').each(function() {
|
||||
@@ -19,6 +20,7 @@
|
||||
input.val('');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
return Search;
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,114 +1,121 @@
|
||||
(function() {
|
||||
var loadingMoreTopics = false;
|
||||
define(function() {
|
||||
var Unread = {};
|
||||
|
||||
app.enter_room('recent_posts');
|
||||
Unread.init = function() {
|
||||
var loadingMoreTopics = false;
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
app.enter_room('recent_posts');
|
||||
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
});
|
||||
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if (newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if (newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if (newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if (newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
|
||||
text += ' Click here to reload.';
|
||||
|
||||
$('#new-topics-alert').html(text).fadeIn('slow');
|
||||
}
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++newPostCount;
|
||||
updateAlertText();
|
||||
});
|
||||
|
||||
$('#mark-allread-btn').on('click', function() {
|
||||
var btn = $(this);
|
||||
socket.emit('api:topics.markAllRead', {}, function(success) {
|
||||
if (success) {
|
||||
btn.remove();
|
||||
$('#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!');
|
||||
}
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).addClass('hide');
|
||||
});
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
var html = templates.prepare(templates['unread'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
container = $('#topics-container');
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
}
|
||||
|
||||
function loadMoreTopics() {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreUnreadTopics', {
|
||||
after: parseInt($('#topics-container').attr('data-next-start'), 10)
|
||||
}, function(data) {
|
||||
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).height() - $(window).height()) * 0.9;
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
if (newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if (newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if (newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if (newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
|
||||
text += ' Click here to reload.';
|
||||
|
||||
$('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow');
|
||||
$('#category-no-topics').addClass('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++newPostCount;
|
||||
updateAlertText();
|
||||
});
|
||||
|
||||
$('#mark-allread-btn').on('click', function() {
|
||||
var btn = $(this);
|
||||
socket.emit('api:topics.markAllRead', {}, function(success) {
|
||||
if (success) {
|
||||
btn.remove();
|
||||
$('#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!');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
|
||||
var html = templates.prepare(templates['unread'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
container = $('#topics-container');
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
$('span.timeago').timeago();
|
||||
}
|
||||
|
||||
function loadMoreTopics() {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreUnreadTopics', {
|
||||
after: parseInt($('#topics-container').attr('data-next-start'), 10)
|
||||
}, function(data) {
|
||||
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).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
|
||||
$('#load-more-btn').show();
|
||||
if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
|
||||
$('#load-more-btn').show();
|
||||
|
||||
$('#load-more-btn').on('click', function() {
|
||||
loadMoreTopics();
|
||||
});
|
||||
$('#load-more-btn').on('click', function() {
|
||||
loadMoreTopics();
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
return Unread;
|
||||
});
|
||||
@@ -1,12 +1,18 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Users = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
Users.init = function() {
|
||||
var timeoutId = 0;
|
||||
var loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
function getActiveSection() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
return active;
|
||||
}
|
||||
|
||||
var active = getActiveSection();
|
||||
|
||||
var lastSearch = null;
|
||||
|
||||
@@ -59,9 +65,9 @@
|
||||
var html = templates.prepare(templates['users'].blocks['users']).parse({
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('#users-container');
|
||||
userListEl = $('#users-container');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
userListEl.html(html);
|
||||
|
||||
|
||||
if (data && data.length === 0) {
|
||||
@@ -75,17 +81,27 @@
|
||||
});
|
||||
|
||||
socket.on('api:user.isOnline', function(data) {
|
||||
if(active == 'online' && !loadingMoreUsers) {
|
||||
$('#users-container').empty();
|
||||
startLoading('users:online', 0);
|
||||
if(getActiveSection() == 'online' && !loadingMoreUsers) {
|
||||
startLoading('users:online', 0, true);
|
||||
socket.emit('api:user.getOnlineAnonCount', {} , function(anonCount) {
|
||||
if(parseInt(anonCount, 10) > 0) {
|
||||
$('#users-container .anon-user').removeClass('hide');
|
||||
$('#online_anon_count').html(anonCount);
|
||||
} else {
|
||||
$('#users-container .anon-user').addClass('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
function onUsersLoaded(users, emptyContainer) {
|
||||
var html = templates.prepare(templates['users'].blocks['users']).parse({
|
||||
users: users
|
||||
});
|
||||
if(emptyContainer)
|
||||
$('#users-container .registered-user').remove();
|
||||
$('#users-container').append(html);
|
||||
$('#users-container .anon-user').appendTo($('#users-container'));
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
@@ -101,18 +117,18 @@
|
||||
}
|
||||
|
||||
if (set) {
|
||||
startLoading(set, $('#users-container').children().length);
|
||||
startLoading(set, $('#users-container').children('.registered-user').length);
|
||||
}
|
||||
}
|
||||
|
||||
function startLoading(set, after) {
|
||||
function startLoading(set, after, emptyContainer) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: after
|
||||
}, function(data) {
|
||||
if (data.users.length) {
|
||||
onUsersLoaded(data.users);
|
||||
onUsersLoaded(data.users, emptyContainer);
|
||||
$('#load-more-users-btn').removeClass('disabled');
|
||||
} else {
|
||||
$('#load-more-users-btn').addClass('disabled');
|
||||
@@ -131,6 +147,7 @@
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
||||
return Users;
|
||||
});
|
||||
@@ -20,7 +20,7 @@ define(['taskbar'], function(taskbar) {
|
||||
module.modalExists = function(touid) {
|
||||
return $('#chat-modal-' + touid).length !== 0;
|
||||
}
|
||||
|
||||
|
||||
function checkStatus(chatModal, callback) {
|
||||
socket.emit('api:user.isOnline', chatModal.touid, function(data) {
|
||||
if(data.online !== chatModal.online) {
|
||||
@@ -35,7 +35,7 @@ define(['taskbar'], function(taskbar) {
|
||||
callback(data.online);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function checkOnlineStatus(chatModal) {
|
||||
if(chatModal.intervalId === 0) {
|
||||
chatModal.intervalId = setInterval(function() {
|
||||
@@ -43,12 +43,12 @@ define(['taskbar'], function(taskbar) {
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.createModal = function(username, touid, callback) {
|
||||
|
||||
|
||||
var chatModal = $('#chat-modal').clone(),
|
||||
uuid = utils.generateUUID();
|
||||
|
||||
|
||||
chatModal.intervalId = 0;
|
||||
chatModal.touid = touid;
|
||||
chatModal.username = username;
|
||||
@@ -61,7 +61,7 @@ define(['taskbar'], function(taskbar) {
|
||||
module.bringModalToTop(chatModal);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
chatModal.find('#chat-with-name').html(username);
|
||||
|
||||
chatModal.find('.close').on('click', function(e) {
|
||||
@@ -74,7 +74,7 @@ define(['taskbar'], function(taskbar) {
|
||||
chatModal.on('click', function(e) {
|
||||
module.bringModalToTop(chatModal);
|
||||
});
|
||||
|
||||
|
||||
addSendHandler(chatModal);
|
||||
|
||||
getChatMessages(chatModal, function() {
|
||||
@@ -85,6 +85,13 @@ define(['taskbar'], function(taskbar) {
|
||||
return chatModal;
|
||||
}
|
||||
|
||||
module.center = function(chatModal) {
|
||||
chatModal.css("position", "fixed");
|
||||
chatModal.css("top", "100px");
|
||||
chatModal.css("left", Math.max(0, (($(window).width() - $(chatModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px");
|
||||
return chatModal;
|
||||
}
|
||||
|
||||
module.load = function(uuid) {
|
||||
var chatModal = $('div[UUID="'+uuid+'"]');
|
||||
chatModal.show();
|
||||
|
||||
@@ -150,7 +150,8 @@ define(['taskbar'], function(taskbar) {
|
||||
cid: threadData.cid,
|
||||
pid: threadData.pid,
|
||||
title: threadData.title || '',
|
||||
body: threadData.body || ''
|
||||
body: threadData.body || '',
|
||||
modified: false
|
||||
};
|
||||
composer.load(uuid);
|
||||
} else {
|
||||
@@ -179,6 +180,9 @@ define(['taskbar'], function(taskbar) {
|
||||
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;
|
||||
|
||||
// Mark this post window as having been changed
|
||||
composer.posts[uuid].modified = true;
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.action-bar button', function() {
|
||||
@@ -188,7 +192,7 @@ define(['taskbar'], function(taskbar) {
|
||||
case 'post': composer.post(uuid); break;
|
||||
case 'minimize': composer.minimize(uuid); break;
|
||||
case 'discard':
|
||||
if (postContentEl.value.length > 0) {
|
||||
if (composer.posts[uuid].modified) {
|
||||
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
|
||||
if (discard) composer.discard(uuid);
|
||||
});
|
||||
@@ -384,6 +388,7 @@ define(['taskbar'], function(taskbar) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$(composer.postContainer).find('.imagedrop').hide();
|
||||
delete composer.posts[post_uuid];
|
||||
uploadsInProgress.length = 0;
|
||||
composer.minimize();
|
||||
taskbar.discard('composer', post_uuid);
|
||||
}
|
||||
|
||||
83
public/src/modules/uploader.js
Normal file
83
public/src/modules/uploader.js
Normal file
@@ -0,0 +1,83 @@
|
||||
define(function() {
|
||||
|
||||
var module = {};
|
||||
|
||||
module.open = function(route, callback) {
|
||||
$('#upload-picture-modal').modal('show').removeClass('hide');
|
||||
module.hideAlerts();
|
||||
|
||||
$('#uploadForm')[0].reset();
|
||||
$('#uploadForm').attr('action', route);
|
||||
|
||||
$('#pictureUploadSubmitBtn').off('click').on('click', function() {
|
||||
$('#uploadForm').submit();
|
||||
});
|
||||
|
||||
$('#uploadForm').off('submit').submit(function() {
|
||||
|
||||
function status(message) {
|
||||
module.hideAlerts();
|
||||
$('#alert-status').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function success(message) {
|
||||
module.hideAlerts();
|
||||
$('#alert-success').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
module.hideAlerts();
|
||||
$('#alert-error').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
status('uploading the file ...');
|
||||
|
||||
$('#upload-progress-bar').css('width', '0%');
|
||||
$('#upload-progress-box').show().removeClass('hide');
|
||||
|
||||
if (!$('#userPhotoInput').val()) {
|
||||
error('select an image to upload!');
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this).find('#imageUploadCsrf').val($('#csrf_token').val());
|
||||
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
|
||||
error: function(xhr) {
|
||||
error('Error: ' + xhr.status);
|
||||
},
|
||||
|
||||
uploadProgress: function(event, position, total, percent) {
|
||||
$('#upload-progress-bar').css('width', percent + '%');
|
||||
},
|
||||
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
error(response.error);
|
||||
return;
|
||||
}
|
||||
callback(response.path);
|
||||
|
||||
success('File uploaded successfully!');
|
||||
setTimeout(function() {
|
||||
module.hideAlerts();
|
||||
$('#upload-picture-modal').modal('hide');
|
||||
}, 750);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
module.hideAlerts = function() {
|
||||
$('#alert-status').addClass('hide');
|
||||
$('#alert-success').addClass('hide');
|
||||
$('#alert-error').addClass('hide');
|
||||
$('#upload-progress-box').addClass('hide');
|
||||
}
|
||||
|
||||
return module;
|
||||
});
|
||||
@@ -57,30 +57,41 @@
|
||||
return template;
|
||||
};
|
||||
|
||||
function loadTemplates(templatesToLoad) {
|
||||
function loadTemplates(templatesToLoad, customTemplateDir) {
|
||||
function loadServer() {
|
||||
var loaded = templatesToLoad.length;
|
||||
|
||||
for (var t in templatesToLoad) {
|
||||
(function (file) {
|
||||
fs.readFile(__dirname + '/../templates/' + file + '.tpl', function (err, html) {
|
||||
var template = function () {
|
||||
this.toString = function () {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
function getTemplates(directory) {
|
||||
for (var t in templatesToLoad) {
|
||||
(function (file) {
|
||||
fs.readFile(directory + '/' + file + '.tpl', function (err, html) {
|
||||
var template = function () {
|
||||
this.toString = function () {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
|
||||
template.prototype.file = file;
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
template.prototype.file = file;
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
|
||||
global.templates[file] = new template;
|
||||
global.templates[file] = new template;
|
||||
|
||||
loaded--;
|
||||
if (loaded == 0) templates.ready();
|
||||
});
|
||||
}(templatesToLoad[t]));
|
||||
loaded--;
|
||||
if (loaded == 0) templates.ready();
|
||||
});
|
||||
}(templatesToLoad[t]));
|
||||
}
|
||||
}
|
||||
if (customTemplateDir) {
|
||||
fs.exists(customTemplateDir, function (exists) {
|
||||
var directory = (exists ? customTemplateDir : __dirname + '/../templates');
|
||||
getTemplates(directory);
|
||||
});
|
||||
} else {
|
||||
getTemplates(__dirname + '/../templates');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function loadClient() {
|
||||
@@ -96,8 +107,8 @@
|
||||
}
|
||||
|
||||
|
||||
templates.init = function (templates_to_load) {
|
||||
loadTemplates(templates_to_load || []);
|
||||
templates.init = function (templates_to_load, custom_templates) {
|
||||
loadTemplates(templates_to_load || [], custom_templates || false);
|
||||
}
|
||||
|
||||
templates.getTemplateNameFromUrl = function (url) {
|
||||
@@ -156,6 +167,10 @@
|
||||
template_data = data;
|
||||
parse_template();
|
||||
}).fail(function (data) {
|
||||
if(data && data.status == 404) {
|
||||
ajaxify.go('404');
|
||||
return;
|
||||
}
|
||||
app.alertError("Can't load template data!");
|
||||
});
|
||||
|
||||
@@ -168,25 +183,26 @@
|
||||
template_data['relative_path'] = RELATIVE_PATH;
|
||||
|
||||
translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
|
||||
document.getElementById('content').innerHTML = translatedTemplate;
|
||||
|
||||
$('#content').html(translatedTemplate);
|
||||
|
||||
jQuery('#content [template-variable]').each(function (index, element) {
|
||||
var value = null;
|
||||
|
||||
switch (element.getAttribute('template-type')) {
|
||||
case 'boolean':
|
||||
value = (element.value === 'true' || element.value === '1') ? true : false;
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
case 'integer':
|
||||
value = parseInt(element.value);
|
||||
break;
|
||||
default:
|
||||
value = element.value;
|
||||
break;
|
||||
switch ($(element).attr('template-type')) {
|
||||
case 'boolean':
|
||||
value = ($(element).val() === 'true' || $(element).val() === '1') ? true : false;
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
case 'integer':
|
||||
value = parseInt($(element).val());
|
||||
break;
|
||||
default:
|
||||
value = $(element).val();
|
||||
break;
|
||||
}
|
||||
|
||||
templates.set(element.getAttribute('template-variable'), value);
|
||||
templates.set($(element).attr('template-variable'), value);
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
@@ -222,6 +238,10 @@
|
||||
return new RegExp("<!-- BEGIN " + block + " -->[\\s\\S]*<!-- END " + block + " -->", 'g');
|
||||
}
|
||||
|
||||
function makeConditionalRegex(block) {
|
||||
return new RegExp("<!-- IF " + block + " -->[\\s\\S]*<!-- ENDIF " + block + " -->", 'g');
|
||||
}
|
||||
|
||||
function getBlock(regex, block, template) {
|
||||
data = template.match(regex);
|
||||
if (data == null) return;
|
||||
@@ -235,6 +255,19 @@
|
||||
return data;
|
||||
}
|
||||
|
||||
function getConditionalBlock(regex, block, template) {
|
||||
data = template.match(regex);
|
||||
if (data == null) return;
|
||||
|
||||
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
||||
|
||||
data = data[0]
|
||||
.replace("<!-- IF " + block + " -->", "")
|
||||
.replace("<!-- ENDIF " + block + " -->", "");
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function setBlock(regex, block, template) {
|
||||
return template.replace(regex, block);
|
||||
}
|
||||
@@ -249,7 +282,9 @@
|
||||
|
||||
for (var d in data) {
|
||||
if (data.hasOwnProperty(d)) {
|
||||
if (data[d] === null) {
|
||||
if (typeof data[d] === 'undefined') {
|
||||
continue;
|
||||
} else if (data[d] === null) {
|
||||
template = replace(namespace + d, '', template);
|
||||
} else if (data[d].constructor == Array) {
|
||||
namespace += d + '.';
|
||||
@@ -282,6 +317,14 @@
|
||||
block = parse(data[d], namespace, block);
|
||||
template = setBlock(regex, block, template);
|
||||
} else {
|
||||
var conditional = makeConditionalRegex(d),
|
||||
block = getConditionalBlock(conditional, namespace, template);
|
||||
|
||||
if (block && !data[d]) {
|
||||
template = template.replace(conditional, '');
|
||||
}
|
||||
|
||||
|
||||
template = replace(namespace + d, data[d], template);
|
||||
}
|
||||
}
|
||||
@@ -306,4 +349,4 @@
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
} : module)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
languageFile = parsedKey[0];
|
||||
|
||||
parsedKey = parsedKey[1];
|
||||
|
||||
translator.load(languageFile, function (languageData) {
|
||||
if (callback) {
|
||||
callback(languageData[parsedKey]);
|
||||
@@ -39,6 +40,20 @@
|
||||
});
|
||||
};
|
||||
|
||||
translator.mget = function (keys, callback) {
|
||||
|
||||
var async = require('async');
|
||||
|
||||
function getKey(key, callback) {
|
||||
translator.get(key, function(value) {
|
||||
callback(null, value);
|
||||
});
|
||||
}
|
||||
|
||||
async.map(keys, getKey, callback);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TODO: Not fully converted to server side yet, ideally server should be able to parse whole templates on demand if necessary
|
||||
* fix: translator.load should determine if server side and immediately return appropriate language file.
|
||||
@@ -46,7 +61,7 @@
|
||||
translator.translate = function (data, callback) {
|
||||
var keys = data.match(/\[\[.*?\]\]/g),
|
||||
loading = 0;
|
||||
|
||||
|
||||
function insertLanguage(text, key, value, variables) {
|
||||
if (value) {
|
||||
for (var i = 1, ii = variables.length; i < ii; i++) {
|
||||
@@ -56,7 +71,7 @@
|
||||
|
||||
text = text.replace(key, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return text;
|
||||
}
|
||||
@@ -65,23 +80,23 @@
|
||||
if (keys.hasOwnProperty(key)) {
|
||||
var variables = keys[key].split(/[,][?\s+]/);
|
||||
|
||||
var parsedKey = keys[key].replace('[[', '').replace(']]', '').split(':'),
|
||||
languageFile = parsedKey[0];
|
||||
|
||||
var parsedKey = keys[key].replace('[[', '').replace(']]', '').split(':');
|
||||
if (!(parsedKey[0] && parsedKey[1])) continue;
|
||||
|
||||
var languageFile = parsedKey[0];
|
||||
parsedKey = parsedKey[1].split(',')[0];
|
||||
|
||||
if (files.loaded[languageFile]) {
|
||||
data = insertLanguage(data, keys[key], files.loaded[languageFile][parsedKey], variables);
|
||||
} else {
|
||||
loading++;
|
||||
|
||||
(function (languageKey, parsedKey) {
|
||||
(function (languageKey, parsedKey, languageFile, variables) {
|
||||
translator.load(languageFile, function (languageData) {
|
||||
data = insertLanguage(data, languageKey, languageData[parsedKey], variables);
|
||||
loading--;
|
||||
checkComplete();
|
||||
});
|
||||
}(keys[key], parsedKey));
|
||||
}(keys[key], parsedKey, languageFile, variables));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var utils, fs;
|
||||
|
||||
try {
|
||||
if ('undefined' === typeof window) {
|
||||
fs = require('fs');
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
module.exports = utils = {
|
||||
generateUUID: function() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16 | 0,
|
||||
v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
@@ -23,20 +24,28 @@
|
||||
main_dir = path.join(__dirname, '..', 'templates');
|
||||
|
||||
fs.readdir(dir, function(err, list) {
|
||||
if (err) return done(err);
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var pending = list.length;
|
||||
if (!pending) return done(null, results);
|
||||
if (!pending) {
|
||||
return done(null, results);
|
||||
}
|
||||
list.forEach(function(file) {
|
||||
file = dir + '/' + file;
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
utils.walk(file, function(err, res) {
|
||||
results = results.concat(res);
|
||||
if (!--pending) done(null, results);
|
||||
if (!--pending) {
|
||||
done(null, results);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push(file.replace(main_dir + '/', '').replace('.tpl', ''));
|
||||
if (!--pending) done(null, results);
|
||||
if (!--pending) {
|
||||
done(null, results);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -49,19 +58,29 @@
|
||||
|
||||
difference = Math.floor(difference / 1000);
|
||||
|
||||
if (difference < 60) return difference + (min ? 's' : ' second') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 60) {
|
||||
return difference + (min ? 's' : ' second') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 60) return difference + (min ? 'm' : ' minute') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 60) {
|
||||
return difference + (min ? 'm' : ' minute') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 24) return difference + (min ? 'h' : ' hour') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 24) {
|
||||
return difference + (min ? 'h' : ' hour') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 24);
|
||||
if (difference < 30) return difference + (min ? 'd' : ' day') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 30) {
|
||||
return difference + (min ? 'd' : ' day') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 30);
|
||||
if (difference < 12) return difference + (min ? 'mon' : ' month') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 12) {
|
||||
return difference + (min ? 'mon' : ' month') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 12);
|
||||
return difference + (min ? 'y' : ' year') + (difference !== 1 && !min ? 's' : '');
|
||||
@@ -94,7 +113,7 @@
|
||||
},
|
||||
|
||||
isUserNameValid: function(name) {
|
||||
return (name && name !== "" && (/^[a-zA-Z0-9 _-]+$/.test(name)));
|
||||
return (name && name !== "" && (/^['"\s\-.*0-9\u00BF-\u1FFF\u2C00-\uD7FF\w]+$/.test(name)));
|
||||
},
|
||||
|
||||
isPasswordValid: function(password) {
|
||||
@@ -116,8 +135,29 @@
|
||||
var tags = '',
|
||||
tag;
|
||||
for (var x = 0, numTags = tagsArr.length; x < numTags; x++) {
|
||||
if (tags.length > 0) tags += "\n\t";
|
||||
if (tags.length > 0) {
|
||||
tags += "\n\t";
|
||||
}
|
||||
tag = '<meta';
|
||||
var y;
|
||||
for (y in tagsArr[x]) {
|
||||
tag += ' ' + y + '="' + tagsArr[x][y] + '"';
|
||||
}
|
||||
tag += ' />';
|
||||
|
||||
tags += tag;
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
|
||||
buildLinkTags: function(tagsArr) {
|
||||
var tags = '',
|
||||
tag;
|
||||
for (var x = 0, numTags = tagsArr.length; x < numTags; x++) {
|
||||
if (tags.length > 0) tags += "\n\t";
|
||||
tag = '<link';
|
||||
var y;
|
||||
for (y in tagsArr[x]) {
|
||||
tag += ' ' + y + '="' + tagsArr[x][y] + '"';
|
||||
}
|
||||
@@ -140,7 +180,9 @@
|
||||
socket.emit('api:meta.buildTitle', url, function(title, numNotifications) {
|
||||
document.title = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title;
|
||||
notificationIcon = notificationIcon || document.querySelector('.notifications a i');
|
||||
if (numNotifications > 0 && notificationIcon) notificationIcon.className = 'icon-circle active';
|
||||
if (numNotifications > 0 && notificationIcon) {
|
||||
notificationIcon.className = 'icon-circle active';
|
||||
}
|
||||
});
|
||||
|
||||
jQuery.getJSON(RELATIVE_PATH + '/api/unread/total', function(data) {
|
||||
@@ -150,11 +192,11 @@
|
||||
if (data.count > 0) {
|
||||
badge
|
||||
.removeClass('badge-inverse')
|
||||
.addClass('badge-important')
|
||||
.addClass('badge-important');
|
||||
} else {
|
||||
badge
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse')
|
||||
.addClass('badge-inverse');
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -162,8 +204,19 @@
|
||||
isRelativeUrl: function(url) {
|
||||
var firstChar = url.slice(0, 1);
|
||||
return (firstChar === '.' || firstChar === '/');
|
||||
},
|
||||
|
||||
makeNumberHumanReadable: function(num) {
|
||||
num = parseInt(num, 10);
|
||||
if (num > 999999) {
|
||||
return (num / 1000000).toFixed(1) + 'm';
|
||||
}
|
||||
else if(num > 999) {
|
||||
return (num / 1000).toFixed(1) + 'k';
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (!String.prototype.trim) {
|
||||
@@ -172,25 +225,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.ltrim) {
|
||||
String.prototype.ltrim = function() {
|
||||
return this.replace(/^\s+/, '');
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.rtrim) {
|
||||
String.prototype.rtrim = function() {
|
||||
return this.replace(/\s+$/, '');
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.fulltrim) {
|
||||
String.prototype.fulltrim = function() {
|
||||
return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g, '').replace(/\s+/g, ' ');
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if ('undefined' !== typeof window) {
|
||||
window.utils = module.exports;
|
||||
}
|
||||
@@ -199,4 +233,4 @@
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
} : module);
|
||||
@@ -97,7 +97,4 @@
|
||||
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
<input type="hidden" template-variable="theirid" value="{theirid}" />
|
||||
<input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/account.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
<input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" />
|
||||
@@ -31,41 +31,6 @@
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="upload-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Upload Picture" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Upload Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="uploadForm" action="{relative_path}/user/uploadpicture" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="userPhoto">Upload a picture</label>
|
||||
<input type="file" id="userPhotoInput" name="userPhoto">
|
||||
<p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p>
|
||||
</div>
|
||||
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
|
||||
</form>
|
||||
|
||||
<div id="upload-progress-box" class="progress progress-striped">
|
||||
<div id="upload-progress-bar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0">
|
||||
<span class="sr-only"> success</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert-status" class="alert alert-info hide"></div>
|
||||
<div id="alert-success" class="alert alert-success hide"></div>
|
||||
<div id="alert-error" class="alert alert-danger hide"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
@@ -177,6 +142,3 @@
|
||||
|
||||
<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>
|
||||
|
||||
@@ -26,6 +26,3 @@
|
||||
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a>
|
||||
</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,7 +27,8 @@
|
||||
<option value="category-orange">category-orange</option>
|
||||
</select>
|
||||
<input data-name="description" placeholder="Category Description" value="{categories.description}" class="form-control category_description description"></input>
|
||||
<button type="submit" class="btn btn-default" data-disabled="{categories.disabled}">Disable</button>
|
||||
<input type="hidden" data-name="order" data-value="{categories.order}"></input>
|
||||
<button type="submit" class="btn btn-default disable-btn" data-disabled="{categories.disabled}">Disable</button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
@@ -101,5 +102,3 @@
|
||||
<div class="col-md-3"><i class="icon-adn"></i></div><div class="col-md-3"><i class="icon-android"></i></div><div class="col-md-3"><i class="icon-apple"></i></div><div class="col-md-3"><i class="icon-bitbucket"></i></div><div class="col-md-3"><i class="icon-bitbucket-sign"></i></div><div class="col-md-3"><i class="icon-bitcoin"></i></div><div class="col-md-3"><i class="icon-btc"></i></div><div class="col-md-3"><i class="icon-css3"></i></div><div class="col-md-3"><i class="icon-dribbble"></i></div><div class="col-md-3"><i class="icon-dropbox"></i></div><div class="col-md-3"><i class="icon-facebook"></i></div><div class="col-md-3"><i class="icon-facebook-sign"></i></div><div class="col-md-3"><i class="icon-flickr"></i></div><div class="col-md-3"><i class="icon-foursquare"></i></div><div class="col-md-3"><i class="icon-github"></i></div><div class="col-md-3"><i class="icon-github-alt"></i></div><div class="col-md-3"><i class="icon-github-sign"></i></div><div class="col-md-3"><i class="icon-gittip"></i></div><div class="col-md-3"><i class="icon-google-plus"></i></div><div class="col-md-3"><i class="icon-google-plus-sign"></i></div><div class="col-md-3"><i class="icon-html5"></i></div><div class="col-md-3"><i class="icon-instagram"></i></div><div class="col-md-3"><i class="icon-linkedin"></i></div><div class="col-md-3"><i class="icon-linkedin-sign"></i></div><div class="col-md-3"><i class="icon-linux"></i></div><div class="col-md-3"><i class="icon-maxcdn"></i></div><div class="col-md-3"><i class="icon-pinterest"></i></div><div class="col-md-3"><i class="icon-pinterest-sign"></i></div><div class="col-md-3"><i class="icon-renren"></i></div><div class="col-md-3"><i class="icon-skype"></i></div><div class="col-md-3"><i class="icon-stackexchange"></i></div><div class="col-md-3"><i class="icon-trello"></i></div><div class="col-md-3"><i class="icon-tumblr"></i></div><div class="col-md-3"><i class="icon-tumblr-sign"></i></div><div class="col-md-3"><i class="icon-twitter"></i></div><div class="col-md-3"><i class="icon-twitter-sign"></i></div><div class="col-md-3"><i class="icon-vk"></i></div><div class="col-md-3"><i class="icon-weibo"></i></div><div class="col-md-3"><i class="icon-windows"></i></div><div class="col-md-3"><i class="icon-xing"></i></div><div class="col-md-3"><i class="icon-xing-sign"></i></div><div class="col-md-3"><i class="icon-youtube"></i></div><div class="col-md-3"><i class="icon-youtube-play"></i></div><div class="col-md-3"><i class="icon-youtube-sign"></i></div>
|
||||
<div class="col-md-3"><i class="icon-ambulance"></i></div><div class="col-md-3"><i class="icon-h-sign"></i></div><div class="col-md-3"><i class="icon-hospital"></i></div><div class="col-md-3"><i class="icon-medkit"></i></div><div class="col-md-3"><i class="icon-plus-sign-alt"></i></div><div class="col-md-3"><i class="icon-stethoscope"></i></div><div class="col-md-3"><i class="icon-user-md"></i></div>
|
||||
</div></div></div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/categories.js"></script>
|
||||
@@ -17,10 +17,7 @@
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -2,6 +2,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="upload-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Upload Picture" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Upload Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="uploadForm" action="" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="userPhoto">Upload a picture</label>
|
||||
<input type="file" id="userPhotoInput" name="userPhoto">
|
||||
<p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p>
|
||||
</div>
|
||||
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
|
||||
</form>
|
||||
|
||||
<div id="upload-progress-box" class="progress progress-striped">
|
||||
<div id="upload-progress-bar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0">
|
||||
<span class="sr-only"> success</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert-status" class="alert alert-info hide"></div>
|
||||
<div id="alert-success" class="alert alert-success hide"></div>
|
||||
<div id="alert-error" class="alert alert-danger hide"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="alert_window"></div>
|
||||
|
||||
<div id="footer" class="container" style="padding-top: 50px; display:none;">
|
||||
@@ -9,7 +44,7 @@
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.getScript(RELATIVE_PATH + '/src/forum/admin/footer.js');
|
||||
require(['forum/admin/footer']);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -96,5 +96,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/groups.js"></script>
|
||||
@@ -7,7 +7,6 @@
|
||||
var RELATIVE_PATH = "{relative_path}";
|
||||
</script>
|
||||
<link id="base-theme" href="{relative_path}/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="{relative_path}/vendor/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen">
|
||||
<link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css">
|
||||
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
@@ -19,25 +18,28 @@
|
||||
<script type="text/javascript" src="{relative_path}/src/translator.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/ajaxify.js"></script>
|
||||
<script src="{relative_path}/vendor/jquery/js/jquery.timeago.js"></script>
|
||||
<script src="{relative_path}/vendor/jquery/js/jquery.form.js"></script>
|
||||
<script src="{relative_path}/vendor/requirejs/require.js"></script>
|
||||
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
|
||||
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: "{relative_path}/src/modules",
|
||||
waitSeconds: 3
|
||||
waitSeconds: 3,
|
||||
paths: {
|
||||
"forum": '../forum'
|
||||
}
|
||||
});
|
||||
</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/nodebb.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/admin.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/theme.css" />
|
||||
</head>
|
||||
|
||||
<body class="admin">
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar navbar-inverse navbar-fixed-top header">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
@@ -50,61 +52,96 @@
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a href="/" target="_blank"><i class="icon-book"></i> Forum</a>
|
||||
<a href="/admin/index"><i class="icon-home"></i> Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/index"><i class="icon-home"></i> Home</a>
|
||||
<a href="/admin/settings"><i class="icon-cogs"></i> Settings</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings"><i class="icon-cogs"></i> Settings</a>
|
||||
<a href="/" target="_top"><i class="icon-book"></i> Forum</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="reconnect"></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav pull-right" id="right-menu">
|
||||
<li><a href="/users" id="user_label"></a></li>
|
||||
|
||||
<ul id="logged-in-menu" class="nav navbar-nav navbar-right">
|
||||
<li id="user_label" class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="user_dropdown">
|
||||
<img src="{userpicture}"/>
|
||||
</a>
|
||||
<ul id="user-control-list" class="dropdown-menu" aria-labelledby="user_dropdown">
|
||||
<li>
|
||||
<a id="user-profile-link" href="/user/{userslug}" target="_top"><span>Profile</span></a>
|
||||
</li>
|
||||
<li id="logout-link">
|
||||
<a href="#">Log out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input id="csrf_token" type="hidden" template-variable="csrf" value="{csrf}" />
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">NodeBB</li>
|
||||
<li class='active'><a href='{relative_path}/admin/index'><i class='icon-home'></i> Home</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/categories/active'><i class='icon-folder-close-alt'></i> Categories</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/users/latest'><i class='icon-user'></i> Users</a></li>
|
||||
<li class=""><a href="{relative_path}/admin/groups"><i class="icon-group"></i> Groups</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/topics'><i class='icon-book'></i> Topics</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/themes'><i class='icon-th'></i> Themes</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/plugins'><i class='icon-code-fork'></i> Plugins</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/settings'><i class='icon-cogs'></i> Settings</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/redis'><i class='icon-hdd'></i> Redis</a></li>
|
||||
<li class=''><a href="{relative_path}/admin/motd"><i class="icon-comment"></i> MOTD</a></li>
|
||||
|
||||
<li class='active'>
|
||||
<a href='{relative_path}/admin/index'><i class='icon-home'></i> Home</a>
|
||||
</li>
|
||||
<li><a href='{relative_path}/admin/categories/active'><i class='icon-folder-close-alt'></i> Categories</a></li>
|
||||
<li><a href='{relative_path}/admin/users/latest'><i class='icon-user'></i> Users</a></li>
|
||||
<li><a href="{relative_path}/admin/groups"><i class="icon-group"></i> Groups</a></li>
|
||||
<li><a href='{relative_path}/admin/topics'><i class='icon-book'></i> Topics</a></li>
|
||||
<li><a href='{relative_path}/admin/themes'><i class='icon-th'></i> Themes</a></li>
|
||||
<li><a href='{relative_path}/admin/plugins'><i class='icon-code-fork'></i> Plugins</a></li>
|
||||
<li><a href='{relative_path}/admin/settings'><i class='icon-cogs'></i> Settings</a></li>
|
||||
<li><a href='{relative_path}/admin/redis'><i class='icon-hdd'></i> Redis</a></li>
|
||||
<li><a href='{relative_path}/admin/logger'><i class='icon-th'></i> Logger</a></li>
|
||||
<li><a href="{relative_path}/admin/motd"><i class="icon-comment"></i> MOTD</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Social Authentication</li>
|
||||
<li class=''><a href='{relative_path}/admin/twitter'><i class='icon-twitter-sign'></i> Twitter</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/facebook'><i class='icon-facebook-sign'></i> Facebook</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/gplus'><i class='icon-google-plus-sign'></i> Google+</a></li>
|
||||
<!--<li class="nav-header">Custom Modules</li>-->
|
||||
<!-- <li class=''><a href=''>Search</a></li> -->
|
||||
<li><a href='{relative_path}/admin/twitter'><i class='icon-twitter-sign'></i> Twitter</a></li>
|
||||
<li><a href='{relative_path}/admin/facebook'><i class='icon-facebook-sign'></i> Facebook</a></li>
|
||||
<li><a href='{relative_path}/admin/gplus'><i class='icon-google-plus-sign'></i> Google+</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Plugins</li>
|
||||
<!-- BEGIN plugins -->
|
||||
<li>
|
||||
<a href='{relative_path}/admin{plugins.route}'><i class="{plugins.icon}"></i> {plugins.name}</a>
|
||||
</li>
|
||||
<!-- END plugins -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Unit Tests</li>
|
||||
<ul class="nav nav-list">
|
||||
<li class=''><a href='{relative_path}/admin/testing/categories'>Categories</a></li>
|
||||
<!--<li class=''><a href='{relative_path}/admin/testing/topics'>Topics</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/posts'>Posts</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/accounts'>Accounts</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/chat'>Chat</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/notifications'>Notifications</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/friends'>Friends</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/feed'>RSS Feed</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/emails'>Emails</a></li>-->
|
||||
<li><a href='{relative_path}/admin/testing/categories'>Categories</a></li>
|
||||
<!--<li><a href='{relative_path}/admin/testing/topics'>Topics</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/posts'>Posts</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/accounts'>Accounts</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/chat'>Chat</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/notifications'>Notifications</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/friends'>Friends</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/feed'>RSS Feed</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/emails'>Emails</a></li>-->
|
||||
</ul>
|
||||
</ul>
|
||||
</div><!--/.well -->
|
||||
</div><!--/span-->
|
||||
<div class="col-md-9" id="content">
|
||||
|
||||
<div class="col-md-9" id="content">
|
||||
|
||||
@@ -18,5 +18,3 @@
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/index.js"></script>
|
||||
46
public/templates/admin/logger.tpl
Normal file
46
public/templates/admin/logger.tpl
Normal file
@@ -0,0 +1,46 @@
|
||||
<h1>Logger</h1>
|
||||
<hr />
|
||||
|
||||
<h3>Logger Settings</h3>
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<p>
|
||||
By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.
|
||||
</p>
|
||||
<br/>
|
||||
<p>
|
||||
Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.
|
||||
</p>
|
||||
<br/>
|
||||
|
||||
<form>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" data-field="loggerStatus"> <strong>Enable HTTP logging</strong>
|
||||
</label>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" data-field="loggerIOStatus"> <strong>Enable socket.io event logging</strong>
|
||||
</label>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<label>Path to log file</label>
|
||||
<input class="form-control" type="text" placeholder="/path/to/log/file.log ::: leave blank to log to your terminal" data-field="loggerPath" />
|
||||
<br />
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -24,10 +24,7 @@
|
||||
<button class="btn btn-lg btn-primary" id="save" checked>Save</button>
|
||||
|
||||
<script>
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -21,5 +21,3 @@
|
||||
Full documentation regarding plugin authoring can be found in the <a target="_blank" href="https://github.com/designcreateplay/NodeBB/wiki/Writing-Plugins-for-NodeBB">NodeBB Wiki</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/plugins.js"></script>
|
||||
@@ -8,6 +8,11 @@
|
||||
<input class="form-control" type="text" placeholder="Your Community Name" data-field="title" /><br />
|
||||
<label>Site Description</label>
|
||||
<input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br />
|
||||
<label>Site Keywords</label>
|
||||
<input type="text" class="form-control" placeholder="Keywords describing your community, comma-seperated" data-field="keywords" /><br />
|
||||
<label>Site Logo</label>
|
||||
<input id="logoUrl" type="text" class="form-control" placeholder="Path to a logo to display on forum header" data-field="brand:logo" /><br />
|
||||
<input id="uploadLogoBtn" type="button" class="btn btn-default" value="Upload"></input> <br />
|
||||
<label>Imgur Client ID</label>
|
||||
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
|
||||
<label>Maximum User Image Size</label>
|
||||
@@ -19,10 +24,8 @@
|
||||
<h3>Privilege Thresholds</h3>
|
||||
<div class="alert alert-warning">
|
||||
<p>Use <strong>privilege thresholds</strong> to manage how much reputation a user must gain to receive moderator access.</p><br />
|
||||
<strong>Manage Thread</strong><br /> <input type="text" class="form-control" value="1000"><br />
|
||||
<strong>Moderate Users</strong><br /> <input type="text" class="form-control" value="10000"><br />
|
||||
<strong>Create Pinned Topics</strong><br /> <input type="text" class="form-control" value="100000"><br />
|
||||
|
||||
<strong>Manage Thread</strong><br /> <input type="text" class="form-control" value="1000" data-field="privileges:manage_topic"><br />
|
||||
<strong>Manage Content</strong><br /> <input type="text" class="form-control" value="1000" data-field="privileges:manage_content"><br />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -70,17 +73,23 @@
|
||||
<strong>Post Delay</strong><br /> <input type="text" class="form-control" value="10000" data-field="postDelay"><br />
|
||||
<strong>Minimum Title Length</strong><br /> <input type="text" class="form-control" value="3" data-field="minimumTitleLength"><br />
|
||||
<strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br />
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-field="allowGuestPosting"> <strong>Allow guests to post without logging in</strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-field="useOutgoingLinksPage"> <strong>Use Outgoing Links Warning Page</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -9,25 +9,25 @@ jQuery(document).ready(function () {
|
||||
|
||||
QUnit.init();
|
||||
asyncTest( "Loading Categories", function() {
|
||||
|
||||
|
||||
jQuery.get(RELATIVE_PATH + '/api/home', function(data) {
|
||||
ok( data.categories.length > 0, JSON.stringify(data.categories) );
|
||||
|
||||
|
||||
start();
|
||||
|
||||
|
||||
for (var i = 0, ii = data.categories.length; i < ii; i++) {
|
||||
var category = data.categories[i],
|
||||
slug = 'category/' + category.slug;
|
||||
|
||||
asyncTest( "Loading Category '" + category.name + "' located at " + slug, function() {
|
||||
jQuery.get(config.api_url + slug, function(data) {
|
||||
jQuery.get(RELATIVE_PATH + '/api/' + slug, function(data) {
|
||||
ok( data.category_name, JSON.stringify(data) ); //todo: check this against data.categories
|
||||
start();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.start();
|
||||
});
|
||||
|
||||
@@ -23,4 +23,10 @@
|
||||
<button class="btn btn-warning" id="revert_theme">Revert</button> This will remove any custom theme applied to your NodeBB, and restore the base theme.
|
||||
</p>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/themes.js"></script>
|
||||
<script>
|
||||
var bootswatchListener = function(data) {
|
||||
require(['forum/admin/themes'], function(t) {
|
||||
t.render(data);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<a target="_blank" href="{relative_path}/topic/{topics.slug}">{topics.title}</a>
|
||||
<ul>
|
||||
<li><i class="icon-time"></i> Posted {topics.relativeTime} ago by {topics.username}</li>
|
||||
<li><i class="icon-time"></i> Posted <span class="timeago" title="{topics.relativeTime}"></span> by {topics.username}</li>
|
||||
<li><i class="icon-comments"></i> {topics.postcount} post(s)</li>
|
||||
</ul>
|
||||
<div class="clear"></div>
|
||||
@@ -22,5 +22,3 @@
|
||||
<div class="text-center">
|
||||
<button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/topics.js"></script>
|
||||
@@ -17,10 +17,7 @@
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<a href="/user/{users.userslug}">{users.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span id='reputation'>{users.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
<span id='reputation'>{users.reputation}</span>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span id='postcount'>{users.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
<span id='postcount'>{users.postcount}</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn btn-default ban-btn">Ban</a>
|
||||
@@ -42,6 +42,3 @@
|
||||
<button id="load-more-users-btn" class="btn btn-primary">Load More</button>
|
||||
</div>
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/users.js"></script>
|
||||
|
||||
@@ -26,38 +26,57 @@
|
||||
|
||||
<div class="category row">
|
||||
<div class="{topic_row_size}">
|
||||
<ul id="topics-container">
|
||||
<!-- BEGIN topics -->
|
||||
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<ul id="topics-container" itemscope itemtype="http://www.schema.org/ItemList">
|
||||
<meta itemprop="itemListOrder" content="descending">
|
||||
<!-- BEGIN topics -->
|
||||
<li class="category-item {topics.deleted-class}" itemprop="itemListElement">
|
||||
<div class="row">
|
||||
<div class="col-md-12 topic-row">
|
||||
<div class="latest-post visible-lg visible-md">
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
<div class="pull-right">
|
||||
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p>{topics.teaser_text}</p>
|
||||
<p class="meta">
|
||||
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href="../../topic/{topics.slug}" itemprop="url">
|
||||
<h3>
|
||||
<meta itemprop="name" content="{topics.title}">
|
||||
|
||||
<span class="topic-title">
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
{topics.title}
|
||||
</span>
|
||||
</h3>
|
||||
</a>
|
||||
<small>
|
||||
<span class="topic-stats">
|
||||
posts
|
||||
<strong>{topics.postcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span class="topic-stats">
|
||||
views
|
||||
<strong>{topics.viewcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span>
|
||||
<a href="/user/{topics.userslug}">
|
||||
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
|
||||
</a>
|
||||
posted <span class="timeago" title="{topics.relativeTime}"></span>
|
||||
</span>
|
||||
|
||||
<span class="pull-right hidden-xs">
|
||||
<a href="/user/{topics.teaser_userslug}">
|
||||
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||
</a>
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
replied
|
||||
</a>
|
||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- END topics -->
|
||||
<!-- END topics -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3 {show_sidebar} category-sidebar">
|
||||
@@ -74,9 +93,9 @@
|
||||
<div class="block-header">
|
||||
[[category:sidebar.active_participants]]
|
||||
</div>
|
||||
<div class="block-content">
|
||||
<div class="block-content active-users">
|
||||
<!-- BEGIN active_users -->
|
||||
<a href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded" /></a>
|
||||
<a data-uid="{active_users.uid}" href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded" /></a>
|
||||
<!-- END active_users -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,12 +109,20 @@
|
||||
<!-- END moderators -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- BEGIN sidebars -->
|
||||
<div class="sidebar-block img-thumbnail {sidebars.block_class}">
|
||||
<div class="block-header">
|
||||
{sidebars.header}
|
||||
</div>
|
||||
<div class="block-content">
|
||||
{sidebars.content}
|
||||
</div>
|
||||
</div>
|
||||
<!-- END sidebars -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" template-variable="category_id" value="{category_id}" />
|
||||
<input type="hidden" template-variable="twitter-intent-url" value="{twitter-intent-url}" />
|
||||
<input type="hidden" template-variable="facebook-share-url" value="{facebook-share-url}" />
|
||||
<input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/category.js"></script>
|
||||
<input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
|
||||
@@ -1,44 +1,36 @@
|
||||
{
|
||||
"custom_mapping": {
|
||||
"admin/testing/categories.*": "admin/testing/categories",
|
||||
"admin/topics.*": "admin/topics",
|
||||
"admin/categories.*": "admin/categories",
|
||||
"admin/users.*": "admin/users",
|
||||
"admin/redis.*": "admin/redis",
|
||||
"admin/index.*": "admin/index",
|
||||
"admin/themes.*": "admin/themes",
|
||||
"admin/plugins.*": "admin/plugins",
|
||||
"^admin/testing/categories.*": "admin/testing/categories",
|
||||
"^admin/topics.*": "admin/topics",
|
||||
"^admin/categories.*": "admin/categories",
|
||||
"^admin/users.*": "admin/users",
|
||||
"^admin/redis.*": "admin/redis",
|
||||
"^admin/index.*": "admin/index",
|
||||
"^admin/themes.*": "admin/themes",
|
||||
"^admin/plugins/?$": "admin/plugins",
|
||||
"^admin/settings.*": "admin/settings",
|
||||
"admin/twitter.*": "admin/twitter",
|
||||
"admin/facebook.*": "admin/facebook",
|
||||
"admin/gplus.*": "admin/gplus",
|
||||
"admin/motd/?$": "admin/motd",
|
||||
"admin/groups/?$": "admin/groups",
|
||||
"install/?$": "install/mail",
|
||||
"install/mail/?": "install/mail",
|
||||
"install/social/?": "install/social",
|
||||
"install/privileges/?": "install/privileges",
|
||||
"users/sort-posts": "users",
|
||||
"users/latest": "users",
|
||||
"users/sort-reputation": "users",
|
||||
"users/search": "users",
|
||||
"user.*edit": "accountedit",
|
||||
"user.*following": "following",
|
||||
"user.*followers": "followers",
|
||||
"user.*settings": "accountsettings",
|
||||
"user.*favourites": "favourites",
|
||||
"user/.*": "account",
|
||||
"^admin/twitter.*": "admin/twitter",
|
||||
"^admin/facebook.*": "admin/facebook",
|
||||
"^admin/logger.*": "admin/logger",
|
||||
"^admin/gplus.*": "admin/gplus",
|
||||
"^admin/motd/?$": "admin/motd",
|
||||
"^admin/groups/?$": "admin/groups",
|
||||
"^users/sort-posts": "users",
|
||||
"^users/latest": "users",
|
||||
"^users/sort-reputation": "users",
|
||||
"^users/search": "users",
|
||||
"^user.*edit": "accountedit",
|
||||
"^user.*following": "following",
|
||||
"^user.*followers": "followers",
|
||||
"^user.*settings": "accountsettings",
|
||||
"^user.*favourites": "favourites",
|
||||
"^user/.*": "account",
|
||||
|
||||
"recent": "recent",
|
||||
"unread": "unread",
|
||||
"popular": "category",
|
||||
"active": "category",
|
||||
"search": "search",
|
||||
"reset/.*": "reset_code",
|
||||
"reset": "reset"
|
||||
"^recent/.*": "recent",
|
||||
|
||||
"reset/.*": "reset_code"
|
||||
},
|
||||
"force_refresh": {
|
||||
"logout": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<div class="well favourites">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/user/{userslug}">{username}</a>
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/user/{userslug}/favourites">favourites</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +23,3 @@
|
||||
</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>
|
||||
|
||||
@@ -15,15 +15,17 @@
|
||||
<img src="{followers.picture}" class="img-thumbnail"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="/user/{followers.userslug}">{followers.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='formatted-number'>{followers.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='formatted-number'>{followers.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
<div class="user-info">
|
||||
<a href="/user/{followers.userslug}">{followers.username}</a>
|
||||
<br/>
|
||||
<div title="reputation" class="reputation">
|
||||
<span class='formatted-number'>{followers.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count" class="post-count">
|
||||
<span class='formatted-number'>{followers.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END followers -->
|
||||
@@ -34,6 +36,3 @@
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
<input type="hidden" template-variable="theirid" value="{theirid}" />
|
||||
<input type="hidden" template-variable="followersCount" value="{followersCount}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/followers.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
@@ -15,18 +15,20 @@
|
||||
<img src="{following.picture}" class="img-thumbnail"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="/user/{following.userslug}">{following.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='formatted-number'>{following.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
<div class="user-info">
|
||||
<a href="/user/{following.userslug}">{following.username}</a>
|
||||
<br/>
|
||||
<div title="reputation" class="reputation">
|
||||
<span class='formatted-number'>{following.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count" class="post-count">
|
||||
<span class='formatted-number'>{following.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='formatted-number'>{following.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
<a id="unfollow-btn" href="#" class="btn btn-default unfollow-btn" followingUid="{following.uid}" data-username="{following.username}">Unfollow</a>
|
||||
</div>
|
||||
|
||||
<!-- END following -->
|
||||
</div>
|
||||
<div id="no-following-notice" class="alert alert-warning hide">This user isn't following anyone :(</div>
|
||||
@@ -35,6 +37,3 @@
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
<input type="hidden" template-variable="theirid" value="{theirid}" />
|
||||
<input type="hidden" template-variable="followingCount" value="{followingCount}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/following.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
</div><!--END container -->
|
||||
|
||||
<div id="chat-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true">
|
||||
<div id="chat-modal" class="modal chat-modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true" data-backdrop="none">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -21,39 +21,50 @@
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="upload-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Upload Picture" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Upload Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="uploadForm" action="" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="userPhoto">Upload a picture</label>
|
||||
<input type="file" id="userPhotoInput" name="userPhoto">
|
||||
<p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p>
|
||||
</div>
|
||||
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
|
||||
</form>
|
||||
|
||||
<div id="upload-progress-box" class="progress progress-striped">
|
||||
<div id="upload-progress-bar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0">
|
||||
<span class="sr-only"> success</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert-status" class="alert alert-info hide"></div>
|
||||
<div id="alert-success" class="alert alert-success hide"></div>
|
||||
<div id="alert-error" class="alert alert-danger hide"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="alert_window"></div>
|
||||
|
||||
|
||||
<footer id="footer" class="container footer">
|
||||
<div class="row footer-stats">
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_online"></span><br /><small>[[footer:stats.online]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_users"></span><br /><small>[[footer:stats.users]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_topics"></span><br /><small>[[footer:stats.topics]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_posts"></span><br /><small>[[footer:stats.posts]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copyright">Copyright © 2013 <a target="_blank" href="http://www.nodebb.org">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
$.getScript(RELATIVE_PATH + '/src/forum/footer.js');
|
||||
require(['forum/footer']);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
{meta_tags}
|
||||
<link href="{cssSrc}" rel="stylesheet" media="screen">
|
||||
<link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css">
|
||||
{link_tags}
|
||||
<!-- BEGIN pluginCSS -->
|
||||
<link rel="stylesheet" href="{pluginCSS.path}">
|
||||
<!-- END pluginCSS -->
|
||||
<script>
|
||||
var RELATIVE_PATH = "{relative_path}";
|
||||
</script>
|
||||
@@ -15,11 +19,14 @@
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: "{relative_path}/src/modules",
|
||||
waitSeconds: 3
|
||||
waitSeconds: 3,
|
||||
paths: {
|
||||
"forum": '../forum'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/theme.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -31,9 +38,14 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="/">
|
||||
<h1 class="navbar-brand forum-title">{title}</h1>
|
||||
</a>
|
||||
<div>
|
||||
<a href="/">
|
||||
<img class="{brand:logo:display} forum-logo" src="{brand:logo}" />
|
||||
</a>
|
||||
<a href="/">
|
||||
<h1 class="navbar-brand forum-title">{title}</h1>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-collapse collapse navbar-ex1-collapse">
|
||||
@@ -47,32 +59,37 @@
|
||||
<li>
|
||||
<a href="/users">[[global:header.users]]</a>
|
||||
</li>
|
||||
<li class="{adminDisplay}">
|
||||
<a href="/admin"><i class="icon-cogs"></i> [[global:header.admin]]</a>
|
||||
</li>
|
||||
<li class="visible-xs">
|
||||
<a href="/search">[[global:header.search]]</a>
|
||||
</li>
|
||||
<li class="visible-xs">
|
||||
<a href="/search">Search</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/"></a>
|
||||
<!-- BEGIN navigation -->
|
||||
<li class="{navigation.class}">
|
||||
<a href="{navigation.route}">{navigation.text}</a>
|
||||
</li>
|
||||
<!-- END navigation -->
|
||||
</ul>
|
||||
|
||||
<form id="search-form" class="navbar-form navbar-right hidden-xs" role="search" method="GET" action="">
|
||||
<div class="hide" id="search-fields">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Search" name="query" value="">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default hide">[[global:search]]</button>
|
||||
</div>
|
||||
<button id="search-button" type="button" class="btn btn-link"><i class="icon-search"></i></button>
|
||||
</form>
|
||||
|
||||
<ul id="logged-in-menu" class="nav navbar-nav navbar-right hide">
|
||||
<li>
|
||||
<a href="#" id="reconnect"></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<form id="search-form" class="navbar-form navbar-right hidden-xs" role="search" method="GET" action="">
|
||||
<div class="hide" id="search-fields">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Search" name="query" value="">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default hide">[[global:search]]</button>
|
||||
</div>
|
||||
<button id="search-button" type="button" class="btn btn-link"><i class="icon-search"></i></button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
<li id="notifications-list" class="notifications dropdown text-center hidden-xs">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="notif_dropdown"><i class="icon-circle-blank"></i></a>
|
||||
<ul id="notif-list" class="dropdown-menu" aria-labelledby="notif_dropdown">
|
||||
@@ -82,24 +99,33 @@
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a id="user_label" href="">
|
||||
<li id="user_label" class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="user_dropdown">
|
||||
<img src=""/>
|
||||
<span></span>
|
||||
</a>
|
||||
<ul id="user-control-list" class="dropdown-menu" aria-labelledby="user_dropdown">
|
||||
<li>
|
||||
<a id="user-profile-link" href=""><span>Profile</span></a>
|
||||
</li>
|
||||
<li id="logout-link">
|
||||
<a href="#">Log out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li id="logout-link">
|
||||
<a href="#">Log out</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="logged-out-menu" class="nav navbar-nav navbar-right">
|
||||
<li id="register-link">
|
||||
<a href="/register">Register</a>
|
||||
</li>
|
||||
<li id="login-link">
|
||||
<a href="/login">Login</a>
|
||||
<li>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="loggedout_dropdown"><i class="icon-signin"></i></a>
|
||||
<ul class="dropdown-menu" aria-labelledby="loggedout_dropdown">
|
||||
<li>
|
||||
<a href="/register">Register</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/login">Login</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<div class="jumbotron {motd_class}">
|
||||
<div class="motd{motd_class}">
|
||||
{motd}
|
||||
</div>
|
||||
|
||||
<div class="row home">
|
||||
<div class="row home" itemscope itemtype="http://www.schema.org/ItemList">
|
||||
<!-- BEGIN categories -->
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<a href="category/{categories.slug}">
|
||||
<a href="category/{categories.slug}" itemprop="url">
|
||||
<meta itemprop="name" content="{categories.name}">
|
||||
<h4><span class="badge {categories.badgeclass}">{categories.topic_count} </span> {categories.name}</h4>
|
||||
<div class="icon {categories.blockclass}">
|
||||
<div id="category-{categories.cid}" class="category-slider-{categories.post_count}">
|
||||
<div class="category-box"><i class="{categories.icon} icon-4x"></i></div>
|
||||
<div class="category-box">{categories.description}</div>
|
||||
<div class="category-box" itemprop="description">{categories.description}</div>
|
||||
<!-- BEGIN posts -->
|
||||
<div class="category-box">
|
||||
<div class="post-preview">
|
||||
@@ -25,4 +26,27 @@
|
||||
</a>
|
||||
</div>
|
||||
<!-- END categories -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row footer-stats">
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_online"></span><br /><small>[[footer:stats.online]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_users"></span><br /><small>[[footer:stats.users]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_topics"></span><br /><small>[[footer:stats.topics]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<div class="stats-card well">
|
||||
<h2><span id="stats_posts"></span><br /><small>[[footer:stats.posts]]</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
<link rel="stylesheet" href="/vendor/fontawesome/css/font-awesome.min.css">
|
||||
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
|
||||
<script type="text/javascript" src="/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js"></script>
|
||||
<script type="text/javascript" src="/vendor/jquery/js/jquery.form.js"></script>
|
||||
<script type="text/javascript" src="/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
|
||||
<script type="text/javascript" src="/src/app.js"></script>
|
||||
<script type="text/javascript" src="/src/templates.js"></script>
|
||||
<script type="text/javascript" src="/src/ajaxify.js"></script>
|
||||
<script type="text/javascript" src="/src/jquery.form.js"></script>
|
||||
<script type="text/javascript" src="/src/utils.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css" />
|
||||
|
||||
@@ -60,5 +60,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/login.js"></script>
|
||||
14
public/templates/notifications.tpl
Normal file
14
public/templates/notifications.tpl
Normal file
@@ -0,0 +1,14 @@
|
||||
<h2>[[notifications:title]]</h2>
|
||||
|
||||
<button type="button" class="btn btn-default" id="mark-all-notifs-read">Mark All as Read</button>
|
||||
|
||||
<ul class="notifications-list">
|
||||
<!-- BEGIN notifications -->
|
||||
<li data-nid="{notifications.nid}" class="{notifications.readClass}">
|
||||
<a href="..{notifications.path}">{notifications.text}</a>
|
||||
<p class="timestamp">
|
||||
<span class="timeago" title="{notifications.datetimeISO}"></span>
|
||||
</p>
|
||||
</li>
|
||||
<!-- END notifications -->
|
||||
</ul>
|
||||
@@ -4,8 +4,16 @@
|
||||
<div id="category_active_users"></div>
|
||||
</ol>
|
||||
|
||||
<ul class="nav nav-pills">
|
||||
<li class=''><a href='/recent/day'>[[recent:day]]</a></li>
|
||||
<li class=''><a href='/recent/week'>[[recent:week]]</a></li>
|
||||
<li class=''><a href='/recent/month'>[[recent:month]]</a></li>
|
||||
</ul>
|
||||
|
||||
<br />
|
||||
|
||||
<a href="/recent">
|
||||
<div class="alert hide" id="new-topics-alert"></div>
|
||||
<div class="alert alert-warning hide" id="new-topics-alert"></div>
|
||||
</a>
|
||||
|
||||
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
|
||||
@@ -16,38 +24,51 @@
|
||||
<div class="{topic_row_size}">
|
||||
<ul id="topics-container">
|
||||
<!-- BEGIN topics -->
|
||||
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xs-12 topic-row img-thumbnail">
|
||||
<div class="latest-post visible-lg visible-md">
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
<div class="pull-right">
|
||||
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p>{topics.teaser_text}</p>
|
||||
<p class="meta">
|
||||
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
</p>
|
||||
</div>
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xs-12 topic-row img-thumbnail">
|
||||
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<h3><span class="topic-title"><strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong> {topics.title}</span></h3>
|
||||
</a>
|
||||
<small>
|
||||
<span class="topic-stats">
|
||||
posts
|
||||
<strong>{topics.postcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span class="topic-stats">
|
||||
views
|
||||
<strong>{topics.viewcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span>
|
||||
<a href="/user/{topics.userslug}">
|
||||
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
|
||||
</a>
|
||||
</div>
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
posted in
|
||||
<a href="../../category/{topics.categorySlug}">
|
||||
<i class="{topics.categoryIcon}"></i> {topics.categoryName}
|
||||
</a>
|
||||
<span class="timeago" title="{topics.relativeTime}"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="pull-right hidden-xs">
|
||||
<a href="/user/{topics.teaser_userslug}">
|
||||
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||
</a>
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
replied
|
||||
</a>
|
||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</li>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<!-- END topics -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/recent.js"></script>
|
||||
|
||||
@@ -80,5 +80,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/register.js"></script>
|
||||
@@ -26,5 +26,3 @@
|
||||
<button class="btn btn-primary btn-block btn-lg" id="reset" type="submit">Reset Password</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/reset.js"></script>
|
||||
@@ -34,6 +34,3 @@
|
||||
</form>
|
||||
</div>
|
||||
<input type="hidden" template-variable="reset_code" value="{reset_code}" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/reset_code.js"></script>
|
||||
@@ -53,5 +53,3 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/search.js"></script>
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
<div class="topic">
|
||||
<input type="hidden" template-variable="expose_tools" value="{expose_tools}" />
|
||||
<input type="hidden" template-variable="topic_id" value="{topic_id}" />
|
||||
<input type="hidden" template-variable="locked" value="{locked}" />
|
||||
<input type="hidden" template-variable="deleted" value="{deleted}" />
|
||||
<input type="hidden" template-variable="pinned" value="{pinned}" />
|
||||
<input type="hidden" template-variable="topic_name" value="{topic_name}" />
|
||||
<input type="hidden" template-variable="postcount" value="{postcount}" />
|
||||
<input type="hidden" template-variable="twitter-intent-url" value="{twitter-intent-url}" />
|
||||
<input type="hidden" template-variable="facebook-share-url" value="{facebook-share-url}" />
|
||||
<input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
|
||||
|
||||
<div class="topic row">
|
||||
<ol class="breadcrumb">
|
||||
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
|
||||
@@ -9,26 +20,29 @@
|
||||
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="icon-rss-sign"></i></a></span>
|
||||
</li>
|
||||
<div id="thread_active_users" class="active-users pull-right hidden-xs"></div>
|
||||
<div class="thread_active_users active-users pull-right hidden-xs"></div>
|
||||
</ol>
|
||||
|
||||
<ul id="post-container" class="container" data-tid="{topic_id}">
|
||||
<!-- BEGIN main_posts -->
|
||||
<a id="post_anchor_{main_posts.pid}" name="{main_posts.pid}"></a>
|
||||
<li class="row post-row main-post" data-pid="{main_posts.pid}" data-uid="{main_posts.uid}" data-username="{main_posts.username}" data-deleted="{main_posts.deleted}">
|
||||
<li class="row post-row main-post" data-pid="{main_posts.pid}" data-uid="{main_posts.uid}" data-username="{main_posts.username}" data-deleted="{main_posts.deleted}" itemscope itemtype="http://schema.org/Article">
|
||||
<div class="col-md-12">
|
||||
<div class="post-block">
|
||||
<meta itemprop="datePublished" content="{main_posts.relativeTime}">
|
||||
<meta itemprop="dateModified" content="{main_posts.relativeEditTime}">
|
||||
<meta itemprop="url" content="/topic/{slug}/">
|
||||
<a class="avatar" href="/user/{main_posts.userslug}">
|
||||
<img src="{main_posts.picture}" align="left" class="img-thumbnail" width=150 height=150 /><br />
|
||||
<img itemprop="image" src="{main_posts.picture}" align="left" class="img-thumbnail" width=150 height=150 /><br />
|
||||
</a>
|
||||
<h3>
|
||||
<p id="topic_title_{main_posts.pid}" class="topic-title">{topic_name}</p>
|
||||
<p id="topic_title_{main_posts.pid}" class="topic-title" itemprop="name">{topic_name}</p>
|
||||
</h3>
|
||||
|
||||
<div class="topic-buttons">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" type="button" title="[[topic:posted_by]] {main_posts.username}">
|
||||
<span class="username-field" href="/user/{main_posts.userslug}">{main_posts.username} </span>
|
||||
<span class="username-field" href="/user/{main_posts.userslug}" itemprop="author" itemscope itemtype="http://schema.org/Person">{main_posts.username} </span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
@@ -49,20 +63,29 @@
|
||||
<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="icon-reply"></i></button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="btn-group pull-right post-tools">
|
||||
<button class="btn btn-sm btn-default link" type="button" title="[[topic:link]]"><i class="icon-link"></i></button>
|
||||
<button class="btn btn-sm btn-default edit {main_posts.display_moderator_tools}" type="button" title="[[topic:edit]]"><i class="icon-pencil"></i></button>
|
||||
<button class="btn btn-sm btn-default delete {main_posts.display_moderator_tools}" type="button" title="[[topic:delete]]"><i class="icon-trash"></i></button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group pull-right post-tools">
|
||||
<button class="btn btn-sm btn-default link" type="button" title="[[topic:link]]"><i class="icon-link"></i></button>
|
||||
<button class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="icon-facebook"></i></button>
|
||||
<button class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="icon-twitter"></i></button>
|
||||
<button class="btn btn-sm btn-default google-share" type="button" title=""><i class="icon-google-plus"></i></button>
|
||||
</div>
|
||||
|
||||
<input id="post_{main_posts.pid}_link" value="" class="pull-right" style="display:none;"></input>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content_{main_posts.pid}" class="post-content">{main_posts.content}</div>
|
||||
<div id="content_{main_posts.pid}" class="post-content" itemprop="articleBody">{main_posts.content}</div>
|
||||
<div class="post-signature">{main_posts.signature}</div>
|
||||
<div class="post-info">
|
||||
<span class="pull-left">
|
||||
{main_posts.additional_profile_info}
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
posted <span class="relativeTimeAgo timeago" title="{main_posts.relativeTime}"></span>
|
||||
<span class="{main_posts.edited-class}">| last edited by <strong><a href="/user/{main_posts.editorslug}">{main_posts.editorname}</a></strong></span>
|
||||
@@ -77,19 +100,21 @@
|
||||
|
||||
<!-- BEGIN posts -->
|
||||
<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
|
||||
<li class="row post-row sub-posts" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-deleted="{posts.deleted}">
|
||||
<li class="row post-row sub-posts" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
|
||||
<meta itemprop="datePublished" content="{posts.relativeTime}">
|
||||
<meta itemprop="dateModified" content="{posts.relativeEditTime}">
|
||||
<div class="col-md-1 profile-image-block hidden-xs hidden-sm">
|
||||
<a href="/user/{posts.userslug}">
|
||||
<img src="{posts.picture}" align="left" class="img-thumbnail" />
|
||||
<img src="{posts.picture}" align="left" class="img-thumbnail" itemprop="image" />
|
||||
<span class="label label-danger {posts.show_banned}">[[topic:banned]]</span>
|
||||
</a>
|
||||
<span class="label label-danger {posts.show_banned}">[[topic:banned]]</span>
|
||||
</div>
|
||||
<div class="col-md-11">
|
||||
<div class="post-block">
|
||||
<div class="topic-buttons">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" type="button" title="Posted by {posts.username}">
|
||||
<span class="username-field" href="/user/{posts.userslug}">{posts.username} </span>
|
||||
<span class="username-field" href="/user/{posts.userslug}" itemprop="author">{posts.username} </span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
|
||||
@@ -119,9 +144,12 @@
|
||||
<input id="post_{posts.pid}_link" value="" class="pull-right" style="display:none;"></input>
|
||||
</div>
|
||||
|
||||
<div id="content_{posts.pid}" class="post-content">{posts.content}</div>
|
||||
<div id="content_{posts.pid}" class="post-content" itemprop="text">{posts.content}</div>
|
||||
<div class="post-signature">{posts.signature}</div>
|
||||
<div class="post-info">
|
||||
<span class="pull-left">
|
||||
{posts.additional_profile_info}
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
posted <span class="relativeTimeAgo timeago" title="{posts.relativeTime}"></span>
|
||||
<span class="{posts.edited-class}">| last edited by <strong><a href="/user/{posts.editorslug}">{posts.editorname}</a></strong></span>
|
||||
@@ -167,39 +195,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true">
|
||||
<div id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Move Topic" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Move Thread</h3>
|
||||
<h3>Move Topic</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="categories-loading"><i class="icon-spin icon-refresh"></i> [[topic:load_categories]]</p>
|
||||
<ul class="category-list"></ul>
|
||||
<p>
|
||||
[[topic:disabled_categories_note]]
|
||||
</p>
|
||||
<div id="move-confirm" style="display: none;">
|
||||
<hr />
|
||||
<div class="alert">This topic will be moved to the category <strong><span id="confirm-category-name"></span></strong></div>
|
||||
<div class="alert alert-info">This topic will be moved to the category <strong><span id="confirm-category-name"></span></strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal" id="move_thread_cancel">[[global:buttons.close]]</a>
|
||||
<button type="button" class="btn btn-primary" id="move_thread_commit" disabled>[[topic:confirm_move]]</a>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal" id="move_thread_cancel">[[global:buttons.close]]</button>
|
||||
<button type="button" class="btn btn-primary" id="move_thread_commit" disabled>[[topic:confirm_move]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<input type="hidden" template-variable="expose_tools" value="{expose_tools}" />
|
||||
<input type="hidden" template-variable="topic_id" value="{topic_id}" />
|
||||
<input type="hidden" template-variable="locked" value="{locked}" />
|
||||
<input type="hidden" template-variable="deleted" value="{deleted}" />
|
||||
<input type="hidden" template-variable="pinned" value="{pinned}" />
|
||||
<input type="hidden" template-variable="topic_name" value="{topic_name}" />
|
||||
<input type="hidden" template-variable="postcount" value="{postcount}" />
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/topic.js"></script>
|
||||
@@ -5,54 +5,66 @@
|
||||
<div id="category_active_users"></div>
|
||||
</ol>
|
||||
|
||||
<a href="/unread">
|
||||
<div class="alert hide" id="new-topics-alert"></div>
|
||||
</a>
|
||||
|
||||
<div class="alert alert-warning {no_topics_message}" id="category-no-topics">
|
||||
<strong>[[unread:no_unread_topics]]</strong>
|
||||
</div>
|
||||
|
||||
<button id="mark-allread-btn" class="btn btn-primary {show_markallread_button}">[[unread:mark_all_read]]</button>
|
||||
|
||||
<a href="/unread">
|
||||
<div class="alert alert-warning hide" id="new-topics-alert"></div>
|
||||
</a>
|
||||
|
||||
<div class="category row">
|
||||
<div class="{topic_row_size}">
|
||||
<ul id="topics-container" data-next-start="{nextStart}">
|
||||
<!-- BEGIN topics -->
|
||||
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<div class="row">
|
||||
<div class="col-md-12 topic-row img-thumbnail">
|
||||
<div class="latest-post visible-lg visible-md">
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
<div class="pull-right">
|
||||
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p>{topics.teaser_text}</p>
|
||||
<p class="meta">
|
||||
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
</p>
|
||||
</div>
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<div class="row">
|
||||
<div class="col-md-12 topic-row">
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<h3><span class="topic-title"><strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong> {topics.title}</span></h3>
|
||||
</a>
|
||||
<small>
|
||||
<span class="topic-stats">
|
||||
posts
|
||||
<strong>{topics.postcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span class="topic-stats">
|
||||
views
|
||||
<strong>{topics.viewcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span>
|
||||
<a href="/user/{topics.userslug}">
|
||||
<img class="img-rounded teaser-pic" src="{topics.picture}" title="{topics.username}"/>
|
||||
</a>
|
||||
</div>
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
posted in
|
||||
<a href="../../category/{topics.categorySlug}">
|
||||
<i class="{topics.categoryIcon}"></i> {topics.categoryName}
|
||||
</a>
|
||||
<span class="timeago" title="{topics.relativeTime}"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="pull-right hidden-xs">
|
||||
<a href="/user/{topics.teaser_userslug}">
|
||||
<img class="img-rounded teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||
</a>
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
replied
|
||||
</a>
|
||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</li>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<!-- END topics -->
|
||||
</ul>
|
||||
<button id="load-more-btn" class="btn btn-primary hide">[[unread:load_more]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/unread.js"></script>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<ul id="users-container" class="users-container">
|
||||
<!-- BEGIN users -->
|
||||
<div class="users-box">
|
||||
<li class="users-box registered-user">
|
||||
<a href="/user/{users.userslug}">
|
||||
<img src="{users.picture}" class="img-thumbnail"/>
|
||||
</a>
|
||||
@@ -29,21 +29,31 @@
|
||||
<a href="/user/{users.userslug}">{users.username}</a>
|
||||
<br/>
|
||||
<div title="reputation" class="reputation">
|
||||
<span class='formatted-number'>{users.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
<span class='formatted-number'>{users.reputation}</span>
|
||||
</div>
|
||||
<div title="post count" class="post-count">
|
||||
<span class='formatted-number'>{users.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
<span class='formatted-number'>{users.postcount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- END users -->
|
||||
<li class="users-box {show_anon} anon-user">
|
||||
<a href="#">
|
||||
<img src="" class="img-thumbnail"/>
|
||||
</a>
|
||||
<br/>
|
||||
<div class="user-info">
|
||||
<span id="online_anon_count">{anonymousUserCount}</span>
|
||||
<br/>
|
||||
<a href="#">Anonymous</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="text-center {loadmore_display}">
|
||||
<button id="load-more-users-btn" class="btn btn-primary">[[users:load_more]]</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/users.js"></script>
|
||||
@@ -1 +0,0 @@
|
||||
@import "../vanilla/account";
|
||||
@@ -1 +0,0 @@
|
||||
@import "../vanilla/admin";
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
@import "../vanilla/category";
|
||||
@@ -1,18 +0,0 @@
|
||||
@import "../vanilla/mixins";
|
||||
@import "animations";
|
||||
|
||||
@import "style";
|
||||
@import "topic";
|
||||
@import "category";
|
||||
@import "noscript";
|
||||
@import "home";
|
||||
@import "header";
|
||||
@import "account";
|
||||
@import "search";
|
||||
@import "unread";
|
||||
@import "admin";
|
||||
@import "users";
|
||||
@import "outgoing";
|
||||
@import "footer";
|
||||
|
||||
@import "../vanilla/modules.less";
|
||||
@@ -1,9 +0,0 @@
|
||||
@import "../vanilla/footer";
|
||||
|
||||
.footer {
|
||||
color: #555;
|
||||
|
||||
a {
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
@import "../vanilla/header";
|
||||
|
||||
.header {
|
||||
//glowing animation for active state
|
||||
.dropdown-toggle {
|
||||
i {
|
||||
@-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
@import "../vanilla/home";
|
||||
|
||||
|
||||
.home {
|
||||
h4 {
|
||||
color: #555;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
@import "../vanilla/noscript";
|
||||
@@ -1 +0,0 @@
|
||||
@import "../vanilla/outgoing";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user