Compare commits

...

369 Commits

Author SHA1 Message Date
Julian Lam
27fce2363d ... and again 2013-11-13 23:09:22 -05:00
Julian Lam
01340c87bd handling missing notifications in update script 2013-11-13 23:08:44 -05:00
Baris Soner Usakli
77c2f551d3 upgrade script fix 2013-11-13 20:03:44 -05:00
Julian Lam
e6bb66705d 0.1.0 2013-11-13 15:15:00 -05:00
Baris Soner Usakli
88154c3ebf closes #497 2013-11-13 13:51:40 -05:00
Julian Lam
1c80a1bad5 ninjafix 2013-11-13 13:31:36 -05:00
Julian Lam
79c52dfe84 updated schema handling so that nodebb won't run unless you are up to date -- fixed #498 2013-11-13 12:46:55 -05:00
Baris Usakli
411ba3542c closes #493 2013-11-12 12:51:57 -05:00
Baris Usakli
fa9636a62a closes #495 2013-11-12 12:41:16 -05:00
Baris Usakli
69fefc0625 closes #496 2013-11-12 12:03:02 -05:00
Julian Lam
e598ffa993 Merge pull request #494 from RefinedSoftwareLLC/patch-2
Update README.md
2013-11-11 11:36:20 -08:00
RefinedSoftwareLLC
d99577ffb2 Update README.md
Added Wiki link because it is so easy to miss yet it is so important because of it's setup guides.
2013-11-11 12:29:18 -07:00
Baris Usakli
40108f92c9 closes #492 2013-11-11 14:28:08 -05:00
Julian Lam
a42b30fd40 removing commented out route 2013-11-11 14:15:27 -05:00
Julian Lam
37497fc5a0 fixed #393 - refactored basic route handling, moved some other routes to debug routes 2013-11-11 14:06:26 -05:00
Julian Lam
9bea23bbfe linting webserver.js 2013-11-11 13:25:54 -05:00
Julian Lam
4e39c50144 fine, 150ms. 2013-11-11 13:16:24 -05:00
Baris Usakli
7e50bcba0c Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-11 12:45:11 -05:00
Baris Usakli
f81c583d86 changed postdelay default 2013-11-11 12:44:42 -05:00
Julian Lam
5647d55147 updated postDelay upgrade script to update if the old value is over 1000, not 10! 2013-11-11 12:41:33 -05:00
Baris Usakli
4ce6ac5af9 closes #491, added check to prevent crash in getAnonUserCount 2013-11-11 12:40:15 -05:00
Baris Usakli
d770963b69 closes #490 2013-11-11 12:18:20 -05:00
Julian Lam
625b96ba73 Merge remote-tracking branch 'origin/master' 2013-11-09 17:20:02 -05:00
Julian Lam
37877ed531 removed cheerio as a dependency 2013-11-09 17:19:53 -05:00
Julian Lam
1f2ef2a7e4 updated cerulean minver 2013-11-07 15:59:07 -05:00
Julian Lam
885eec79c3 ninjafix for bad conditional 2013-11-07 15:54:19 -05:00
Julian Lam
aa1994be67 updated MOTD to not be inside a jumbotron, updated default styling of MOTD 2013-11-07 15:52:35 -05:00
Baris Usakli
4552e6286e dont allow empty userslugs to register 2013-11-07 12:17:23 -05:00
Julian Lam
d7856bcd4f fixing bootswatch themes, which broke in the last update 2013-11-06 21:37:48 -05:00
psychobunny
7ea852fae3 Revert "mobile: collapse menu after clicking on menu item"
This reverts commit c67c37bb20.
2013-11-06 16:21:01 -05:00
psychobunny
c67c37bb20 mobile: collapse menu after clicking on menu item 2013-11-06 15:24:03 -05:00
Baris Usakli
7d1aa02fd1 removed schema.js 2013-11-06 13:56:32 -05:00
Julian Lam
2309ab2002 hopefully closed #485 2013-11-06 12:07:35 -05:00
Julian Lam
b87840d4c9 fixing issue where the default for "use_port" ended up being no. Now yes. 2013-11-06 11:36:00 -05:00
psychobunny
b5c22c7ff7 removed unfollow button + logic from following view 2013-11-05 15:10:38 -05:00
Baris Usakli
3b3e8348e4 closes #430 2013-11-05 14:50:50 -05:00
Baris Usakli
259ad42b31 numbers are strong 2013-11-05 14:40:46 -05:00
Baris Soner Usakli
51cb33bccc Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-05 01:00:57 -05:00
Baris Soner Usakli
2a8a62a253 remove console.log 2013-11-05 01:00:55 -05:00
psychobunny
82ae80090e console.log 2013-11-05 00:27:38 -05:00
psychobunny
1d6135150f multiple plugins pointing to admin can conflict - fix 2013-11-04 23:59:41 -05:00
Baris Soner Usakli
41f98d29b7 added row to topic container 2013-11-04 23:24:43 -05:00
Baris Usakli
7b5a6bd3c9 fixed category infinite loading 2013-11-04 17:17:04 -05:00
Baris Usakli
fe15366524 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-04 17:02:27 -05:00
Baris Usakli
1cbbb3873f removed rounded images 2013-11-04 17:02:19 -05:00
Barış Soner Uşaklı
5e1ab7989a Merge pull request #479 from deniswolf/users-cleanup
User.js cleanup: spec, jshint satisfaction
2013-11-04 13:57:26 -08:00
Baris Usakli
0e5724cd2c template changes yet again 2013-11-04 16:52:58 -05:00
Denis Wolf
89e9d56dee user.js: Else in the sky with diamonds - fixing syntactic style. 2013-11-04 23:30:30 +02:00
Baris Usakli
58f9c2c18d removed console.log 2013-11-04 16:13:56 -05:00
Baris Usakli
1ec6726459 template changes to recent and unread 2013-11-04 16:00:34 -05:00
Baris Usakli
6b4520e526 recent unread template changes 2013-11-04 15:17:57 -05:00
Baris Usakli
98f20564de Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-04 15:01:11 -05:00
Baris Usakli
4da819b02b changesto category.tpl 2013-11-04 15:01:01 -05:00
Julian Lam
dc90db74c0 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-04 13:22:17 -05:00
Julian Lam
c5bc2dd64f upgrading to bootstrap 3.0.1, wish us luck! 2013-11-04 13:22:05 -05:00
Baris Usakli
bbb045698d closes #481 2013-11-04 12:37:06 -05:00
psychobunny
d9e364cd86 fixes #480 2013-11-04 12:14:36 -05:00
Denis Wolf
b179991be4 user.js: ladies and gentlemen, I'm going to satisfy jshint right before your eyes! 2013-11-04 01:57:58 +02:00
Denis Wolf
05de4870b0 user: add spec 2013-11-04 01:32:31 +02:00
Julian Lam
01f0131f5d upping markdown minver 2013-11-03 17:50:16 -05:00
Julian Lam
d69847c54e NodeBB will now fire hooks if a config setting changes 2013-11-03 17:49:03 -05:00
Julian Lam
cc78f6f155 moved firing of plugin activation to after a plugin reload 2013-11-03 17:29:14 -05:00
Julian Lam
1e2100902c closed #478 2013-11-03 17:25:07 -05:00
Julian Lam
4353a9da25 issue #478 - hot-swapping of plugins 2013-11-03 17:15:18 -05:00
Julian Lam
e480b1bace - removed reference to deprecated local modules folder in plugins.init 2013-11-03 12:39:24 -05:00
Julian Lam
5a96f5f64b removed filterBannedPosts method that seemed unused -- monkey-patching
install script to remember old values (if present, otherwise use defaults)
2013-11-03 11:53:44 -05:00
Baris Usakli
7296b701fa recent reply style change 2013-11-01 16:25:48 -04:00
Baris Usakli
a21d91d870 closes #476 2013-11-01 16:01:48 -04:00
Baris Usakli
6931695e64 when you type correctly 2013-11-01 15:07:41 -04:00
Baris Usakli
e12d02f29c jquery best 2013-11-01 15:04:54 -04:00
Baris Usakli
404865c32e closes #429 2013-11-01 14:55:55 -04:00
Baris Usakli
75879c47c5 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-01 14:08:48 -04:00
Baris Usakli
f946918176 fixed anon images in topic view, anon users arent added to active users for a category 2013-11-01 14:08:39 -04:00
Julian Lam
6ca3df2431 upping cerulean minver 2013-11-01 13:59:35 -04:00
Baris Usakli
c8ec095d99 anons cant edit after posting #365 2013-11-01 13:45:24 -04:00
Baris Usakli
0179a55ee4 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-01 13:04:25 -04:00
Baris Usakli
04ed1df0ef closes #365, allow guest posting. enabled from admin/settings, defaults to disabled 2013-11-01 13:04:15 -04:00
Julian Lam
de66ee1a89 updated error when no test db is defined -- also made an invalid "db" option a fatal error. Before, it was just defaulting to 0! Wiped out my test db hahaha 2013-11-01 12:16:36 -04:00
Julian Lam
e8c4bda984 Merge pull request #469 from deniswolf/redis-refactoring
Use separate db for testing
2013-11-01 08:44:56 -07:00
Julian Lam
7074b75b9d extra console.log 2013-11-01 11:36:35 -04:00
Julian Lam
857756f636 silly closures... 2013-11-01 11:36:05 -04:00
Julian Lam
e4c62200de fixing screenshot previews in admin/themes 2013-11-01 11:27:02 -04:00
Denis Wolf
89ec677d54 redismock: notify developer about test db config 2013-11-01 02:54:29 +02:00
Denis Wolf
8ff656430d tests: check if we are able to use test db wrapper at all 2013-11-01 02:54:28 +02:00
Denis Wolf
db22394976 redis.js - cleanup 2013-11-01 02:31:34 +02:00
Denis Wolf
ef5548a749 tests: categories.js is using testing db now 2013-11-01 02:31:34 +02:00
Denis Wolf
14c3bb7d63 redismock.js - wrapper for using test db instead of production 2013-11-01 02:31:34 +02:00
Baris Soner Usakli
1a415b60be fixed online page to match the changes made to the template 2013-10-31 16:20:21 -04:00
Julian Lam
85fa68bd92 upping minver for mentions plugin, fixing bad url in admin/topics 2013-10-31 15:54:50 -04:00
Julian Lam
e41ca491ff fixed use of divs inside a UL... how weird heh. 2013-10-31 15:02:52 -04:00
Julian Lam
bb5962cda0 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-31 15:02:40 -04:00
Julian Lam
2de878821b addressed #466, dynamicaly loaded topics now have proper delete/lock/pin
states
2013-10-31 15:02:12 -04:00
psychobunny
5bea2999ef cleanup 2013-10-31 14:38:51 -04:00
Baris Soner Usakli
67e9bf74a7 fixes unread page in mobile 2013-10-31 14:12:47 -04:00
Baris Soner Usakli
ed42012058 closes #472, need to wait for transitions to end before ajaxifying or the overlays stays there (drunk) 2013-10-31 13:30:34 -04:00
Baris Soner Usakli
9b7c9e4a81 camel case for user.js methods 2013-10-30 18:31:36 -04:00
psychobunny
85daacdf7a remove reference to deprecated mobileMenu 2013-10-30 16:38:58 -04:00
psychobunny
17ea41fdae category - use jQuery for graceful degradation 2013-10-30 16:23:36 -04:00
psychobunny
ffd2a18837 updated cerulean to 0.0.4 2013-10-30 15:32:20 -04:00
Julian Lam
5d7f38f99f Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-29 18:25:50 -04:00
Julian Lam
e762267e03 fixed screenshots for themes 2013-10-29 18:25:33 -04:00
psychobunny
a71870de28 pass in site description as well for potential themes to take advantage of 2013-10-29 17:27:23 -04:00
Baris Soner Usakli
7eba0b85f4 fixed andrews fail 2013-10-29 15:19:29 -04:00
psychobunny
30a45ee78e prevent a potentially badly written theme.json from blowing up your forum 2013-10-29 15:13:07 -04:00
psychobunny
ca9cd36067 fix for switching back to a theme without a custom template dir 2013-10-29 14:51:58 -04:00
psychobunny
1d5a208896 themes - added ability to route custom templates serverside 2013-10-29 14:40:35 -04:00
Baris Soner Usakli
60e2938b58 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-29 14:16:10 -04:00
Baris Soner Usakli
89540172b2 closes #464 2013-10-29 14:16:03 -04:00
psychobunny
bbb716723f templates - basic implementation of conditionals
<!-- IF variable -->
show me
<!-- ENDIF variable -->
2013-10-29 14:04:07 -04:00
psychobunny
842cd17979 themes - added ability to route custom templates 2013-10-29 12:34:41 -04:00
Barış Soner Uşaklı
e0e32efd26 Merge pull request #465 from deniswolf/utils-cleanup
Utils.js cleanup
2013-10-28 15:44:38 -07:00
Denis Wolf
a127fcd056 utils.js - detect node/browser with IF, not silent TRY/CATCH 2013-10-28 23:55:45 +02:00
Denis Wolf
c614af2cd9 utils.js - removed unused *trim shims - it's better to bring them when and if needed 2013-10-28 23:41:11 +02:00
Denis Wolf
c2abff6e6d utils.js - strict mode, missing semicolon, braces etc 2013-10-28 23:33:37 +02:00
Baris Usakli
4444d2ee6a removed whitespace 2013-10-28 16:16:44 -04:00
Julian Lam
463bc1374c Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-28 15:52:43 -04:00
Julian Lam
d9a60fc2ef rejigging format of notif page 2013-10-28 15:52:39 -04:00
Baris Usakli
22a3b227a3 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-28 15:49:21 -04:00
Baris Usakli
12f3f1a45c show admin link in header if user is admin, closes #459 2013-10-28 15:49:12 -04:00
Julian Lam
84d4c2944c Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-28 15:26:10 -04:00
Julian Lam
0a9b918c75 fixed notifications not exiting (whoops) 2013-10-28 15:25:35 -04:00
Julian Lam
8ddf200ce7 Merge branch 'cli-help' of https://github.com/RefinedSoftwareLLC/NodeBB into RefinedSoftwareLLC-cli-help
Conflicts:
	app.js
2013-10-28 15:24:21 -04:00
Julian Lam
207ff98211 Merge pull request #399 from JetMaddox/master
Disable Framing on all sites.

I'm merging this ***conditionally*** - at some point, we wish to introduce integration with CMSes like WordPress. An easy way to achieve this would be placing NodeBB in an iframe.

At that point in time, we'll want to make this a configurable option via admin panel.
2013-10-28 12:18:28 -07:00
Julian Lam
1fb09a9c8c merging @deniswolf's new tests, and double-checking that generateUUID still works 2013-10-28 15:15:59 -04:00
Julian Lam
67f9b22c86 Merge branch 'master' of https://github.com/deniswolf/NodeBB 2013-10-28 15:14:17 -04:00
Julian Lam
8b5cf0c696 Merge branch 'notif_pruning' 2013-10-28 15:10:21 -04:00
Julian Lam
26d9cc56d3 added cronjob for notifications 2013-10-28 15:10:10 -04:00
Julian Lam
930a9c8bca completed notifications pruning method 2013-10-28 14:53:41 -04:00
Baris Usakli
51a9bd9e56 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-28 14:36:40 -04:00
Baris Usakli
ac12bd0b8f closes #462 2013-10-28 14:36:31 -04:00
Julian Lam
d7651d1504 Merge branch 'master' into notif_pruning 2013-10-28 13:46:07 -04:00
Julian Lam
7e1f996079 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-28 13:45:55 -04:00
Julian Lam
1af4a9abb4 upped mentions plugin minver 2013-10-28 13:45:51 -04:00
Julian Lam
ed7c9348b7 interim commit 2013-10-28 13:45:32 -04:00
Baris Usakli
ccf4ed1235 topics and posts counters will update for everyone 2013-10-28 13:34:36 -04:00
Julian Lam
b22ee67612 Merge branch 'master' into notif_pruning 2013-10-28 10:52:30 -04:00
Baris Usakli
b5a953b16c fixes topics going into wrong category 2013-10-28 10:36:39 -04:00
Baris Usakli
7613f02eff closes #457 2013-10-28 10:27:03 -04:00
Baris Soner Usakli
78a3dd68ea hide the no topics warning when a new topic is posted on /unread 2013-10-28 00:27:18 -04:00
Baris Soner Usakli
afc0e25b26 closes #463 2013-10-28 00:08:44 -04:00
Baris Soner Usakli
82e14eef35 closes #458 2013-10-27 23:29:53 -04:00
Denis Wolf
81e9c9807f utils.js - missing semicolon, strict equality 2013-10-28 02:10:44 +02:00
Denis Wolf
02e2b53a1d TESTS: Utils.js: tests for validations, uuid generation 2013-10-28 02:09:57 +02:00
Denis Wolf
babe9b6f54 valid jshint config with globals for tests only 2013-10-28 02:09:09 +02:00
Baris Soner Usakli
52f198481b closes #456, if the file path doesnt exists logger was crashing 2013-10-26 20:19:42 -04:00
Baris Soner Usakli
252187f1fe Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-26 19:00:42 -04:00
Baris Soner Usakli
99812c33e7 closes #454 2013-10-26 19:00:34 -04:00
Julian Lam
97c5f6009d closes #455 2013-10-26 18:26:30 -04:00
RefinedSoftwareLLC
5e15f8683e Update app.js
Only check if file exists when no other supported --arguments are given.
Display that 0.0.0.0 is Any Address.
Moved upgrade section to after install section.
added CLI --help!
2013-10-26 10:56:05 -06:00
Baris Soner Usakli
fba1f7ae05 removed console.log 2013-10-25 18:09:07 -04:00
Baris Soner Usakli
865e5ae3a3 closes #448 2013-10-25 18:08:23 -04:00
Baris Soner Usakli
1af98835b1 added share buttons to main post 2013-10-25 18:00:08 -04:00
Baris Soner Usakli
6eadf67add another privilege fix 2013-10-25 16:56:18 -04:00
Baris Usakli
f3f280d008 another fix to priv 2013-10-25 16:02:18 -04:00
Julian Lam
aecbe6d316 interim commit, still nothing done 2013-10-25 16:01:31 -04:00
Baris Usakli
63419d7ca9 fix for reputation checks for manage content and manage threads 2013-10-25 15:35:48 -04:00
Julian Lam
3480a1d60e Merge branch 'master' into notif_pruning 2013-10-25 14:51:38 -04:00
Barış Soner Uşaklı
190712e250 Merge pull request #420 from twinlabs/relax-username-validation
relax username validation (#413)
2013-10-25 11:25:56 -07:00
Baris Usakli
a662330b1b fixed users page lists 2013-10-25 12:08:50 -04:00
Baris Usakli
04ee1d137d fixed unread and recent new post/topic notifications 2013-10-25 11:05:06 -04:00
Baris Usakli
27f421587e closes #444 2013-10-25 09:57:02 -04:00
Baris Soner Usakli
f9f0bd8685 closes #445 2013-10-24 23:32:25 -04:00
Julian Lam
53b12f50a7 updating git url for vanilla theme 2013-10-24 08:35:32 -04:00
Julian Lam
63d49463da removed deprecated /plugins folder, updated vanilla package info to always
load the latest latest version - closes #438
2013-10-24 08:10:58 -04:00
Baris Usakli
549017d035 cleaned up getCategories 2013-10-23 16:27:33 -04:00
Baris Usakli
45feef5884 closes #421 2013-10-23 15:45:00 -04:00
Baris Usakli
24592cc696 fixed the anon-box in users/online to show inline 2013-10-23 15:24:43 -04:00
Baris Usakli
c640c550fd removed old browsing code from websockets.js 2013-10-23 15:12:19 -04:00
Baris Usakli
0565b7b8c8 moved css out 2013-10-23 15:00:06 -04:00
Baris Usakli
1691c74727 testing breadcrumb images 2013-10-23 14:39:46 -04:00
Julian Lam
f6be3eacfc interim commit 2013-10-23 12:37:29 -04:00
Baris Usakli
790df903ac added view count to topics 2013-10-23 12:26:24 -04:00
Baris Usakli
25e6f72921 closes #434 2013-10-23 12:16:48 -04:00
Baris Soner Usakli
ab1015b11e #433 2013-10-22 21:47:09 -04:00
Julian Lam
55e990f71d fixes #433 - looks like someone removed the code that floated pinned
topics to the top...
2013-10-22 21:20:56 -04:00
Julian Lam
40a8150519 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-22 21:15:56 -04:00
Baris Soner Usakli
87744302ba fixed button tags in topic.tpl 2013-10-22 19:45:06 -04:00
Baris Soner Usakli
17083dc5e6 closes #417 2013-10-22 19:41:34 -04:00
Julian Lam
b29616fbd8 adding link to notifications page in notifications dropdown 2013-10-22 18:10:51 -04:00
Baris Usakli
468688615f Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-22 15:54:10 -04:00
Baris Usakli
f4faee4283 moved image uploading to a require js module, added image upload to site logo 2013-10-22 15:54:02 -04:00
Julian Lam
62c85274a3 mark all notifs as read functionality. CSS tweaks... 2013-10-22 15:06:07 -04:00
Julian Lam
db2917193e Merge branch 'master' into notifications_page 2013-10-22 14:35:38 -04:00
Julian Lam
705571de8c notifications page + ajaxify route + css styling 2013-10-22 14:35:20 -04:00
psychobunny
2ee29683a7 random whitespace 2013-10-22 14:22:53 -04:00
psychobunny
51395dda91 updated all RDB.hget in posts to use getPostField. new post filters for retrieving and saving posts. made editPost saving synchronous. 2013-10-22 14:22:21 -04:00
Baris Usakli
9babef0095 closes #422, will restart over if reconnection fails after x number of attempts 2013-10-22 13:42:23 -04:00
Baris Usakli
e9545c9a7f closes #426 2013-10-22 13:07:41 -04:00
Baris Usakli
619214e462 added relative path to logo 2013-10-22 12:54:20 -04:00
Baris Usakli
31b600686a logo links to homepage 2013-10-22 12:51:06 -04:00
Baris Usakli
bccc4e8019 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-22 12:39:24 -04:00
Baris Usakli
7039a4d762 closes #425 2013-10-22 12:39:14 -04:00
Julian Lam
7b7f0115e5 closed #432 - added link rel="up" and rel="alternate" to category and
topic pages
2013-10-22 12:23:07 -04:00
Baris Soner Usakli
24e79b3f4e #426 2013-10-21 18:51:33 -04:00
Julian Lam
5c70b2b307 issue #427 - proper handling of theme reverting 2013-10-21 12:10:23 -04:00
Julian Lam
65a8de7845 special theme.set socket call + static dir support, closes #427, tweaked languages 404 to work with subdirs 2013-10-21 12:07:37 -04:00
Julian Lam
44d2297546 monkeypatching the 404 route to handle missing languages and client side scripts. Removing "soft 404" in favour of hard 404 due to complaints from google webmaster tools 2013-10-21 11:09:46 -04:00
psychobunny
a41280707e updated the vanilla & cerulean theme's package versions 2013-10-20 13:59:10 -04:00
Jet
a8f2fd66ae Adding brackets.
Too much Coffeescript. Had some filters on. :)
2013-10-20 13:27:25 +02:00
Baris Soner Usakli
7e8ddbadfb closes #423, closes #424 2013-10-19 23:30:10 -04:00
Julian Lam
80aeb3677d added new packages to install script, defaulting to cerulean 2013-10-19 17:50:40 -04:00
Julian Lam
f92bbdaefa Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-19 17:48:00 -04:00
Julian Lam
fc73f16425 closes #418 2013-10-19 17:45:12 -04:00
Julian Lam
c1f47f536d install script tweaks so vanilla is default 2013-10-19 17:41:26 -04:00
Julian Lam
9166c82dc2 tweaking admin panel integration 2013-10-19 17:25:17 -04:00
Julian Lam
3decc8b9b1 removing nodebb.less file 2013-10-19 17:14:54 -04:00
Julian Lam
bac5da30e7 removing themes folders from core 2013-10-19 17:10:29 -04:00
Julian Lam
baf379c6d7 theme intergration into nodebb based on config hash value 2013-10-19 16:24:33 -04:00
Baris Soner Usakli
e9b6cdb37a add responsive class to imgs 2013-10-19 15:58:54 -04:00
Baris Soner Usakli
9d36d2c749 fixed placeholder text in chat modal 2013-10-19 15:31:17 -04:00
Noah Chase
5945ab1a0a relax username validation (#413)
this commit allows for matching accented characters, dots, '@' symbol,
and other important things.
2013-10-19 15:27:20 -04:00
psychobunny
40319a66ff reverting 754aef8a84 2013-10-19 11:48:46 -04:00
psychobunny
754aef8a84 fixes #392 - needs testing on try to see if google updates title correctly
order of events: 1. title changes, 2. content fades in, 3. history
pushState
2013-10-19 11:31:01 -04:00
Baris Soner Usakli
0613b530e8 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-18 16:02:43 -04:00
Baris Soner Usakli
5e9819b96f #415, moved search form into logged in menu 2013-10-18 16:02:33 -04:00
Julian Lam
b46e334a40 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-18 15:58:39 -04:00
Julian Lam
af49845ae6 upping mentions minver 2013-10-18 15:58:28 -04:00
Baris Soner Usakli
4cb8241334 added mising template variables to fix image change modal 2013-10-18 15:48:15 -04:00
Julian Lam
530e6cb20e upping mentions plugin minver 2013-10-18 14:03:15 -04:00
Julian Lam
e0adc03588 upping markdown ver again 2013-10-18 13:19:13 -04:00
Julian Lam
e48f6e6d9b Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-18 13:10:43 -04:00
Julian Lam
04ff4df5d7 updated markdown plugin min ver 2013-10-18 13:10:34 -04:00
Baris Usakli
d789e96d79 fixed 2 missing requires in api.js 2013-10-17 14:49:31 -04:00
Baris Usakli
45761fd48b fixed missing require in messaging 2013-10-17 12:05:53 -04:00
Julian Lam
cb6a47a5d9 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-16 14:08:57 -04:00
Julian Lam
67dbdfd80d fixed test cases to not throw redis error due to Reds 2013-10-16 14:08:31 -04:00
Baris Usakli
dee99c1752 update category topic count if topic is deleted/restored 2013-10-16 13:42:07 -04:00
Baris Usakli
a0c7e187f5 footer stats will display human readable stuff 2013-10-16 13:25:17 -04:00
Julian Lam
938503bd56 Merge branch 'category-test-uses-base-config' of https://github.com/twinlabs/forum.node into nchase-merge 2013-10-16 13:10:36 -04:00
Baris Usakli
33bda6fd16 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-16 13:04:37 -04:00
Baris Usakli
929336cb57 closes #408 2013-10-16 13:04:28 -04:00
Julian Lam
883aca038b disabled categories are now faded in the "Move Thread" modal. closes #409
Why is it called "move topic"? Renamed.
2013-10-16 12:57:12 -04:00
Julian Lam
77e03dc18d readded missing styles for the "Move Thread" modal 2013-10-16 12:34:04 -04:00
Baris Usakli
230ed1ab11 moved cid so that other functions can use it in category.js 2013-10-16 12:03:44 -04:00
Baris Usakli
b31fa856d0 added meta require to topics.js 2013-10-15 15:39:45 -04:00
Baris Usakli
98b97b9898 added callback to hmset in category.create 2013-10-15 15:20:12 -04:00
Baris Usakli
41cf7c6814 added commas after some requires, added missing plugins require to categories 2013-10-15 15:01:12 -04:00
Baris Usakli
da3a2f436c added categories require to feed.js 2013-10-15 14:44:42 -04:00
Julian Lam
569a7178d7 fixed install script error 2013-10-15 14:31:27 -04:00
Julian Lam
7e7497c3bd Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-15 13:12:51 -04:00
Julian Lam
905c78d5a3 reverting changes to the badge 2013-10-15 13:12:43 -04:00
Baris Usakli
37b1cb009a added chevron to fave template 2013-10-15 12:01:27 -04:00
Julian Lam
56586e1fda tweaking more badges fro /home, itemprops for homem reduced padding right of badge in category view 2013-10-15 11:17:23 -04:00
Julian Lam
2040fcdba4 moved badge outside of topic title, added schema.org itemprop stuff for category listing 2013-10-15 11:11:21 -04:00
Julian Lam
d24b57ae86 upping markdown package to 0.1.5 2013-10-14 23:19:09 -04:00
Julian Lam
db72102de7 upping markdown version 2013-10-14 22:02:10 -04:00
Julian Lam
e8801a75f3 fixing weird passing in of a single param as an array for post.save hook 2013-10-14 21:47:45 -04:00
Julian Lam
918826ff48 fixed action calls, and added a new hook for plugin activation and deactivation 2013-10-14 21:11:17 -04:00
Julian Lam
fa2fe5c941 allowing plugin system to detect object method path by dot notation 2013-10-14 17:24:57 -04:00
Julian Lam
8ef2761f53 tweaking plugins again -- simplifying 2013-10-14 17:07:54 -04:00
Julian Lam
9375369b88 fixed issues caused by inadvertant globalisation (and its subsequent fix) 2013-10-14 16:41:34 -04:00
Julian Lam
ce77c82b0c Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-14 16:28:35 -04:00
Julian Lam
1e39ae2f1e minor tweak to plugins (used object format instead of potentially
confusing array)
2013-10-14 16:28:08 -04:00
Julian Lam
c143894547 Merge pull request #405 from twinlabs/stop-topic-global-leak
fix typo that was causing Topic module's variables to leak into global namespace
2013-10-14 11:45:53 -07:00
psychobunny
37450ff00c optimized ajaxify to only recurse through script tags; updated so it looks deeper for embedded scripts (ie. within plugin subtemplates) 2013-10-14 14:26:20 -04:00
psychobunny
ca9c468edd plugins - filter:category.build_sidebars allows plugins to generate custom sidebar content 2013-10-14 14:25:36 -04:00
psychobunny
d6570d1496 prevent admin crashes on improperly configured plugins 2013-10-14 12:00:31 -04:00
psychobunny
bf677522a9 added additional_profile_info footer in posts view; plugins - filter:posts.custom_profile_info hook lets you add info to post block footer
also fixed app.alert - if title is not set then do not show title.
2013-10-13 19:16:48 -04:00
Noah Chase
fd89f71fc0 fix typo that was causing topic variables to leak into global namespace 2013-10-13 18:39:42 -04:00
Noah Chase
83477ece18 category tests should be able to use whatever the app defines as config options 2013-10-13 15:18:33 -04:00
psychobunny
32990794ce fixed admin bug (on f5 was not populating fields); plugins - filter:admin.create_routes allows you to create path to custom admin page 2013-10-13 14:30:39 -04:00
psychobunny
4b5bae4f9b fixed plugin path in admin header 2013-10-13 13:52:33 -04:00
psychobunny
2b07917020 plugins - filter:admin.header.build allows you to add plugins to navigation in ACP 2013-10-13 13:34:15 -04:00
psychobunny
338acb8fc2 added a way to disable ajaxify on links manually if needed. 2013-10-12 17:43:29 -04:00
psychobunny
2a4b228e19 plugins - filter:server.create_routes allows you to add custom routes to NodeBB 2013-10-12 17:19:18 -04:00
Julian Lam
c6c3ab94b1 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-12 17:00:48 -04:00
Julian Lam
8671516b95 added more itemprop stuff... replies are now comments. 2013-10-12 17:00:30 -04:00
Baris Soner Usakli
5710ab47ae Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-12 14:45:23 -04:00
Baris Soner Usakli
e3b0eb29f1 added favourites link to template 2013-10-12 14:45:15 -04:00
Julian Lam
795594b6a6 added some microdata for better google scrapage. closes issue #401 2013-10-12 14:23:58 -04:00
Julian Lam
e91da53d9e using reduce to run filter hooks instead of eachSeries, and fixed up
incorrect usage of .apply() in tests folder
2013-10-12 13:50:08 -04:00
Julian Lam
f807df84d8 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-12 13:31:07 -04:00
Julian Lam
7f32d5741d removing restriction that caused hook system to go weird when arrays were
passed in as args
2013-10-12 13:30:43 -04:00
psychobunny
30c7113bd8 removed console.log 2013-10-12 13:08:49 -04:00
psychobunny
a63732027f plugins - filter:header.build allows plugins to add custom navigation to header 2013-10-12 13:05:47 -04:00
Julian Lam
13d8f51f6a added test file for categories (incomplete)
re: issue #391
2013-10-12 11:06:53 -04:00
Julian Lam
5d48ed5fb4 using map instead of eachSeries to load categories for /home. Error
handling.
2013-10-12 10:37:44 -04:00
Baris Soner Usakli
4b89b3e2ed fixed time for month 2013-10-11 23:32:47 -04:00
Julian Lam
1ee24517e3 enhanced the "discard post" confirmation message to only trigger if
something in the post body actually changed
2013-10-11 20:55:31 -04:00
Julian Lam
64e35c734e decreased the severity of the border radii on the post window, and removed
them completely from the bottom of the window, as it conflicts with the
taskbar
2013-10-11 20:50:30 -04:00
Baris Soner Usakli
08130e8088 fixed conditional in follower/following count 2013-10-11 17:18:17 -04:00
Baris Usakli
0c5937805b clear uploads in progress if discard is clicked, temp fix until composer is revamped 2013-10-11 15:16:00 -04:00
Baris Usakli
d315829eaf closes #394 2013-10-11 15:06:56 -04:00
Baris Usakli
6dad1c3bbb Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-11 14:58:04 -04:00
Baris Usakli
3577c11c89 fixed infinite loading on /recent 2013-10-11 14:57:55 -04:00
psychobunny
8326c223ab fixed f5 bug with /recent/:term which previously gave a 404 2013-10-11 14:57:17 -04:00
Baris Usakli
73de5f78fe language file 2013-10-11 14:50:09 -04:00
Baris Usakli
4e59b85073 recent page, for hour, day, week, month 2013-10-11 14:48:14 -04:00
Baris Usakli
9af26db57a follower count fix 2013-10-11 14:29:34 -04:00
Baris Usakli
8e4ca8e474 closes #397 2013-10-11 13:06:21 -04:00
Baris Usakli
79de3976bf removed non existant paths from templates/config.json, added starts with to regexes 2013-10-11 12:02:43 -04:00
Baris Usakli
4b80f13373 removed reduntant paths fron templates/config.json 2013-10-11 11:59:17 -04:00
Baris Usakli
6210c6dbf4 removed extra ) in search.js 2013-10-11 11:54:15 -04:00
Jet
97592eede6 Fixed case for header 2013-10-11 11:19:24 +02:00
Jet
73dafa6aff Disable framing
Set the X-Frame-Options to DENY for added security.
2013-10-11 11:08:52 +02:00
Baris Usakli
6c3e121b6c remove a line, not needed 2013-10-09 17:38:00 -04:00
Baris Usakli
95ee7fb49f added flag to empty container 2013-10-09 17:30:49 -04:00
Baris Usakli
b3f73eace1 fix for online page 2013-10-09 17:16:07 -04:00
Baris Soner Usakli
07a497362a removed console.log 2013-10-07 17:49:25 -04:00
Baris Soner Usakli
afa078d00c closes #288 2013-10-07 17:48:11 -04:00
psychobunny
3fd7d9a604 moved jquery.form.js into appropriate location 2013-10-07 16:56:20 -04:00
psychobunny
80a0d2d8d8 ^ this person is awesome, what a commit! 2013-10-07 16:01:03 -04:00
psychobunny
27f4fdd179 fixes #377 2013-10-07 13:00:57 -04:00
psychobunny
d1a94a91c2 added app.uid 2013-10-07 12:57:40 -04:00
psychobunny
69a31dcdd9 fixes #388 2013-10-07 12:49:13 -04:00
psychobunny
a2a6bf87f7 fixes #387 2013-10-07 12:38:57 -04:00
Julian Lam
bd1e95b655 cleaned up admin template mapping 2013-10-06 18:28:17 -04:00
Julian Lam
278f9bfc03 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-06 18:04:25 -04:00
Julian Lam
a3a8950afd bumping markdown plugin version to 0.1.3 2013-10-06 18:04:03 -04:00
Julian Lam
f95913e623 Merge pull request #386 from adarqui/issue-login-redirect-40x
Issue login redirect 40x
2013-10-06 11:12:19 -07:00
Julian Lam
8e4b51fba4 requiring latest version of mentions plugin 2013-10-06 14:10:54 -04:00
Julian Lam
84915a1843 added css files support for plugins, added ready output for NodeBB, put
some minification related outputs to debug-mode only
2013-10-06 13:55:30 -04:00
Andrew Darqui
4b0e915698 just changed the 403/404 checking logic to use regex. 2013-10-06 04:32:03 -04:00
Andrew Darqui
b2e81b5d17 Fixes a small issue with the redirect after logging in. If app.previousUrl is undefined (from accessing the page directly), login would fail to redirect you because of an error with indexOf on the undefined app.previousUrl. 2013-10-06 04:24:38 -04:00
Andrew Darqui
ef47f3fd15 if nodebb returns 403/404, don't overwrite previousUrl. If you don't do this, when logging in after a 403/404, it will redirect you to the 403/404 page. 2013-10-06 04:17:06 -04:00
Julian Lam
f88f72abd2 Merge branch 'adarqui' 2013-10-05 22:47:59 -04:00
Julian Lam
715c14b78d fixed error where client side script loader on admin page was throwing 404
errors on require.js require
2013-10-05 22:46:30 -04:00
Julian Lam
4af7da3451 Merge branch 'express-logger-0.0.7-fixes' of http://github.com/adarqui/NodeBB into adarqui 2013-10-05 22:36:41 -04:00
Julian Lam
63ff572076 WIP commit for CSS static directories 2013-10-05 22:33:29 -04:00
Andrew Darqui
129af904f6 fixed an issue with Logger.io_close, needed to check for undefined on the socket.oEmit and socket. functions 2013-10-05 22:06:44 -04:00
Andrew Darqui
8cc71d2b47 fixed logger.tpl to use the new requirejs code 2013-10-05 21:44:54 -04:00
Julian Lam
0b299b2fe7 Merge pull request #381 from adarqui/express-logger-0.0.7
Express & Socket.io logger
2013-10-05 14:39:14 -07:00
Julian Lam
b1cef5f73d Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-05 13:48:41 -04:00
Julian Lam
42067ce53c closes #382 - install script can be run via command line 2013-10-05 13:48:23 -04:00
Baris Usakli
2bdf12fb67 derp, need to revamp online page 2013-10-04 16:21:30 -04:00
Baris Usakli
c00b138bf2 some fixes to online page 2013-10-04 16:03:03 -04:00
Baris Usakli
1155eaf1f1 online page changes 2013-10-04 15:30:52 -04:00
Baris Usakli
52f2e193d6 if api fails with 404 ajaxify to 404, added type check to updateProfile 2013-10-04 13:39:44 -04:00
Baris Usakli
f5619a9b29 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-10-04 13:01:33 -04:00
Baris Usakli
057608bac0 added usernames to following followers, closes #369 2013-10-04 13:01:28 -04:00
Julian Lam
57465eb277 misrouting now only shows warning when in debug mode 2013-10-04 10:37:32 -04:00
Julian Lam
01e04d60a9 fixing redis db in upgrade path 2013-10-04 10:34:08 -04:00
Julian Lam
be8d9be832 flushed out upgrade path for notifications 2013-10-04 10:27:32 -04:00
Julian Lam
07d07020f0 requiring latest nodebb plugin versions 2013-10-04 10:06:17 -04:00
Andrew Darqui
7c1f7e7a23 just some text formatting fixes 2013-10-04 01:54:23 -04:00
Andrew Darqui
a3f6fee41f refactored the logger completely. 2013-10-04 01:46:50 -04:00
Julian Lam
0414ec7f83 removing testbed code from repo (why was it even checked in?!!) 2013-10-03 23:21:29 -04:00
Julian Lam
12af2a7ff6 install script calling proper redis db 2013-10-03 23:10:17 -04:00
Julian Lam
4d6881fa65 reset update for 0.0.7, and added new schema update for notifications 2013-10-03 22:36:00 -04:00
Julian Lam
019e8e0d14 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-10-03 21:20:00 -04:00
Julian Lam
763bd775c4 closed #380 2013-10-03 21:18:59 -04:00
Julian Lam
585e07bc79 closed #368 - notifications now no longer need scores 2013-10-03 20:35:16 -04:00
Julian Lam
b911d8d075 Merge pull request #379 from ipetepete/master
Added config option to set a db for NodeBB, defaults to redis db0
2013-10-03 16:48:44 -07:00
Julian Lam
d583122c64 Merge pull request #378 from iamcardinal/master
FIxes Search and touches on postbox, closes #370
2013-10-03 16:38:05 -07:00
Peter Peterson
3968877b1e Merge branch 'master' of github.com:ipetepete/NodeBB 2013-10-03 13:02:57 -07:00
Peter Peterson
46f03de9f6 Added ability to set redis db to use, defaults to db0 2013-10-03 13:00:30 -07:00
Julian Lam
0e18ec022c not running init() if there is no init method in each template script 2013-10-03 15:51:48 -04:00
Julian Lam
64117ab613 removing deprecated bootstrap css file from admin header 2013-10-03 15:07:30 -04:00
Julian Lam
038e04dee6 revamped client side scripts so that they are loaded using Require.js instead. 2013-10-03 15:04:25 -04:00
Julian Lam
b49c7b8609 added user-scalable=no to header meta tag (in lieu of fastclick lib)
closes #376 - reopen if necessary.
2013-10-03 11:47:40 -04:00
Julian Lam
948949c571 closed #375 - now asking socket.io to connect to "current page" instead of hardcoded url, removed api_url and "socket" section from public config 2013-10-03 11:34:15 -04:00
Julian Lam
f173a79a0d Merge pull request #371 from iamcardinal/master
Adds meta:keywords tags fixes tabbing, closes #330
2013-10-02 16:10:43 -07:00
Quinton Marchi
994791add6 Touches on postbox
Adds border-radius. Compatible with: Firefox, Webkit-based, and IE9+.
2013-10-02 17:48:27 -04:00
Quinton Marchi
d177e71b46 Fixes double search, closes #370
Haven't tested it but I can't see any problems.
2013-10-02 14:31:41 -04:00
Quinton Marchi
fcab1501f8 Merge remote-tracking branch 'origin/testing-2013-10-01' 2013-10-02 14:23:25 -04:00
Quinton Marchi
eb022220f4 Final Edit for keywords 2013-10-02 14:22:56 -04:00
Andrew Darqui
f48687528e fixed up logger.tpl a little. Added a basic 'collision detection' for the loggerPath and loggeIOPath filenames. 2013-10-02 01:28:37 -04:00
Andrew Darqui
9007f9de9e added socket.io logging which can be dynamically enabled/disabled via the /admin/logger panel 2013-10-02 01:15:45 -04:00
Andrew Darqui
55d84d0f9b basic logger functionality added - accessible via http://nodebb/admin/logger 2013-10-02 00:25:46 -04:00
Minami
90b4d688f8 Testing adding of Meta Tags 2013-10-01 22:14:16 -05:00
Andrew Darqui
aacd42f4bc ignore vim .swp files from git 2013-10-01 22:38:33 -04:00
Andrew Darqui
51d7dda5a7 added basic logger using config.json 2013-10-01 22:38:13 -04:00
Baris Soner Usakli
1c32acf7b6 removed WITHSCORES from getLatestTopics, how was this working at all? 2013-10-01 14:17:24 -04:00
psychobunny
22c73f3c12 closes #345 2013-10-01 12:08:05 -04:00
Julian Lam
9613ea9018 reverted change where post title was sanitized on saving (which didn't
seem to work), now sanitizing post title on output
2013-10-01 11:54:00 -04:00
Julian Lam
3d4802ac68 Merge remote-tracking branch 'origin' 2013-10-01 11:18:06 -04:00
Julian Lam
59c9bdb3a5 updated contributor list 2013-10-01 11:17:57 -04:00
Julian Lam
d7953eb779 updated screenshots for v0.0.7 2013-09-30 17:22:39 -04:00
Quinton Marchi
f5c4f98834 Merge pull request #1 from designcreateplay/master
Merge changes into repo for edit
2013-09-30 11:42:16 -07:00
182 changed files with 7438 additions and 14025 deletions

13
.gitignore vendored
View File

@@ -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

View File

@@ -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
![NodeBB Main Category Listing](http://i.imgur.com/ZBrHqLr.png)
![NodeBB Main Category Listing](http://i.imgur.com/zffCFoh.png)
![NodeBB Topic Page](http://i.imgur.com/YSBA6Vr.png)
![NodeBB Topic Page](http://i.imgur.com/tcHW08M.png)
## 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
View File

@@ -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)');
};
}());

View File

@@ -1 +1 @@
*.rss
*.rss

View File

@@ -1 +1 @@
*.rss
*.rss

View File

@@ -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
View 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));

View File

@@ -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
View File

View File

@@ -1 +0,0 @@
@import "../themes/cerulean/cerulean.less";

View File

@@ -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",

View File

@@ -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",

View File

@@ -0,0 +1,3 @@
{
"title": "Notifications"
}

View File

@@ -0,0 +1,5 @@
{
"day": "Day",
"week": "Week",
"month": "Month"
}

View File

@@ -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"

View File

@@ -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));

View File

@@ -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');
});
}

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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 || {}));
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View 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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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
View 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;
});

View File

@@ -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;
});

View 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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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();

View File

@@ -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);
}

View 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;
});

View File

@@ -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)

View File

@@ -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));
}
}

View File

@@ -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);

View File

@@ -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}" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -96,5 +96,3 @@
</div>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/groups.js"></script>

View File

@@ -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">

View File

@@ -18,5 +18,3 @@
</p>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/index.js"></script>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
});

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}" />

View File

@@ -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
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 &copy; 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -60,5 +60,3 @@
</div>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/login.js"></script>

View 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>

View File

@@ -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>

View File

@@ -80,5 +80,3 @@
</div>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/register.js"></script>

View File

@@ -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>

View File

@@ -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>

View File

@@ -53,5 +53,3 @@
</ul>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/search.js"></script>

View File

@@ -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}&nbsp;</span>
<span class="username-field" href="/user/{main_posts.userslug}" itemprop="author" itemscope itemtype="http://schema.org/Person">{main_posts.username}&nbsp;</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}&nbsp;</span>
<span class="username-field" href="/user/{posts.userslug}" itemprop="author">{posts.username}&nbsp;</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">&times;</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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -1 +0,0 @@
@import "../vanilla/account";

View File

@@ -1 +0,0 @@
@import "../vanilla/admin";

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
@import "../vanilla/category";

View File

@@ -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";

View File

@@ -1,9 +0,0 @@
@import "../vanilla/footer";
.footer {
color: #555;
a {
color: #222;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -1,13 +0,0 @@
@import "../vanilla/home";
.home {
h4 {
color: #555;
line-height: 21px;
}
.icon {
border-radius: 4px;
}
}

View File

@@ -1 +0,0 @@
@import "../vanilla/noscript";

View File

@@ -1 +0,0 @@
@import "../vanilla/outgoing";

Some files were not shown because too many files have changed in this diff Show More