Compare commits

...

582 Commits

Author SHA1 Message Date
Julian Lam
e9b9f090e4 0.2.2 2014-01-16 09:32:41 -05:00
Julian Lam
b82a6fa888 adding accidentally removed upgrade directives to v0.2.x branch 2014-01-16 09:30:18 -05:00
Baris Soner Usakli
8ab539b518 0.2.1 2014-01-04 00:50:23 -05:00
Baris Soner Usakli
9078c2a536 0.2.0 2014-01-04 00:49:10 -05:00
Baris Soner Usakli
fd20e4a400 5% more 2014-01-04 00:41:25 -05:00
Baris Soner Usakli
1a64e40b21 upgrade check 2014-01-04 00:38:43 -05:00
Julian Lam
cbfba4b45b 0.2.1 2014-01-04 00:04:37 -05:00
Baris Soner Usakli
f21a98f37f first pass, #524, #667 2014-01-03 21:33:41 -05:00
psychobunny
0edd6985ec templates.js added support for !@first and !@last conditionals 2014-01-03 21:23:08 -05:00
psychobunny
98a646fa62 changed numRecentTopics to numRecentReplies, that makes more sense now 2014-01-03 20:37:29 -05:00
psychobunny
1d69436b44 fix to getRecentReplies causing count = 0 to return unlimited 2014-01-03 20:36:00 -05:00
psychobunny
d6c7551120 pull # of recent replies based on settings 2014-01-03 20:29:09 -05:00
psychobunny
fb53e89023 upgrade.js - categories.numRecentTopics 2014-01-03 20:20:23 -05:00
psychobunny
d28beaa4dc admin - add "# recent topics to display" to categories 2014-01-03 20:17:55 -05:00
psychobunny
ac06567617 admin - add custom class to categories (forgot to commit this) 2014-01-03 20:14:26 -05:00
Baris Soner Usakli
d0a2c077ff refactored posts.create to use waterfall 2014-01-03 19:32:52 -05:00
psychobunny
ebf07626de use categories.class instead of hardcoded columns 2014-01-03 17:22:37 -05:00
psychobunny
9ef8bb4cdc woops, fail at iteration 2014-01-03 17:22:23 -05:00
psychobunny
bc8a53aadd upgrade.js - adding categories.class and categories.link 2014-01-03 17:09:19 -05:00
psychobunny
03c4a1bc73 remove console.log(file) - no more exploding terminals 2014-01-03 15:59:49 -05:00
Julian Lam
d93163896f Merge pull request #701 from aonz1982/install_automated_setup
Install automated setup
2014-01-02 18:47:38 -08:00
Julian Lam
08bdbc0bcc cleanup of PR - fixed some logic bugs and linted 2014-01-02 21:44:13 -05:00
Julian Lam
35d6a99775 Merge branch 'AdminButton' of github.com:genderup/NodeBB into genderup-AdminButton 2014-01-02 21:34:03 -05:00
Julian Lam
e45e86fcc1 fixed width icons in ACP. Also, who uses single quotes in HTML? ARGH 2014-01-02 18:13:46 -05:00
Baris Soner Usakli
9589d340b9 removed console.log 2014-01-02 16:00:24 -05:00
Baris Soner Usakli
91afbf106e removed console.log 2014-01-02 16:00:05 -05:00
Baris Soner Usakli
aff11359ae Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2014-01-02 15:59:34 -05:00
Baris Soner Usakli
aff8cef5f3 closes #717 2014-01-02 15:59:25 -05:00
Julian Lam
879855744d Merge branch 'master' of github.com:designcreateplay/NodeBB 2014-01-02 15:21:00 -05:00
Julian Lam
b5ab0c9097 updated minvers for markdown and mentions 2014-01-02 15:20:54 -05:00
psychobunny
2c398f81d2 fixed the twitter link to @NodeBB 2014-01-02 15:06:08 -05:00
Baris Soner Usakli
520fcadd3f closes #456 2014-01-02 14:53:27 -05:00
Baris Soner Usakli
e8eb9f91b9 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2014-01-02 14:26:30 -05:00
Baris Soner Usakli
8bfb338eee #712 2014-01-02 14:26:08 -05:00
Julian Lam
bbb655abdd Merge branch 'master' of github.com:designcreateplay/NodeBB 2014-01-02 02:17:55 -05:00
Julian Lam
095e5527e3 upping markdown minver 2014-01-02 02:17:47 -05:00
Barış Soner Uşaklı
4eb8c34855 Merge pull request #715 from cmastudios/age
Calculate age based on days instead of years
2014-01-01 18:35:47 -08:00
cmastudios
e3185b9560 Calculate age based on days instead of years
Calculating the age based on the year only caused issues in the display of the age because it was off. Example: Person who was born in march of 1999 is displayed as 15 instead of 14 after the turn of the year.
2014-01-01 19:46:11 -05:00
Julian Lam
449adfae59 Merge branch 'master' of github.com:designcreateplay/NodeBB 2014-01-01 15:31:32 -05:00
Julian Lam
714e61b137 upping markdown minver 2014-01-01 15:31:27 -05:00
Baris Soner Usakli
d0e4689907 removed extra event in websocket.js 2014-01-01 14:44:15 -05:00
Baris Soner Usakli
1996e64c9b if imgur client id is not set but local file uploads are enabled use that 2014-01-01 14:38:12 -05:00
Baris Soner Usakli
e2fb617cc0 thread delete restore fix 2014-01-01 14:15:53 -05:00
Baris Soner Usakli
fd88aff195 mongo string fix 2014-01-01 14:11:12 -05:00
Julian Lam
f7a1cca861 properly fixed #713 2013-12-31 20:53:24 -05:00
Julian Lam
e3fb996a80 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-31 20:28:37 -05:00
Julian Lam
fe53037e53 hopefully fixing #713 2013-12-31 20:28:31 -05:00
Baris Soner Usakli
438f90d859 expire functionality in dbal 2013-12-31 19:08:38 -05:00
Baris Soner Usakli
742c8fb43c closes #712 2013-12-31 17:14:27 -05:00
Baris Soner Usakli
d43f3cebc6 closes #707, closes #686 2013-12-31 17:01:51 -05:00
Julian Lam
9d452241ad Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-31 14:46:33 -05:00
Julian Lam
c19a51e1b6 tweaking slugify code to not remove numbers 2013-12-31 14:46:28 -05:00
Baris Soner Usakli
1b41a8f467 fixed typo 2013-12-31 14:32:16 -05:00
Baris Soner Usakli
f933fc0167 forgot topics.js oops 2013-12-31 14:27:56 -05:00
Baris Soner Usakli
8cfb239aac closes #705 2013-12-31 14:25:26 -05:00
Baris Soner Usakli
a974c6fa99 possible fix for mongo crash 2013-12-31 13:45:37 -05:00
Julian Lam
998f780fd2 fuck, I am dumb. 2013-12-31 13:11:32 -05:00
Julian Lam
b022d46d47 upgrade script for Topic titless and Usernames - closes #709 2013-12-31 13:04:29 -05:00
Julian Lam
f7793e54b1 adding events.log 2013-12-31 10:49:26 -05:00
Julian Lam
1e61033667 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-31 10:49:05 -05:00
Julian Lam
53caa5e422 full unicode support in slugify method, thanks to XRegExp 2013-12-31 10:48:59 -05:00
Baris Soner Usakli
40d20846d8 moar refactor 2013-12-31 03:36:42 -05:00
Baris Soner Usakli
204913c63d Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-31 03:14:29 -05:00
Baris Soner Usakli
6c30437c47 closes #706, refactor admin image uploads, fixed gif uploads 2013-12-31 03:14:22 -05:00
Barış Soner Uşaklı
dd7fe47dfd Merge pull request #704 from akhoury/master
guarding against Errors on Socket Disconnect
2013-12-30 16:55:21 -08:00
Aziz Khoury
50323c3d23 guarding against Errors on Socket Disconnect
Saw this 

```
TypeError: Cannot call method 'indexOf' of undefined
    at Socket.<anonymous> (/home/admin/NodeBB/src/websockets.js:108:33)
    at Socket.EventEmitter.emit [as $emit] (events.js:95:17)
    at Socket.onDisconnect (/home/admin/NodeBB/node_modules/socket.io/lib/socket.js:153:10)
    at SocketNamespace.handleDisconnect (/home/admin/NodeBB/node_modules/socket.io/lib/namespace.js:229:46)
```

and this

```
/home/admin/NodeBB/src/websockets.js:113
			if (userSockets[uid].length === 0) {
			                    ^
TypeError: Cannot read property 'length' of undefined
    at Socket.<anonymous> (/home/admin/NodeBB/src/websockets.js:113:24)
```

in my logs, when users are disconnecting
2013-12-30 19:40:57 -05:00
psychobunny
69e0aa338d resolved some todo that's been in there for many months 2013-12-30 17:11:59 -05:00
psychobunny
6dcd06b63c removed unused parameter in templates.prepare 2013-12-30 16:26:30 -05:00
psychobunny
42d77080f3 plugins: filter:server.create_routes - allow plugins to add custom templates and/or modify individual blocks withiin a template 2013-12-30 16:20:05 -05:00
Baris Soner Usakli
fbb4998999 closes #676 2013-12-30 16:09:07 -05:00
Julian Lam
14744a854f fixes #703 - topic feeds were not saving (who knows for how long!) 2013-12-30 12:39:13 -05:00
Pongsan Sayampol
2c6afb4244 Add a way to set social network logins 2013-12-30 11:46:33 +07:00
Pongsan Sayampol
24907e456d Fix the issue that the default values are saved to config.json 2013-12-30 11:45:10 +07:00
Baris Soner Usakli
2039885d96 removed console.logs 2013-12-29 23:35:04 -05:00
Baris Soner Usakli
daacdb50f3 closes #645 2013-12-29 23:33:28 -05:00
Baris Soner Usakli
21155b1b80 recent rss link fix 2013-12-29 21:35:03 -05:00
Barış Soner Uşaklı
5d69167a64 updated pictures 2013-12-29 20:55:46 -05:00
psychobunny
5db27a835f fix for minification of plugins static directories 2013-12-29 19:36:18 -05:00
Baris Soner Usakli
c8e423e9cf check if item is valid 2013-12-29 18:25:21 -05:00
Baris Soner Usakli
be3465c5ca display events in admin page events section 2013-12-29 18:10:42 -05:00
Baris Soner Usakli
637e037e27 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-29 16:03:32 -05:00
Baris Soner Usakli
3e6bcd83cc fixed typo in user reset 2013-12-29 16:03:25 -05:00
psychobunny
dc4aeca427 also send notification data along with event:new_notification socket call 2013-12-29 15:09:34 -05:00
Baris Soner Usakli
9eb09f14cb updated vanilla ver 2013-12-29 13:57:04 -05:00
Baris Soner Usakli
a55fc364a0 closes #697 2013-12-29 13:47:34 -05:00
Baris Soner Usakli
2f90949560 vanilla color fixes 2013-12-28 14:36:33 -05:00
Baris Soner Usakli
a940219321 remvoed the timeout on the login call 2013-12-28 13:11:49 -05:00
Michael Mitchell
aaf6b11dc9 removed extraneous 2013-12-28 08:34:59 -08:00
Michael Mitchell
0f0913bfe5 add admin button 2013-12-27 14:18:30 -08:00
Michael Mitchell
5569337c40 first 2013-12-27 14:09:27 -08:00
Baris Soner Usakli
ba2f47ead6 closes #690, closes #691 2013-12-27 14:09:22 -05:00
Julian Lam
b52782deb8 removed use of strip_tags and using String library instead 2013-12-26 21:10:26 -05:00
Julian Lam
2c6bf93eb5 added String parsing library, sanitizing meta tag "description" for
topics, and added og:description meta tag -- fixes #688
2013-12-26 20:37:45 -05:00
Julian Lam
4a11307b24 0.2.0 2013-12-26 16:04:06 -05:00
Julian Lam
d8183c056c added cache buster to require.js files - fixes #687 2013-12-26 15:58:46 -05:00
Julian Lam
6ad28dadd4 Merge pull request #685 from akhoury/master
can i haz header-icons pls, merry xmas
2013-12-26 12:21:35 -08:00
Baris Soner Usakli
2c489c600c closes #675 2013-12-24 13:12:25 -05:00
akhoury
abc782f5e6 can i haz header-icons pls, merry xmas 2013-12-24 13:00:04 -05:00
Baris Soner Usakli
a18e366493 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-23 22:07:21 -05:00
Baris Soner Usakli
12e95df068 removed console.log 2013-12-23 22:07:13 -05:00
Julian Lam
a078f73e84 omg I don't know what I am doing 2013-12-23 22:06:53 -05:00
Julian Lam
8f879cd3eb topics will no longer show up in /recent if user cannot access them 2013-12-23 21:50:47 -05:00
Julian Lam
8385ceef79 topics in private categories can no longer be accessed via ajaxify or by
direct link
2013-12-23 21:42:34 -05:00
Baris Soner Usakli
83cc8f3ba8 fixed header template value too 2013-12-23 21:03:45 -05:00
Baris Soner Usakli
cab6ab8e17 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-23 21:00:02 -05:00
Baris Soner Usakli
e9fbed71ae closes #613, fixed allowRegistration incase its undefined 2013-12-23 20:59:55 -05:00
Julian Lam
b4121f262d removing serializeUser console.log -- how long has this ben here? haha 2013-12-23 20:51:17 -05:00
Julian Lam
256a2fa9c6 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-23 20:12:43 -05:00
Julian Lam
0f362b7fac updated bootstrap - because why not 2013-12-23 20:07:00 -05:00
Baris Soner Usakli
b504e2cd11 closes #658 2013-12-23 19:17:03 -05:00
Baris Soner Usakli
ea84fd70af Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-23 15:40:24 -05:00
Baris Soner Usakli
09efb83ef3 make sure relativeTime doesn't display negative values 2013-12-23 15:40:16 -05:00
Julian Lam
b2aeb14094 upping vanilla minver 2013-12-23 15:28:39 -05:00
Julian Lam
0ed8fd6cbd Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-23 15:23:45 -05:00
Julian Lam
2f1a3b9789 enabled touch events for composer resizing -- composer is now ipad
supported!
2013-12-23 15:23:08 -05:00
Baris Soner Usakli
d068139d81 changed header links to icons 2013-12-23 14:40:58 -05:00
Baris Soner Usakli
764e937c43 removed inline style 2013-12-23 14:21:28 -05:00
Baris Soner Usakli
f72cf35348 closes #680 2013-12-23 13:59:12 -05:00
Baris Soner Usakli
fe1a75d1fd paginator starts hidden 2013-12-23 13:49:24 -05:00
Baris Soner Usakli
3dc9e2f4fa added the success alert 2013-12-23 13:31:13 -05:00
Baris Soner Usakli
3bd8cf69a1 admins can create users from admin/users panel 2013-12-23 13:27:26 -05:00
Baris Soner Usakli
2c8725558e added allowRegistration to install 2013-12-23 12:31:51 -05:00
Baris Soner Usakli
f68f02d346 closes #679 2013-12-23 12:22:50 -05:00
Barış Soner Uşaklı
8fd78199e2 Merge pull request #678 from aonz1982/install_mongo_username
Installation problem when selecting MongoDB
2013-12-23 08:23:56 -08:00
Pongsan Sayampol
1bde86a33f MongoDB username is not saved to the config file during the installation. 2013-12-23 23:07:18 +07:00
Baris Soner Usakli
b6d4ae2732 removed mobile menu 2013-12-22 15:20:11 -05:00
Baris Soner Usakli
680dbf138a closes #669 2013-12-22 15:15:59 -05:00
Baris Soner Usakli
3752a1c691 small fixes 2013-12-21 19:42:21 -05:00
Baris Soner Usakli
3a00c177d3 closes #150 2013-12-21 19:42:07 -05:00
Baris Soner Usakli
845e5e7986 no need to store taskbar 2013-12-21 00:18:11 -05:00
Baris Soner Usakli
57d2db36e6 if not term just use empty string 2013-12-21 00:07:00 -05:00
Baris Soner Usakli
8bfea656c4 Merge branch 'master' of https://github.com/akhoury/NodeBB into akhoury-master 2013-12-20 23:53:53 -05:00
Baris Soner Usakli
ea2c03e28b closes #670 2013-12-20 23:35:40 -05:00
Baris Soner Usakli
f29b375ed4 refactored composer so that it uses a different div for each instance, closes #575 2013-12-20 18:41:56 -05:00
Julian Lam
07f1e0bcb5 ninjafix -- if you don't have a saved composer width, it doesn't set a
width (default ot 50%)
2013-12-20 12:34:26 -05:00
Julian Lam
0b94297c64 Merge remote-tracking branch 'origin/master' into composer-revamp 2013-12-19 18:36:20 -05:00
Baris Soner Usakli
8655c2d2a6 removed console.log 2013-12-19 15:43:15 -05:00
Baris Usakli
da9c673ec4 closes #651 2013-12-19 14:57:59 -05:00
Julian Lam
1e6863ee19 adjusting snap guide sensitivity 2013-12-19 00:08:47 -05:00
Julian Lam
00eb6b4efc responsive fixes for new composer 2013-12-19 00:02:14 -05:00
Julian Lam
002826d84f resizing the post composer now saves the size for future windows 2013-12-18 23:56:59 -05:00
Julian Lam
289e081e2e fixed new composer posting (woot) 2013-12-18 23:18:12 -05:00
Baris Soner Usakli
a717e9626e closes #666, the issue of the beast 2013-12-18 19:23:11 -05:00
Julian Lam
83f20c1cdb OMG SNAPS 2013-12-18 18:48:42 -05:00
Baris Soner Usakli
bc8adff70a fixed plugin deactivation 2013-12-18 15:32:38 -05:00
Julian Lam
c4623e2447 Merge remote-tracking branch 'origin/master' into composer-revamp 2013-12-18 15:12:30 -05:00
Julian Lam
99fba3b83a fixing issue where composer pushing didn't wait for the template to
properly render :P
2013-12-18 10:03:49 -05:00
Baris Soner Usakli
2580caf864 fixing #591 again looks like it got overwritter by merge 2013-12-18 05:35:06 -05:00
Julian Lam
f2d631e42d new composer window template (WIP!) 2013-12-17 23:42:02 -05:00
Julian Lam
d86aefb518 removing defunct "template" argument in ajaxify.go 2013-12-17 21:53:50 -05:00
psychobunny
3782ae1647 don't show profile fields if they are hidden. closes #660. also added missing language keys to EN 2013-12-17 18:05:58 -05:00
psychobunny
36e1a121ed removing some console.log's 2013-12-17 17:48:51 -05:00
psychobunny
2e52dd59ee Merge pull request #664 from ifuyivara/master
Adding support for restricting category read access based on user groups
2013-12-17 14:47:25 -08:00
psychobunny
d7a444d9ab dynamically update post count in post footer on new post 2013-12-17 16:10:32 -05:00
psychobunny
f9e2b50826 dynamically update user rep in post footer if reputation is increased 2013-12-17 16:01:08 -05:00
psychobunny
32a32fcf5c match login.tpl button to look like register.tpl 2013-12-17 15:42:01 -05:00
psychobunny
9435acfa71 skip debug messages for supervisor 2013-12-17 15:37:42 -05:00
root
35ad3be969 Adding support for restricting category read access based on user groups 2013-12-17 20:34:21 +00:00
Baris Soner Usakli
9cb20c3886 added callback to api categories.get 2013-12-16 19:12:26 -05:00
psychobunny
a2c9867902 plugins - woops, wrong namespace for admin api calls 2013-12-16 15:45:52 -05:00
psychobunny
58a3f33200 plugins - added api routes to filter:admin.create_routes 2013-12-16 15:40:59 -05:00
psychobunny
25bac03bab fixed facebook + gplus authentication - callbackURL was invalid 2013-12-15 14:12:36 -05:00
Baris Soner Usakli
770cea9329 added more error first to some functions 2013-12-14 16:29:33 -05:00
Baris Soner Usakli
30c11a8b42 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-13 16:36:38 -05:00
Baris Soner Usakli
27a01f6c61 closes #657 2013-12-13 16:36:29 -05:00
psychobunny
bf27ade251 admin - added ability to remove icon from categories 2013-12-13 16:26:32 -05:00
psychobunny
9d88b9eed3 default motd now shows @NodeBB, and not @dcplabs 2013-12-13 15:55:24 -05:00
akhoury
04ea573caa removed jQuery from iife, the $ is global 2013-12-12 22:52:44 -05:00
akhoury
d9eefd667c fixed potential guestSearch config bug 2013-12-12 22:51:17 -05:00
akhoury
3ad98f3783 remove uncessary checks in guest config upgrade 2013-12-12 22:50:49 -05:00
akhoury
b89cf6f480 Merge remote-tracking branch 'upstream/master' 2013-12-12 22:40:25 -05:00
psychobunny
4397da144f fixes crash introduced @1021615848e49da3434f00cfdb6fb79ab5990b47 2013-12-12 16:07:15 -05:00
psychobunny
cf8f0ca225 cleanup 2013-12-12 16:02:12 -05:00
Baris Soner Usakli
885242018f one more 2013-12-12 12:19:03 -05:00
Baris Soner Usakli
1abfe5de63 added err to addUserInfoToPost 2013-12-12 12:17:58 -05:00
akhoury
04dd1f9dac Merge remote-tracking branch 'upstream/master' 2013-12-11 22:51:04 -05:00
akhoury
114294e24a allowGuestSearching config .. second attempt 2013-12-11 22:50:36 -05:00
Baris Soner Usakli
dfa4cd4ae3 closes #653 2013-12-11 21:40:55 -05:00
Julian Lam
65d5a6cb81 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-11 16:10:22 -05:00
Julian Lam
64e87b761f closed #652 2013-12-11 16:10:08 -05:00
Baris Soner Usakli
1021615848 added err first to some calls 2013-12-11 16:08:20 -05:00
Julian Lam
69c1ec97c9 re: issue #652, this took longer than expected... 2013-12-11 16:02:29 -05:00
Julian Lam
45e7d64aeb fixing extra padding around 'in' that jshinter added for no goddamn reason 2013-12-11 12:29:35 -05:00
Julian Lam
b86a01ded1 moving cache buster to production builds only (*facepalm* I'm bad at this) 2013-12-10 22:20:11 -05:00
Julian Lam
e67af67180 fixing derped plugin css files due to cache buster 2013-12-10 22:16:43 -05:00
Julian Lam
9d03147f4e upping mentions minver 2013-12-10 22:07:10 -05:00
Julian Lam
c313c4501b pushNotifCount method for real-time updating of notification bell + favicon for multiple tabs (closes #219) 2013-12-10 22:05:37 -05:00
Julian Lam
538356846d fixed regression introduced by #643. Non-logo favicon is now properly
referenced (absolute url, not relative)
2013-12-10 21:20:54 -05:00
Baris Usakli
55d008d71f added err to callbacks 2013-12-10 15:39:53 -05:00
Baris Usakli
401a30e02c fixed categories.getRecentReplies so it only returns count posts instead of getting 10, posts are added and removed from sorted set when they are deleted restored 2013-12-10 15:33:35 -05:00
Baris Usakli
9816272b7b if there are more than 50 results only return 50, issue #648 2013-12-10 15:02:22 -05:00
Julian Lam
d72d2decd5 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-09 14:50:59 -05:00
Julian Lam
43c05d1d85 adding cache busters to client side files (closed #586) 2013-12-09 14:50:40 -05:00
Barış Soner Uşaklı
14f35a8c6b Update README.md 2013-12-09 14:49:57 -05:00
Barış Soner Uşaklı
ffe1549cad Update README.md 2013-12-09 14:49:03 -05:00
Barış Soner Uşaklı
3ca58a438d Update README.md 2013-12-09 14:44:26 -05:00
Baris Usakli
5da4cead67 closes #631 2013-12-09 14:37:13 -05:00
Baris Usakli
0000a7f0b9 #631 2013-12-09 14:16:04 -05:00
Baris Usakli
27ac24b1e3 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-09 13:56:18 -05:00
Baris Usakli
95495926fc some fixes to composer image upload 2013-12-09 13:56:09 -05:00
psychobunny
0f254c0b6c moved theme.css to top 2013-12-09 13:36:48 -05:00
psychobunny
b27478876e moved favicon link to top of header 2013-12-09 13:36:47 -05:00
psychobunny
a3734f2e15 completed custom favicon upload, closes #643 2013-12-09 13:36:47 -05:00
psychobunny
1b843fba9c added admin route to upload favicon 2013-12-09 13:36:46 -05:00
psychobunny
35f17db141 added button in admin to upload favicon + show current favicon image 2013-12-09 13:36:45 -05:00
Barış Soner Uşaklı
bcb364c4d4 Merge pull request #644 from designcreateplay/dbal
Dbal
2013-12-09 09:47:24 -08:00
Baris Usakli
6e16cb4b30 fixed conflicts 2013-12-09 12:46:27 -05:00
Baris Usakli
40e71299a1 closes #642 2013-12-09 12:39:36 -05:00
Julian Lam
0148cf06d0 replaced all instances of missing config.relative_path to RELATIVE_PATH 2013-12-09 10:02:34 -05:00
Julian Lam
ffa31ca0e7 upping markdown minver 2013-12-08 16:59:21 -05:00
Barış Soner Uşaklı
ba3b8a21ae Merge pull request #640 from akhoury/master
issue#635
2013-12-08 12:26:24 -08:00
akhoury
05209b01b9 setting initialValues for 'global' object closes #635 2013-12-08 15:18:00 -05:00
akhoury
4e0d0c2c20 adding error.log .gitignore, winston is that you? 2013-12-08 15:16:37 -05:00
Baris Soner Usakli
225284073f Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-08 14:50:11 -05:00
psychobunny
ccef51095a fix typo 2013-12-08 14:49:47 -05:00
Baris Soner Usakli
cd77a1a457 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-08 14:42:54 -05:00
Baris Soner Usakli
6aeca98cd4 closes #633 2013-12-08 14:42:47 -05:00
psychobunny
07a3b3f00b added delete button to category image uploader in case you wanted to revert to bgColor 2013-12-08 14:37:44 -05:00
Barış Soner Uşaklı
77e0cdcc3e Merge pull request #637 from akhoury/master
Auto install fix
2013-12-08 11:22:17 -08:00
psychobunny
e316dd3330 fixed a bug in create category 2013-12-08 14:20:57 -05:00
psychobunny
b511653e74 fixes #622 2013-12-08 14:15:03 -05:00
psychobunny
b5e37a6ce8 fixes #123, parse object properties in template outside of namespace 2013-12-08 13:50:11 -05:00
psychobunny
a34ed92fac console.log 2013-12-08 13:27:41 -05:00
psychobunny
4ff8509a0e clean up from the last admin/categories refactor 2013-12-08 13:19:20 -05:00
psychobunny
e76936abfc added ability to upload category images. closes #638, closes #532 2013-12-08 13:13:13 -05:00
Baris Soner Usakli
9471fd8e46 closes #635 2013-12-08 13:04:38 -05:00
akhoury
2d4ceb8f9f should close #634 2013-12-08 11:59:48 -05:00
psychobunny
4c40ee8e6e fixed missing language key on image uploader modal 2013-12-08 11:31:57 -05:00
psychobunny
ddcf46fc73 uploading picture didn't update profile image until refreshed - fixed 2013-12-08 11:30:29 -05:00
psychobunny
7db234f958 added setting to convert profile iamges to png in control panel 2013-12-08 11:26:55 -05:00
psychobunny
f6f7959d28 convert profile images to static png if setting is checked in control panel, closes #562 2013-12-08 11:25:15 -05:00
psychobunny
7fa2f474fe fixes bug introduced in c0721e105f 2013-12-08 10:55:34 -05:00
psychobunny
d5e8044575 ability to disable privilege threshold system, closes #528 2013-12-08 10:49:42 -05:00
psychobunny
c0721e105f added setting to disable privilege threshold system 2013-12-08 10:47:05 -05:00
psychobunny
a475e38078 if app is not focused and chat message comes in, activate alternatingTitle. closes #620 2013-12-08 10:40:02 -05:00
psychobunny
932b960aa9 app.isFocused; app.js cleanup 2013-12-08 10:38:09 -05:00
psychobunny
24b12e23ea fixes #636, conditionals broken from f1ad469861 2013-12-08 10:31:18 -05:00
psychobunny
2a5d6e04fc fixes #583 (aka. "son, we heard you like containers so we put containers inside your containers") -- duly fixed 2013-12-08 10:23:47 -05:00
psychobunny
4459d9d4e0 closes #551 2013-12-07 19:18:57 -05:00
psychobunny
2be3158aff added semver dep, pls run npm install semver 2013-12-07 19:11:50 -05:00
psychobunny
f1ad469861 templates - don't clean up selector conditionals; fixes topic posting bug introduced at 090d35f306 2013-12-07 19:07:51 -05:00
psychobunny
0c05ee82b1 #621, moved everything out of parallel - poster doesn't need to wait for these functions to happen
also renamed topics.markUnRead to topics.markAsUnreadForAll
2013-12-07 18:58:27 -05:00
psychobunny
4f97275d24 closes #621 2013-12-07 18:45:26 -05:00
psychobunny
4b9bfca767 français, deutsche, and español support for 202a4c4105 2013-12-07 18:22:31 -05:00
psychobunny
202a4c4105 added rep + post count metrics to post body, closes #443 2013-12-07 18:19:06 -05:00
psychobunny
090bc2ad10 403 all guests on /search & /search/term regardless of mobile/desktop - closes #612 2013-12-07 17:45:51 -05:00
psychobunny
000c7efb1a Merge pull request #623 from tedr56/master
French language files
2013-12-07 14:29:10 -08:00
psychobunny
90ad08a00d fixes #616 2013-12-07 17:17:47 -05:00
psychobunny
90e1e2436c closes #539 2013-12-07 17:03:16 -05:00
Baris Soner Usakli
21f5dad1d2 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-07 17:03:14 -05:00
Baris Soner Usakli
bc8bb352a8 added stdout check to exec 2013-12-07 17:03:07 -05:00
psychobunny
274310e35a removed notification count in title, removed api calls to it (left method intact) so hopefully title building is faster too. closes #625 2013-12-07 16:57:49 -05:00
Baris Soner Usakli
0bcc1642c7 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-07 16:51:57 -05:00
Baris Soner Usakli
d1d6605dcf closes #501 2013-12-07 16:51:47 -05:00
psychobunny
da94d6214b when new notification comes in, refresh notification page - closes #608 2013-12-07 16:42:17 -05:00
psychobunny
67e49db797 ajaxify.refresh(); removed console.log 2013-12-07 16:40:14 -05:00
psychobunny
de9100c489 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-07 16:33:56 -05:00
psychobunny
4348e1efa4 use ajaxify.currentPage to find the user's location easily 2013-12-07 16:33:42 -05:00
Julian Lam
6de3dba239 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-07 16:33:41 -05:00
Julian Lam
021cf9b8f6 fixes to language template usage 2013-12-07 16:33:37 -05:00
psychobunny
ca087e6fa7 fixes #576 2013-12-07 16:27:22 -05:00
psychobunny
6c9e28232f added translation method to recent's infinite loader 2013-12-07 16:21:12 -05:00
psychobunny
ceac8e2dc9 added translation to unread infinite loader 2013-12-07 16:20:31 -05:00
psychobunny
98cf6eec71 shift clicking now opens links in a new window, closes #630 2013-12-07 16:18:07 -05:00
Baris Soner Usakli
5a00767370 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-07 16:15:15 -05:00
Baris Soner Usakli
4c90d22e43 closes #619 2013-12-07 16:15:07 -05:00
psychobunny
e0e153eafb added translation to category infinite loader method 2013-12-07 16:14:51 -05:00
psychobunny
5c3c2623f2 added translation to new post creation, closes #627 2013-12-07 16:14:51 -05:00
Baris Soner Usakli
34fc326a37 closes #618 2013-12-07 15:59:44 -05:00
psychobunny
36745608bf removed console.log 2013-12-07 15:49:48 -05:00
psychobunny
090d35f306 clean up conditional comments in the DOM; conditionals now treat undefined variables as false; closes #617 2013-12-07 15:48:35 -05:00
Julian Lam
347f5c132e updates to language files, capitalizing some values across languages 2013-12-07 15:47:00 -05:00
Julian Lam
d6f8162f17 upping markdown minver again? 2013-12-07 15:02:00 -05:00
Julian Lam
b0b0f3640c closed #629 2013-12-07 15:00:12 -05:00
Julian Lam
4fd08332cc Merge branch 'dbal' of github.com:designcreateplay/NodeBB into dbal 2013-12-07 13:12:07 -05:00
Julian Lam
aaaffb823f upgrading markdown minver 2013-12-07 13:11:59 -05:00
Baris Soner Usakli
76636b64db small fixes 2013-12-06 21:08:21 -05:00
Baris Soner Usakli
be6ed43223 removed the setName from sorted sets and stored them with _key like everything else 2013-12-06 18:58:22 -05:00
Barış Soner Uşaklı
9f2196abfb Merge pull request #626 from designcreateplay/dbal
Dbal
2013-12-06 15:08:32 -08:00
Baris Soner Usakli
90a75ee045 added check for unknown db 2013-12-06 17:51:16 -05:00
Baris Soner Usakli
2a21b4855e fixed instruction in database.js 2013-12-06 17:47:48 -05:00
Baris Soner Usakli
8f769d53a3 fixed missing winston 2013-12-06 17:46:03 -05:00
Baris Usakli
9ee250b597 fixes humanreadable numbers in infinite scrolling 2013-12-06 16:26:07 -05:00
Baris Usakli
074d7e7c8d Merge remote-tracking branch 'origin/master' into dbal 2013-12-06 15:58:58 -05:00
Barış Soner Uşaklı
870d48dc81 Merge pull request #603 from Joopmicroop/master
added account class for account template pages for easy styling
2013-12-06 12:38:32 -08:00
Baris Usakli
9ca10c25d4 fixed return in posts.create 2013-12-06 15:23:48 -05:00
tedr56
8e2cc1c883 Correct english language file 2013-12-06 21:06:07 +01:00
tedr56
af09f4aca8 added french language files 2013-12-06 21:04:57 +01:00
Baris Usakli
ab63ca6d92 store strings in sorted sets and lists to mimic redis 2013-12-06 14:53:03 -05:00
Baris Usakli
400845ce6c cleanup mongo init 2013-12-06 14:34:25 -05:00
Baris Usakli
351b07bb34 added authentication to mongo 2013-12-06 14:31:11 -05:00
Baris Usakli
f861d44d55 updated upgrade script to use mongo or redis 2013-12-06 14:22:31 -05:00
Baris Usakli
2b7e4cbdf4 moved tests from debug to tests folder 2013-12-06 13:46:12 -05:00
Baris Usakli
df10bde2db added 1 more parseInt 2013-12-06 13:30:44 -05:00
Baris Usakli
0da141e7bc removed redismock, added database mocked, fixed tests to work with dbal 2013-12-06 13:21:21 -05:00
Baris Usakli
6313a5eeb1 Merge remote-tracking branch 'origin/master' into dbal 2013-12-06 12:59:32 -05:00
Pablo Macaluso
26de85c1de updated es translations with new strings added from #607 2013-12-06 12:21:34 -05:00
Julian Lam
a54f464a13 updating language files with missing files for es 2013-12-06 11:57:03 -05:00
Julian Lam
bfbc596348 fixing extra comma in es language file 2013-12-06 11:38:33 -05:00
Julian Lam
7508bd216e new executable 2013-12-06 11:27:34 -05:00
Pablo Macaluso
8cfc5dda37 added spanish language files (courtesy of Pablo Macaluso) 2013-12-06 10:51:20 -05:00
Julian Lam
b81737bc0f moved refreshTitle into app.js, messaging someone now invokes an alternating title, hehe 2013-12-05 23:24:47 -05:00
Baris Soner Usakli
1c23be8911 removed console.log 2013-12-05 21:53:24 -05:00
Baris Soner Usakli
5f86e31d1e fixed favouriting 2013-12-05 21:51:05 -05:00
Baris Soner Usakli
746119bd45 remove dataFileVersion it breaks templates 2013-12-05 21:45:21 -05:00
Julian Lam
385aa6df92 fixed last couple tweaks, ready t'merge 2013-12-05 21:36:21 -05:00
Baris Soner Usakli
097810a057 parseInt fixes getObjects fix 2013-12-05 21:29:51 -05:00
Baris Soner Usakli
3a7fcc2d3d search in mongo 2013-12-05 21:07:35 -05:00
Baris Soner Usakli
95bb838699 Merge remote-tracking branch 'origin/master' into dbal 2013-12-05 21:04:00 -05:00
Baris Soner Usakli
81055523a0 fixed bracket 2013-12-05 20:24:25 -05:00
Baris Soner Usakli
e9fbab0f26 need mongo 2.4+ for text search' 2013-12-05 20:11:05 -05:00
Baris Soner Usakli
53ca7a1143 added search functions to database files, removed reds from nodebb moved it to redis 2013-12-05 20:06:36 -05:00
Baris Soner Usakli
6c70d37f1c removed reds from app.js 2013-12-05 18:32:45 -05:00
Baris Soner Usakli
447073560f moved reds into redis, fixed notifications prune param 2013-12-05 18:31:15 -05:00
Julian Lam
2c131f172a fixing up some translations - WIP 2013-12-05 18:27:37 -05:00
Baris Soner Usakli
1564e3d530 tons of fixes to mongo and redis, to make returns the same 2013-12-05 18:26:26 -05:00
Julian Lam
3d9a732c4a Merge branch 'master' of github.com:BudickDa/NodeBB into BudickDa-master
Conflicts:
	public/language/en/global.json
2013-12-05 18:05:26 -05:00
Julian Lam
4819bea378 more tweaks to the chat dropdown -- handling null case -- issue #615 2013-12-05 17:41:51 -05:00
Julian Lam
030ce95dea closed #615 - added "active chats" dropdown to header
- some minor tweaks also, to make taskbar work a little better with chats
2013-12-05 17:35:44 -05:00
Julian Lam
2a1671ba9b whoops 2013-12-05 16:40:30 -05:00
Julian Lam
a65c79cb02 minimized the reconnect spinner to icon only, added bootstrap tooltip 2013-12-05 16:08:35 -05:00
Baris Usakli
dcbe4ffd4a more notif.prune cleanup 2013-12-05 15:32:36 -05:00
Baris Usakli
215b919362 added db function 2013-12-05 15:29:27 -05:00
Baris Usakli
58df656c65 removed temp vars 2013-12-05 15:27:14 -05:00
Baris Usakli
a8c91e2452 more 2013-12-05 15:26:30 -05:00
Baris Usakli
806a454b05 fixing notifications.prune 2013-12-05 15:25:58 -05:00
Baris Usakli
705754e823 merged master 2013-12-05 14:57:18 -05:00
Baris Usakli
fe527ff2a9 removed RedisStoreLib from websockets.js it moved into redis.js 2013-12-05 14:48:58 -05:00
Julian Lam
041e77f688 fixing replying (whoops) 2013-12-05 14:48:27 -05:00
Baris Usakli
b927f6ce29 added raw info 2013-12-05 14:30:18 -05:00
Baris Usakli
519d376071 added mongo info function, change admin redis template to database template 2013-12-05 14:24:18 -05:00
Julian Lam
d9ee9bf5e3 revamped handling of unread messages, so that a socket call is made to all socket clients whenever a new unread message is available. Prior behaviour had the unread count updated via ajax call on ajaxify (which was clumsy at best and didn't update automagically) 2013-12-05 13:59:16 -05:00
Baris Usakli
67d5ea83e7 replace . with \uff0e in mongo 2013-12-05 13:33:01 -05:00
Baris Usakli
7875138c08 added parseInt 2013-12-05 13:11:27 -05:00
Julian Lam
43b012b32e defactored getUnreadTopics into separate getUnreadTids method -- for no reason, mind you 2013-12-05 12:20:56 -05:00
Baris Usakli
c096656eff merged master fixed conflicts 2013-12-05 12:17:01 -05:00
Baris Usakli
2dd295118c setRemove converts to string too 2013-12-05 12:04:09 -05:00
Baris Soner Usakli
567997ef3c changed mongo sets to store just strings, fixes worlds problems 2013-12-04 23:51:57 -05:00
Baris Soner Usakli
c698af17ae added error checking to collection index creation 2013-12-04 23:36:52 -05:00
Baris Soner Usakli
39b70a9e09 derp 2013-12-04 22:55:31 -05:00
Baris Soner Usakli
9b557cafd8 better sets in mongo, should change sorted sets too using sort 2013-12-04 22:19:11 -05:00
Julian Lam
fe4aee177d lots of tweaks to chat and taskbar 2013-12-04 21:44:36 -05:00
Baris Soner Usakli
7c4347736c added index on _key 2013-12-04 21:35:38 -05:00
Julian Lam
16e07d475f pushing FA icon for chat taskbar title 2013-12-04 21:05:44 -05:00
Julian Lam
2125bb2223 only minimizing CSS files on non-development 2013-12-04 20:37:13 -05:00
Baris Soner Usakli
72a3ab1d6c derp 2013-12-04 19:06:36 -05:00
Baris Soner Usakli
639247a8b0 fixed notifications 2013-12-04 18:58:20 -05:00
Baris Soner Usakli
f0caac242c fixed incr in topic post 2013-12-04 18:34:58 -05:00
Baris Soner Usakli
e066fbf36a sets are storing numbers in mongo as opposed to redis which stores strings, causes tons of problems 2013-12-04 18:26:26 -05:00
Baris Soner Usakli
b215dbde19 updated install 2013-12-04 17:57:51 -05:00
Baris Soner Usakli
1325e4c501 commented out reds in install 2013-12-04 17:08:49 -05:00
Baris Soner Usakli
2e2938616d more mongo and redis stuff 2013-12-04 16:58:06 -05:00
Baris Soner Usakli
53a7eab3e8 removed couple RDB.multis 2013-12-04 16:31:05 -05:00
Julian Lam
6bd4a34e69 fixed favicon sometimes updating with a string 0, should always be an int 2013-12-04 16:28:18 -05:00
Julian Lam
d27f2eb214 fixed #604 2013-12-04 16:25:42 -05:00
Baris Soner Usakli
b547d3577b removed a multi from categories.js 2013-12-04 15:13:43 -05:00
Baris Soner Usakli
4b5988c269 isSetMember returns true or false 2013-12-04 15:11:17 -05:00
Baris Soner Usakli
cfd3a7d126 added flushdb command to both dbs 2013-12-04 14:51:50 -05:00
Baris Soner Usakli
0471a192ab more mongo stuff 2013-12-04 14:25:14 -05:00
Baris Soner Usakli
5c6a7d4b94 added regex keys to mongo 2013-12-04 12:36:22 -05:00
Baris Soner Usakli
ca01fb9f7d added key methods to mongo 2013-12-04 12:25:53 -05:00
Julian Lam
b176629b44 integrated Tinycon lib and actually fixed notification counts... heh.
closed #610
2013-12-04 12:15:27 -05:00
Baris Soner Usakli
113cb85c46 added lists to mongo 2013-12-04 12:10:53 -05:00
Julian Lam
5b6f5ebf9d added tinycon lib and changed notif icon to be in CSS instead of in
template
2013-12-04 11:40:57 -05:00
Baris Soner Usakli
21bbe68c97 added sortedSetRemove to mongo 2013-12-03 22:30:36 -05:00
Baris Soner Usakli
28c75e09a9 can haz sorted sets? 2013-12-03 22:16:44 -05:00
Julian Lam
69f453c73c Merge pull request #601 from cnvo/patch-1
Replace notifications icons from fa-circle to fa-bell
2013-12-03 18:20:50 -08:00
Julian Lam
dab0435d9b Merge pull request #600 from cnvo/patch-2
Replace notifications icons from fa-circle to fa-bell
2013-12-03 18:20:35 -08:00
Baris Soner Usakli
b5770be71f whitespace 2013-12-03 18:19:27 -05:00
Baris Soner Usakli
bf3822e8a5 incr wil create key if it doesnt exist 2013-12-03 18:11:35 -05:00
Baris Soner Usakli
cb6c42ea44 user stuff 2013-12-03 18:03:50 -05:00
Baris Soner Usakli
3dc3769088 fixed redis.keys 2013-12-03 17:48:18 -05:00
Baris Soner Usakli
3d18c4015a cleanup 2013-12-03 17:43:12 -05:00
Baris Soner Usakli
610d45bb32 completed hashes in mongodb I hope :) 2013-12-03 17:29:10 -05:00
Baris Soner Usakli
66bc4184d8 deleteObjectField, isObjectField in mongo 2013-12-03 17:13:59 -05:00
Baris Soner Usakli
b024d10185 implemented getObjectValues in mongo 2013-12-03 16:50:06 -05:00
Julian Lam
e847c015a5 linting notifications.js 2013-12-03 16:12:59 -05:00
Baris Usakli
10474f8e2a more mongo work 2013-12-03 15:17:42 -05:00
BudickDa
8a99eef4fb Update global.json 2013-12-03 20:40:45 +01:00
Julian Lam
da66efe7a5 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-03 14:23:31 -05:00
Julian Lam
1bd94b4e41 closed #605 2013-12-03 14:23:16 -05:00
Baris Usakli
e862a1c4cc added init method to database, progress made 2013-12-03 14:21:08 -05:00
psychobunny
8b55920acf plugins - filter:posts.custom_profile_info pass in pid as well 2013-12-03 14:19:11 -05:00
BudickDa
32df811765 i18n for all templates completed, jsons for german added 2013-12-03 19:53:16 +01:00
Baris Usakli
91d6f83de4 more mongo work 2013-12-03 13:36:44 -05:00
psychobunny
7c8f857aaf plugins - filter:server.create_routes allows you to define API routes as well now 2013-12-03 13:10:46 -05:00
psychobunny
1f970c3bdb incorrect url was being passed for callback - twitter and g+ 2013-12-03 11:28:55 -05:00
Joopmicroop
700cab686b Update following.tpl 2013-12-03 15:49:10 +01:00
Joopmicroop
af06e0335a Update followers.tpl 2013-12-03 15:48:28 +01:00
Joopmicroop
4a8dc82bd2 Update accountedit.tpl 2013-12-03 15:45:15 +01:00
Joopmicroop
8a4e2b1ea0 Update accountsettings.tpl 2013-12-03 15:42:33 +01:00
Baris Soner Usakli
1f52717f1e fixed indent 2013-12-02 22:50:39 -05:00
Baris Soner Usakli
304285e874 some mongo stuff 2013-12-02 22:48:32 -05:00
Baris Soner Usakli
d4eddc6e2c lcased groups 2013-12-02 22:36:26 -05:00
Baris Soner Usakli
6d79521922 moved connect-redis to the redis.db file, expose the sessionStore from redis.js db file, do the same for mongo db with mongo-connect 2013-12-02 22:33:55 -05:00
Baris Soner Usakli
95db5f93cb remvoed console.logs 2013-12-02 22:01:29 -05:00
Baris Soner Usakli
b900bc9cce more fixes 2013-12-02 21:58:37 -05:00
Baris Soner Usakli
fc066c21bf added upgrade for global keys 2013-12-02 21:33:35 -05:00
Baris Soner Usakli
32d5118266 tons more changes 2013-12-02 21:20:55 -05:00
Baris Soner Usakli
3775c8e50a tons more changes 2013-12-02 19:40:11 -05:00
Barış Soner Uşaklı
99fbc0dfd5 Update account.js
removed extra s
2013-12-02 19:23:52 -05:00
Baris Soner Usakli
e32d230974 format 2013-12-02 17:48:21 -05:00
Baris Usakli
c9308efbec more work 2013-12-02 17:10:26 -05:00
Baris Usakli
636551d2e9 plugins.js uses db, added some set methods to redis.js 2013-12-02 16:35:32 -05:00
Baris Usakli
347d6c2768 moved filename function into redis 2013-12-02 16:23:14 -05:00
Baris Usakli
4f654fb489 more work 2013-12-02 16:19:30 -05:00
Baris Usakli
80e7fd93c6 added redis 2013-12-02 15:46:25 -05:00
Baris Usakli
95efb2ae5e started dbal 2013-12-02 15:45:15 -05:00
Baris Usakli
8d3a647d16 fixed hardcoded userslug 2013-12-02 14:53:33 -05:00
Baris Usakli
53afe6cb68 closes #591 2013-12-02 14:40:34 -05:00
Barış Soner Uşaklı
391b8098a3 Merge pull request #588 from akhoury/master
websockets to follow same pattern, added hook action:user.create
2013-12-02 11:25:51 -08:00
Baris Usakli
190948336a closes #590 2013-12-02 13:28:46 -05:00
Julian Lam
fcda27e251 allowing plugins with no hooks to not cause NodeBB to hang on app start 2013-12-02 11:36:46 -05:00
Trvr
a48a07603c Replace notifications icons from fa-circle to fa-bell
We should change this icon to utilize the icons better.
2013-12-02 04:40:20 -05:00
Trvr
d930e2a1be Replace notifications icons from fa-circle to fa-bell
We should change this icon to utilize the icons better.
2013-12-02 04:40:19 -05:00
Baris Soner Usakli
7e2166903f Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-12-01 21:12:13 -05:00
Baris Soner Usakli
d5c0ca4a9d closes #595 2013-12-01 21:12:05 -05:00
Julian Lam
7695e76494 removed buildFooter filter 2013-12-01 21:04:10 -05:00
Julian Lam
78af47362c Merge pull request #597 from draco2003/patch-4
Cleanup console.log calls
2013-12-01 17:30:07 -08:00
Dan Rowe
18a890ed51 Cleanup console.log calls
Can't see my debugging inbetween all your debugging ;)
2013-12-01 20:28:16 -05:00
Julian Lam
5f731dd1f6 Merge pull request #596 from draco2003/patch-3
Missed one lcasing
2013-12-01 17:17:05 -08:00
Julian Lam
51990142d5 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-12-01 20:13:56 -05:00
Julian Lam
b840169a8c upping markdown minver 2013-12-01 20:13:43 -05:00
Dan Rowe
94fdeb2378 Missed one lcasing
Also anyone else nervous about arbitrary hook execution here?
2013-12-01 20:11:27 -05:00
Julian Lam
68e7ee7f07 signature parsing now calls its own postTools method (with associated plugin hook 2013-12-01 20:07:30 -05:00
Julian Lam
77d5ecc82a added post.buildFooter and post.parseSignature hooks 2013-12-01 19:59:17 -05:00
Baris Soner Usakli
437379413e closes #592 2013-12-01 18:06:01 -05:00
Baris Soner Usakli
ddb7896df1 fixed post count in paginator and post bar when infinite loader kicked in 2013-12-01 17:42:26 -05:00
Baris Soner Usakli
66cdb9a067 just send err 2013-12-01 16:25:57 -05:00
Baris Soner Usakli
f5741fd48b fixed conflict 2013-12-01 16:21:50 -05:00
Baris Soner Usakli
f405dec4e9 #589 2013-12-01 16:21:19 -05:00
Julian Lam
cc242ca667 fixed #589 2013-12-01 16:19:12 -05:00
akhoury
2733198f9e oops 2013-12-01 11:05:10 -05:00
akhoury
52700fbe16 websockets.js to 'use strict'; 2013-12-01 10:56:29 -05:00
akhoury
0f53749e70 websockets.js to follow same pattern to avoid circular dependencies 2013-12-01 10:53:59 -05:00
akhoury
1aa1ddb4ec added plugin hook > action:user.create on user creation 2013-12-01 10:52:50 -05:00
akhoury
71125fd1e2 adding webstorm's .idea folder to .gitignore 2013-12-01 10:43:48 -05:00
Julian Lam
5621fb8622 switching back to version numbers for vanilla, testing prereleases 2013-11-30 21:38:35 -05:00
Julian Lam
b9f1176ec1 moved paginator progress bar into paginator block, better mobile handling, and hiding it on page load 2013-11-30 21:36:35 -05:00
Julian Lam
b3b4d3c37d Merge pull request #585 from akhoury/master
During 11k users migration, was hitting too many open files errors
2013-11-30 11:13:18 -08:00
Baris Soner Usakli
cdbca7d262 more cleanup in routes/user.js 2013-11-30 13:54:52 -05:00
Baris Soner Usakli
140f90f7f3 cleaned up requires 2013-11-30 13:35:42 -05:00
Aziz Khoury
a905c6a084 During 11k users migration, was hitting too many open files errors 2013-11-30 13:13:18 -05:00
Baris Soner Usakli
bfe081f672 added err to PostTools.isMain 2013-11-29 23:14:28 -05:00
Baris Soner Usakli
46a14715e3 fixed logout in admin site, cleaned up topics.post, anon users can post topics now if allowed 2013-11-29 23:08:42 -05:00
Baris Soner Usakli
c1da56ce45 added auto mention if u click post reply 2013-11-29 18:00:52 -05:00
Baris Usakli
ee63fae803 closes #584 2013-11-29 16:10:41 -05:00
Baris Usakli
720711756f show bottom post bar after new post, fixed the insertion of new posts 2013-11-29 14:42:58 -05:00
Baris Usakli
90a12c3253 fixed index to int 2013-11-29 14:28:15 -05:00
Baris Usakli
6a00ab3782 removed btn-large from post topic button 2013-11-29 14:22:02 -05:00
Baris Usakli
9ccfa3f18c fixed js error 2013-11-29 14:19:39 -05:00
Baris Usakli
1660d75205 stricter selector for human readable numbers 2013-11-29 14:18:06 -05:00
Baris Usakli
f6d57a241d fixed human readable view post counts 2013-11-29 14:12:19 -05:00
Julian Lam
bcfb4ca0e4 off by one error 2013-11-29 14:01:09 -05:00
Julian Lam
0159a43a20 Merge branch 'master' of github.com:designcreateplay/NodeBB
Conflicts:
	public/src/forum/topic.js
2013-11-29 13:31:07 -05:00
Julian Lam
86b019ec89 paginator progress bar 2013-11-29 13:30:01 -05:00
psychobunny
98a12cd1e1 clearing first blocks from dynamically inserted templates - made regex less greedy 2013-11-29 13:18:20 -05:00
Baris Usakli
e6452cbd4d removed unusued uploadedImages property 2013-11-29 13:14:38 -05:00
Baris Usakli
cd44ead595 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-29 13:12:59 -05:00
Baris Usakli
652fda1a6f fixed conditional in posts.js 2013-11-29 13:12:50 -05:00
psychobunny
30a7f1a816 removed .main-post from follow class (now using conditionals to show/hide) 2013-11-29 13:09:56 -05:00
Baris Usakli
6ca3d0c4af Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-29 13:09:35 -05:00
Baris Usakli
b2074c6dda fixes to infinite load and paginator 2013-11-29 13:09:26 -05:00
Julian Lam
d7c0e1c62e Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-29 12:35:58 -05:00
Julian Lam
56ef05a0bf putting category options behind a dropdown, and fixing category enable/disable 2013-11-29 12:35:36 -05:00
Baris Usakli
dd36cce329 closes #582 2013-11-29 12:02:06 -05:00
Baris Usakli
2b2799dae9 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-29 11:50:16 -05:00
Baris Usakli
293c176053 added first to follow button in topic.tpl 2013-11-29 11:50:07 -05:00
Julian Lam
3cef5fce2b Merge pull request #580 from Joopmicroop/patch-1
Allow same origin framing
2013-11-29 08:42:46 -08:00
Joopmicroop
7d0f160c4c Allow same origin framing
Allow people to frame there own server setup
2013-11-29 11:04:46 +01:00
Baris Soner Usakli
c5f9f896b2 make all admin routes only accesible to admins 2013-11-28 22:29:32 -05:00
Baris Soner Usakli
db98b3db55 removed the fields again, gitsucks 2013-11-28 18:55:10 -05:00
Baris Soner Usakli
dba47e4bef removed console.log 2013-11-28 18:45:36 -05:00
Baris Soner Usakli
2a46ead29f removed container div, removed second if first, it breaks templates 2013-11-28 18:44:02 -05:00
Julian Lam
707179edf3 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-28 17:51:02 -05:00
Julian Lam
53164e96ab removed non-functional save button from category whitelist modal 2013-11-28 17:50:40 -05:00
Baris Soner Usakli
9702e28a07 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-28 17:45:17 -05:00
Baris Soner Usakli
fa56eca962 fixed fave stars, removed the watch button from normal posts 2013-11-28 17:45:04 -05:00
Julian Lam
684839f04a Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-28 17:37:41 -05:00
Julian Lam
0188ea9a3b setting cache headers to 0 on development mode 2013-11-28 17:37:17 -05:00
psychobunny
85aa587749 merge conflicts 2013-11-28 17:16:27 -05:00
psychobunny
48e36e3c31 templates bugfix - now supports multiple conditionals of the same variable; added data-favourited button and deprecated reliance on BS3 2013-11-28 17:14:04 -05:00
Baris Usakli
0b6d018f8d oops removed by mistage 2013-11-28 16:20:05 -05:00
Baris Usakli
f2bf65252d removed the fields 2013-11-28 16:15:18 -05:00
Baris Usakli
4335f8c5c6 fixed date in upgrade 2013-11-28 15:57:00 -05:00
Baris Usakli
ae82f57c67 added upgrade script to delete removed keys from posts 2013-11-28 15:55:40 -05:00
Baris Usakli
168052bf45 added back feeds, removed duplicate post var in feed 2013-11-28 15:33:26 -05:00
Baris Usakli
a87ebb64d8 clean up of posts and some template additions 2013-11-28 15:18:19 -05:00
Julian Lam
38da65ee58 tweaks to category permission modal 2013-11-28 14:27:19 -05:00
Julian Lam
e3e1a556cd groups pruning method 2013-11-28 12:55:05 -05:00
Baris Usakli
56b618c915 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-28 11:17:01 -05:00
Baris Usakli
7198110b57 closes #573 2013-11-28 11:16:52 -05:00
psychobunny
b7aea63c53 create auth routes only after plugin system is enabled and auth.init hook is fired 2013-11-28 11:13:08 -05:00
Baris Usakli
78b65c0b12 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-28 10:58:24 -05:00
Baris Usakli
0742590c0c added pid to share links, removed unused vars 2013-11-28 10:58:14 -05:00
psychobunny
c16f04bbcf plugins - filter:admin.header.build (pre-existing hook) now allows you to add menu items to the social authentication panel as well 2013-11-28 10:42:25 -05:00
psychobunny
51b38c4c55 plugins - added filter:auth.init hook to add additional login strategies; fixed callbackURL 2013-11-28 10:30:43 -05:00
Julian Lam
79eddc9b06 Merge pull request #570 from draco2003/patch-2
Minify socket.io js
2013-11-27 20:07:36 -08:00
Baris Soner Usakli
21f63ac17f Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-27 22:02:57 -05:00
Baris Soner Usakli
fdf5227c35 closes #572 2013-11-27 22:02:50 -05:00
Julian Lam
98f03a723e upping mentions minver 2013-11-27 21:30:26 -05:00
Julian Lam
69427fa10e tweaks to make the ACP work better on tablets, some work on hidden groups 2013-11-27 20:20:08 -05:00
Julian Lam
670986f7ef Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-27 20:07:37 -05:00
Julian Lam
8b62041d28 refactored groups and categories, groups no longer explodes when
attempting to add members to a whitelist that doesn't exist, isEmpty
checks (new methods!) and such...
2013-11-27 20:07:04 -05:00
psychobunny
89c5d01efa modularized SSO, paving the way for potential plugins. cleaned up associated templates/js 2013-11-27 16:47:54 -05:00
Julian Lam
0a06f1ac7d Merge remote-tracking branch 'origin/master' into category-whitelisting 2013-11-27 16:23:48 -05:00
Julian Lam
df2e9b4b47 admin panel integration for category whitelisting 2013-11-27 16:23:30 -05:00
Dan Rowe
aed4e2792d Minify socket.io js
potential solution for issues/569
2013-11-27 15:57:29 -05:00
Baris Soner Usakli
c860df7975 more #559 2013-11-27 15:03:36 -05:00
Baris Soner Usakli
2148f6ff95 closes #559 2013-11-27 15:02:09 -05:00
Baris Soner Usakli
2d05a06e37 fixed template var, oops 2013-11-27 14:25:58 -05:00
Baris Soner Usakli
e3da005780 grouped the social buttons 2013-11-27 14:25:01 -05:00
Julian Lam
f0e8633dcc category whitelisting for posting messages, isAdmin now error-first 2013-11-27 12:47:00 -05:00
Baris Usakli
590208f0c7 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-27 12:39:15 -05:00
Baris Usakli
7d7ead3f47 fixed js error in recent.js, remove content assignment 2013-11-27 12:39:07 -05:00
Julian Lam
974629ce85 hahah, bad paste, missing a bunch of line endings 2013-11-27 11:48:18 -05:00
Julian Lam
72ef8c839f Merge remote-tracking branch 'origin/master' into category-whitelisting
Conflicts:
	src/groups.js
2013-11-27 11:34:52 -05:00
Julian Lam
5ee5c8179a category whitelist for replying to posts, lots of refactoring, too 2013-11-27 11:27:20 -05:00
psychobunny
46d6d7637e potentially fixes ubbmigrator issue https://github.com/akhoury/nodebb-plugin-ubbmigrator/issues/2 2013-11-27 11:21:16 -05:00
psychobunny
62e2aa67d7 topics - added back social sharing buttons to OP 2013-11-27 10:58:37 -05:00
Julian Lam
182659d0e1 removing extra .js suffix from required files and physically separating
nodebb libs from deps
2013-11-27 08:55:02 -05:00
Julian Lam
4855131652 category read restrictions 2013-11-27 08:49:23 -05:00
Julian Lam
4d2469b4ce tweaks to whitelist (interim commit) 2013-11-27 08:34:25 -05:00
Julian Lam
4e2326fc84 Merge remote-tracking branch 'origin' into category-whitelisting 2013-11-27 08:17:23 -05:00
Baris Soner Usakli
3a8dca6fb9 closes #560 2013-11-26 23:55:55 -05:00
Baris Soner Usakli
31635b92f3 closes #561 2013-11-26 23:19:21 -05:00
Baris Soner Usakli
a0c3de0273 display signature block only if user has signature 2013-11-26 19:22:02 -05:00
Baris Soner Usakli
32b191fa62 removed global.io 2013-11-26 19:09:32 -05:00
Baris Usakli
f616125d02 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-26 16:30:19 -05:00
Baris Usakli
e1f6e064a9 removed io from user.js, figure out why notifications are breaking when websockets.js is required from it 2013-11-26 16:30:10 -05:00
Julian Lam
633be7ff3c Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-26 15:50:37 -05:00
Julian Lam
6b3863bfbf updated category manager in admin panel 2013-11-26 15:50:33 -05:00
Baris Usakli
b66ca703b8 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-26 15:28:55 -05:00
Baris Usakli
48a7f9058f closes #558 2013-11-26 15:28:45 -05:00
psychobunny
8f80733563 fixing #552 overflows properly 2013-11-26 15:19:30 -05:00
Baris Usakli
aa1451cfbc Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-26 15:15:31 -05:00
Baris Usakli
13ef28118d closes #557 2013-11-26 15:15:23 -05:00
Julian Lam
34459e7cf1 fixing category admin panel integration 2013-11-26 15:14:12 -05:00
Baris Usakli
798e17a954 change loading text to inline 2013-11-26 14:48:55 -05:00
Julian Lam
5ad80218a2 upping cerulean minver 2013-11-26 14:45:12 -05:00
Baris Usakli
e90e54cec1 fixed icons in topic move 2013-11-26 14:37:27 -05:00
Baris Usakli
bca1fde69c updated schema date 2013-11-26 14:33:18 -05:00
Baris Usakli
d70c688b65 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-26 14:26:01 -05:00
Baris Usakli
541993c80a fontawesome 4 migration, closes #473 2013-11-26 14:25:46 -05:00
psychobunny
529c8acf6c app.js: added --install alias for --setup 2013-11-26 14:15:40 -05:00
psychobunny
e98d05b1a5 closes #556, also removes the newline that was previously inserted 2013-11-26 13:18:41 -05:00
Julian Lam
0afc7c9761 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-26 12:25:24 -05:00
Julian Lam
e5c1edd5f5 using ajaxifying class for content and footer instead of jquery fadein/out 2013-11-26 12:25:19 -05:00
Baris Usakli
a5e78aab08 closes #554 2013-11-26 11:59:54 -05:00
Baris Usakli
cec69c2be0 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-26 11:43:35 -05:00
Baris Usakli
bc8815cb94 closes #555 2013-11-26 11:43:25 -05:00
Julian Lam
efac6272bb default link tag (apple-touch-icon) added 2013-11-26 11:34:38 -05:00
Julian Lam
bdb30976b5 adding maxAge headers for statically served files (eep!) - issue #552 2013-11-26 11:17:34 -05:00
Baris Soner Usakli
1d4ae8fe4d changed the paginator icons to chevron 2013-11-25 21:38:04 -05:00
Julian Lam
abdb4c34ef tweaks to topic locking 2013-11-25 17:49:17 -05:00
Julian Lam
08d130893c fixing dropdown support in Settings.prepare 2013-11-25 17:48:55 -05:00
Julian Lam
b2fb9aa99f refactored topic locking pinning and deleting (and its inverses) so that the privilege check is done not in the method, but in the socket call 2013-11-25 17:20:44 -05:00
psychobunny
561ee9e4f1 closes #519 2013-11-25 17:00:42 -05:00
Julian Lam
3f70d45f3d Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-25 17:00:06 -05:00
Julian Lam
c07783fa2c ... 2013-11-25 17:00:01 -05:00
psychobunny
d4d0e8cd9b set unreplied flag to new topics sent via websocket 2013-11-25 16:51:52 -05:00
psychobunny
4be5ac2c23 removed mobile overlay for topics - will reimplement as a plugin 2013-11-25 16:47:06 -05:00
psychobunny
070c95d8de fixed regression from main_posts deprecation - bottom post bar restored 2013-11-25 16:38:45 -05:00
psychobunny
8d12ecb758 moved pagination into header 2013-11-25 16:37:28 -05:00
Baris Soner Usakli
81560c4698 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-11-25 16:29:37 -05:00
Baris Soner Usakli
390937a20b added err to getAllTopics 2013-11-25 16:29:28 -05:00
Julian Lam
2a8a38b742 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-25 16:28:14 -05:00
Julian Lam
5a5c3c8c60 added app.load action hook 2013-11-25 16:28:07 -05:00
psychobunny
905e451455 closes #549 2013-11-25 16:16:18 -05:00
psychobunny
69b8b47b15 updated cerulean dep to 0.0.9 2013-11-25 15:57:18 -05:00
Julian Lam
b16e1a0113 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-11-25 15:56:22 -05:00
Julian Lam
0b39968a9c fixing back button behaviour and playing nice with History API 2013-11-25 15:56:16 -05:00
psychobunny
d8b3abb611 updating cerulean dep to 0.0.8 2013-11-25 15:48:56 -05:00
psychobunny
7fb0a616a1 refactored topic.tpl and merged main_posts and sub_posts 2013-11-25 15:48:55 -05:00
psychobunny
d987e51a8b removed references to main_posts in forum/topic.js 2013-11-25 15:48:55 -05:00
psychobunny
0f7f6cd0a3 templates.js added IF !value 2013-11-25 15:48:54 -05:00
psychobunny
0e8b33aa79 templates.js added @first and @last conditionals 2013-11-25 15:48:54 -05:00
psychobunny
353b3047cd pulling meta info from OP not main_posts 2013-11-25 15:48:53 -05:00
psychobunny
377de06eeb removed main_posts from topics core, returning OP in posts array now 2013-11-25 15:48:53 -05:00
psychobunny
4013e27a8b removed main_posts from feed.j 2013-11-25 15:48:52 -05:00
psychobunny
181c6eb2e6 removed main_posts from noscript 2013-11-25 15:48:52 -05:00
Julian Lam
18009ebb39 interim commit 2013-11-13 19:54:46 -05:00
190 changed files with 13988 additions and 7586 deletions

5
.gitignore vendored
View File

@@ -8,6 +8,7 @@ public/css/*.css
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
.project .project
.idea
*.swp *.swp
Vagrantfile Vagrantfile
.vagrant .vagrant
@@ -15,3 +16,7 @@ provision.sh
*.komodoproject *.komodoproject
feeds/recent.rss feeds/recent.rss
# winston?
error.log
events.log

View File

@@ -8,9 +8,9 @@
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") * [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
![NodeBB Main Category Listing](http://i.imgur.com/zffCFoh.png) ![NodeBB Main Category Listing](http://i.imgur.com/zRdzCcj.png)
![NodeBB Topic Page](http://i.imgur.com/JihdcUa.png) ![NodeBB Topic Page](http://i.imgur.com/ZC8W39a.png)
## How can I follow along/contribute? ## How can I follow along/contribute?
@@ -23,7 +23,7 @@
NodeBB requires the following software to be installed: NodeBB requires the following software to be installed:
* A version of Node.js at least 0.8 or greater * A version of Node.js at least 0.8 or greater
* Redis, version 2.6 or greater. * Redis, version 2.6 or greater or MongoDB, version 2.4 or greater
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB) * nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
## Installation ## Installation
@@ -32,6 +32,8 @@ First, we install our base software stack:
# apt-get install git nodejs redis-server npm build-essential imagemagick # apt-get install git nodejs redis-server npm build-essential imagemagick
If you want to use MongoDB instead of Redis install it from http://www.mongodb.org/downloads and remove 'redis-server' from the above command. [MongoDB-Setup](https://github.com/designcreateplay/NodeBB/wiki/MongoDB-Setup)
**If your package manager only installed a version of Node.js that is less than 0.8 (e.g. Ubuntu 12.10, 13.04):** **If your package manager only installed a version of Node.js that is less than 0.8 (e.g. Ubuntu 12.10, 13.04):**
# add-apt-repository ppa:chris-lea/node.js # add-apt-repository ppa:chris-lea/node.js

108
app.js
View File

@@ -25,9 +25,11 @@
var fs = require('fs'), var fs = require('fs'),
async = require('async'), async = require('async'),
semver = require('semver'),
winston = require('winston'), winston = require('winston'),
pkg = require('./package.json'),
path = require('path'), path = require('path'),
pkg = require('./package.json'),
utils = require('./public/src/utils.js'),
meta; meta;
// Runtime environment // Runtime environment
@@ -48,6 +50,12 @@
winston.error(err.stack); winston.error(err.stack);
}; };
require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) {
if(err || !stdout) {
winston.warn('Couldn\'t find convert. Did you install imagemagick?');
}
});
// Log GNU copyright info along with server info // Log GNU copyright info along with server info
winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.'); winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
@@ -55,15 +63,15 @@
winston.info(''); winston.info('');
if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('upgrade') && fs.existsSync(__dirname + '/config.json')) { if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && fs.existsSync(__dirname + '/config.json')) {
// Load server-side configs // Load server-side configs
nconf.file({ nconf.file({
file: __dirname + '/config.json' file: __dirname + '/config.json'
}); });
meta = require('./src/meta.js'); meta = require('./src/meta');
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/'); nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + path.sep);
nconf.set('upload_url', nconf.get('url') + 'uploads/'); nconf.set('upload_url', path.join(path.sep, nconf.get('relative_path'), 'uploads', path.sep));
nconf.set('base_dir', __dirname); nconf.set('base_dir', __dirname);
winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.'); winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
@@ -73,61 +81,55 @@
winston.info('Base Configuration OK.'); winston.info('Base Configuration OK.');
} }
meta.configs.init(function () { if (semver.gt(pkg.dependencies['nodebb-theme-cerulean'], require('./node_modules/nodebb-theme-cerulean/package.json').version)) {
// Initial setup for Redis & Reds winston.error('nodebb-theme-cerulean is out of date - please run npm install.')
var reds = require('reds'), }
RDB = require('./src/redis.js');
reds.createClient = function () { require('./src/database').init(function(err) {
return reds.client || (reds.client = RDB); meta.configs.init(function () {
};
var templates = require('./public/src/templates.js'), var templates = require('./public/src/templates'),
translator = require('./public/src/translator.js'), translator = require('./public/src/translator'),
webserver = require('./src/webserver.js'), webserver = require('./src/webserver'),
SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket']}), SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket'], 'browser client minification': true}),
websockets = require('./src/websockets.js'), websockets = require('./src/websockets'),
posts = require('./src/posts.js'), plugins = require('./src/plugins'),
plugins = require('./src/plugins'), // Don't remove this - plugins initializes itself notifications = require('./src/notifications'),
Notifications = require('./src/notifications'), upgrade = require('./src/upgrade');
Upgrade = require('./src/upgrade');
Upgrade.check(function(schema_ok) { upgrade.check(function(schema_ok) {
if (schema_ok || nconf.get('check-schema') === false) { if (schema_ok || nconf.get('check-schema') === false) {
websockets.init(SocketIO); websockets.init(SocketIO);
global.templates = {}; plugins.init();
global.translator = translator; global.templates = {};
global.translator = translator;
translator.loadServer(); translator.loadServer();
var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false; var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false;
// 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',
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
], customTemplates);
plugins.ready(function() { utils.walk(path.join(__dirname, 'public/templates'), function (err, tplsToLoad) {
templates.ready(webserver.init); templates.init(tplsToLoad, customTemplates);
}); });
Notifications.init(); plugins.ready(function() {
} else { templates.ready(webserver.init);
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):'); notifications.init();
winston.warn(' node app --no-check-schema') } else {
process.exit(); 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('setup') || !fs.existsSync(__dirname + '/config.json')) { } else if (nconf.get('setup') || nconf.get('install') || !fs.existsSync(__dirname + '/config.json')) {
// New install, ask setup questions // New install, ask setup questions
if (nconf.get('setup')) { if (nconf.get('setup')) {
winston.info('NodeBB Setup Triggered via Command Line'); winston.info('NodeBB Setup Triggered via Command Line');
@@ -159,10 +161,12 @@
nconf.file({ nconf.file({
file: __dirname + '/config.json' file: __dirname + '/config.json'
}); });
meta = require('./src/meta.js'); require('./src/database').init(function(err) {
meta = require('./src/meta.js');
meta.configs.init(function () { meta.configs.init(function () {
require('./src/upgrade').upgrade(); require('./src/upgrade').upgrade();
});
}); });
} else/* if (nconf.get('help') */{ } else/* if (nconf.get('help') */{
winston.info('Usage: node app [options] [arguments]'); winston.info('Usage: node app [options] [arguments]');

View File

@@ -2,85 +2,97 @@
{ {
"name": "Announcements", "name": "Announcements",
"description": "Announcements regarding our community", "description": "Announcements regarding our community",
"blockclass": "category-blue", "bgColor": "#0059B2",
"icon" : "icon-bullhorn", "color": "#fff",
"icon" : "fa-bullhorn",
"order": 1 "order": 1
}, },
{ {
"name": "General Discussion", "name": "General Discussion",
"description": "A place to talk about whateeeever you want", "description": "A place to talk about whateeeever you want",
"blockclass": "category-blue", "bgColor": "#0059B2",
"icon" : "icon-comment", "color": "#fff",
"icon" : "fa-comment",
"order": 2 "order": 2
}, },
{ {
"name": "NodeBB Development", "name": "NodeBB Development",
"description": "NodeBB development news and announcements", "description": "NodeBB development news and announcements",
"blockclass": "category-blue", "bgColor": "#0059B2",
"icon" : "icon-github", "color": "#fff",
"icon" : "fa-github",
"order": 3 "order": 3
}, },
{ {
"name": "Blogs", "name": "Blogs",
"description": "Blog posts from individual members", "description": "Blog posts from individual members",
"blockclass": "category-blue", "bgColor": "#0059B2",
"icon" : "icon-pencil", "color": "#fff",
"icon" : "fa-pencil",
"order": 4 "order": 4
}, },
{ {
"name": "Feature Requests", "name": "Feature Requests",
"description": "Got a feature request you'd like to see? Give us a shout here.", "description": "Got a feature request you'd like to see? Give us a shout here.",
"blockclass": "category-purple", "bgColor": "#ab1290",
"icon" : "icon-lightbulb", "color": "#fff",
"icon" : "fa-lightbulb-o",
"order": 5 "order": 5
}, },
{ {
"name": "Bug Reports", "name": "Bug Reports",
"description": "Having trouble with NodeBB? Let us know...", "description": "Having trouble with NodeBB? Let us know...",
"blockclass": "category-purple", "bgColor": "#ab1290",
"icon" : "icon-cogs", "color": "#fff",
"icon" : "fa-cogs",
"order": 6 "order": 6
}, },
{ {
"name": "NodeBB Plugins", "name": "NodeBB Plugins",
"description": "Enhance your NodeBB with plugins!", "description": "Enhance your NodeBB with plugins!",
"blockclass": "category-purple", "bgColor": "#ab1290",
"icon" : "icon-plus-sign", "color": "#fff",
"icon" : "fa-plus-square",
"order": 7 "order": 7
}, },
{ {
"name": "NodeBB Link Exchange", "name": "NodeBB Link Exchange",
"description": "Link exchange", "description": "Link exchange",
"blockclass": "category-purple", "bgColor": "#ab1290",
"icon" : "icon-exchange", "color": "#fff",
"icon" : "fa-exchange",
"order": 8 "order": 8
}, },
{ {
"name": "News", "name": "News",
"description": "News from around the world", "description": "News from around the world",
"blockclass": "category-darkblue", "bgColor": "#004C66",
"icon" : "icon-globe", "color": "#fff",
"icon" : "fa-globe",
"order": 9 "order": 9
}, },
{ {
"name": "Movies", "name": "Movies",
"description": "Discuss the latest movies here", "description": "Discuss the latest movies here",
"blockclass": "category-darkblue", "bgColor": "#004C66",
"icon" : "icon-film", "color": "#fff",
"icon" : "fa-film",
"order": 10 "order": 10
}, },
{ {
"name": "Games", "name": "Games",
"description": "Discuss the latest games here", "description": "Discuss the latest games here",
"blockclass": "category-darkblue", "bgColor": "#004C66",
"icon" : "icon-screenshot", "color": "#fff",
"icon" : "fa-crosshairs",
"order": 11 "order": 11
}, },
{ {
"name": "Random", "name": "Random",
"description": "Anything and (almost) everything welcome!", "description": "Anything and (almost) everything welcome!",
"blockclass": "category-darkblue", "bgColor": "#004C66",
"icon" : "icon-beer", "color": "#fff",
"icon" : "fa-beer",
"order": 12 "order": 12
} }
] ]

81
mocks/databasemock.js Normal file
View File

@@ -0,0 +1,81 @@
/**
* Database Mock - wrapper for database.js, makes system use separate test db, instead of production
* ATTENTION: testing db is flushed before every use!
*/
(function(module) {
'use strict';
var 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 dbType = nconf.get('database'),
testDbConfig = nconf.get('test_database'),
productionDbConfig = nconf.get(dbType);
if(!testDbConfig){
errorText = 'test_database is not defined';
winston.info(
"\n===========================================================\n"+
"Please, add parameters for test database in config.json\n"+
"For example (redis):\n"+
'"test_database": {' + '\n' +
' "host": "127.0.0.1",' + '\n' +
' "port": "6379",' + '\n' +
' "password": "",' + '\n' +
' "database": "1"' + '\n' +
'}\n'+
" or (mongo):\n" +
'"test_database": {' + '\n' +
' "host": "127.0.0.1",' + '\n' +
' "port": "27017",' + '\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 = 'test_database has the same config as production db';
winston.error(errorText);
throw new Error(errorText);
}
nconf.set(dbType, testDbConfig);
db = require('../src/database');
before(function(done) {
db.init(function(err) {
//Clean up
db.flushdb(function(err) {
if(err){
winston.error(err);
throw new Error(err);
} else {
winston.info('test_database flushed');
done();
}
//TODO: data seeding, if needed at all
});
});
});
module.exports = db;
}(module));

View File

@@ -1,69 +0,0 @@
/**
* 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));

60
nodebb
View File

@@ -1,6 +1,54 @@
#!/bin/sh #!/bin/bash
clear
echo "Launching NodeBB in \"development\" mode." # $0 script path
echo "To run the production build of NodeBB, please use \"forever\"." # $1 action
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB" # $2 subaction
NODE_ENV=development supervisor --extensions 'node|js|tpl' -- app $1
case "$1" in
start)
node app
;;
dev)
echo "Launching NodeBB in \"development\" mode."
echo "To run the production build of NodeBB, please use \"forever\"."
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
NODE_ENV=development node app
;;
watch)
echo "Launching NodeBB in \"development\" mode."
echo "To run the production build of NodeBB, please use \"forever\"."
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
NODE_ENV=development supervisor -q --extensions 'node|js|tpl' -- app $1
;;
language)
case "$2" in
check)
node app --language="check"
;;
*)
echo "Language Settings"
echo $"Usage: $0 language {check}"
echo ''
column -s ' ' -t <<< '
check Compare language files against the /en directory
'
;;
esac
;;
*)
echo "Welcome to NodeBB"
echo $"Usage: $0 {start|dev|watch|language}"
echo ''
column -s ' ' -t <<< '
start Start NodeBB in production mode
dev Start NodeBB in development mode
watch Start NodeBB in development mode and watch for changes
language Language settings
'
exit 1
esac

View File

@@ -2,7 +2,7 @@
"name": "nodebb", "name": "nodebb",
"license": "GPLv3 or later", "license": "GPLv3 or later",
"description": "NodeBB Forum", "description": "NodeBB Forum",
"version": "0.1.1", "version": "0.2.2",
"homepage": "http://www.nodebb.org", "homepage": "http://www.nodebb.org",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -14,12 +14,10 @@
}, },
"dependencies": { "dependencies": {
"socket.io": "~0.9.16", "socket.io": "~0.9.16",
"redis": "0.8.3",
"express": "3.2.0", "express": "3.2.0",
"express-namespace": "~0.1.1", "express-namespace": "~0.1.1",
"emailjs": "0.3.4", "emailjs": "0.3.4",
"cookie": "0.0.6", "cookie": "0.0.6",
"connect-redis": "1.4.5",
"passport": "0.1.17", "passport": "0.1.17",
"passport-local": "0.1.6", "passport-local": "0.1.6",
"passport-twitter": "0.1.5", "passport-twitter": "0.1.5",
@@ -28,7 +26,7 @@
"less-middleware": "0.1.12", "less-middleware": "0.1.12",
"marked": "0.2.8", "marked": "0.2.8",
"bcrypt": "0.7.5", "bcrypt": "0.7.5",
"async": "0.2.8", "async": "~0.2.8",
"node-imagemagick": "0.1.8", "node-imagemagick": "0.1.8",
"gravatar": "1.0.6", "gravatar": "1.0.6",
"nconf": "~0.6.7", "nconf": "~0.6.7",
@@ -40,13 +38,20 @@
"prompt": "~0.2.11", "prompt": "~0.2.11",
"uglify-js": "~2.4.0", "uglify-js": "~2.4.0",
"validator": "~1.5.1", "validator": "~1.5.1",
"nodebb-plugin-mentions": "~0.1.14", "nodebb-plugin-mentions": "~0.1",
"nodebb-plugin-markdown": "~0.1.8", "nodebb-plugin-markdown": "~0.3",
"nodebb-theme-vanilla": "designcreateplay/nodebb-theme-vanilla", "nodebb-theme-vanilla": "~0.0.12",
"nodebb-theme-cerulean": "0.0.7", "nodebb-theme-cerulean": "0.0.10",
"cron": "~1.0.1" "cron": "~1.0.1",
"semver": "~2.2.1",
"string": "~1.7.0",
"xregexp": "~2.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"redis": "0.8.3",
"mongodb": "~1.3.19",
"connect-redis": "1.4.5",
"connect-mongo": "0.4.0",
"hiredis": "~0.1.15" "hiredis": "~0.1.15"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -0,0 +1,14 @@
{
"new_topic_button": "Neues Thema",
"no_topics": "<strong>Es gibt noch keine Threads in dieser Kategorie.</strong><br />Warum beginnst du nicht den ersten?",
"sidebar.recent_replies": "Neuste Antworten",
"sidebar.active_participants": "Aktive Teilnehmer",
"sidebar.moderators": "Moderatoren",
"posts": "Posts",
"views": "Aufrufe",
"posted": "Geposted",
"browsing": "Sieht zu",
"no_replies": "Niemand hat geantwortet",
"replied": "geantwortet",
"last_edited_by": "zuletzt editiert durch"
}

View File

@@ -0,0 +1,10 @@
{
"chat.chatting_with": "Chatten mit <span id='chat-with-name'></span>",
"chat.placeholder": "schreibe hier etwas, und drücke Enter zum absenden",
"chat.send": "Senden",
"stats.online": "Online",
"stats.users": "Benutzer",
"stats.topics": "Themen",
"stats.posts": "Beiträge",
"success": "erfolg"
}

View File

@@ -0,0 +1,31 @@
{
"home": "Übersicht",
"search": "Suche",
"buttons.close": "Schließen",
"403.title": "Zugriff Verweigert",
"403.message": "Du bist nicht dazu berechtigt, diese Seite aufzurufen. <a href='/login'>Logge dich ein</a> und versuche es erneut.",
"404.title": " Nicht Gefunden",
"404.message": "Die abgefragte Seite wurde nicht gefunden. <a href='/''>Zurück zur Übersicht</a>.",
"500.title": "Internal error.",
"500.message": "Ooops! Looks like something went wrong!",
"register": "Registrierung",
"login": "Login",
"logout": "Logout",
"logout.title": "Du bist ausgeloggt.",
"logout.message": "Du hast dich soeben erfolgreich aus dem Forum ausgeloggt.",
"save_changes": "Speichere Änderungen",
"close": "Schließen",
"header.admin": "Admin",
"header.recent": "Aktuell",
"header.unread": "Ungelesen",
"header.users": "Benutzer",
"header.search": "Suche",
"header.profile": "Profil",
"notifications.loading": "Benachrichtigungen laden",
"chats.loading": "Nachrichten werden geladen"
}

View File

@@ -0,0 +1,10 @@
{
"login": "Einloggen",
"username": "Benutzername",
"password": "Passwort",
"remember_me": "Eingeloggt bleiben?",
"forgot_password": "Passwort vergessen?",
"alternative_logins": "Login Alternativen",
"failed_login_attempt": " Anmeldeversuch fehlgeschlagen, versuche es erneut.",
"login_successful": "Du hast dich erfolgreich eingeloggt!"
}

View File

@@ -0,0 +1,9 @@
{
"title": "Benachrichtigungen",
"back_to_home": "Zurück zur Startseite",
"mark_all_as_read": "Alles als gelesen markieren",
"outgoing_link": "Externer Link",
"outgoing_link_message": "Du verlässt nun",
"continue_to": "Gehe weiter zu",
"return_to": "Kehre zurück zu"
}

View File

@@ -0,0 +1,5 @@
{
"day": "Tag",
"week": "Woche",
"month": "Monat"
}

View File

@@ -0,0 +1,16 @@
{
"register": "Registrieren",
"help.email": "Deine E-Mail Adresse ist standardmäßig nicht öffentlich sichtbar.",
"help.username_restrictions": "Einen einmaligen Benutzernamen. 3-16 Zeichen. Andere Benutzer können dich mit @<span id='yourUsername'>Benutzername</span> anschreiben.",
"help.minimum_password_length": "Dein Passwort muss mindestens sechs Zeichen lang sein.",
"email_address": "E-Mail",
"email_address_placeholder": "E-Mail Adresse hier eingeben",
"username": "Benutzername",
"username_placeholder": "Benutzernamen eingeben",
"password": "Passwort",
"password_placeholder": "Passwort eingeben",
"confirm_password": "Passwort bestätigen",
"confirm_password_placeholder": "Passwort zur Bestätigung erneut eingeben",
"register_now_button": "Jetzt registrieren",
"alternative_registration": "Alternative Registrierung"
}

View File

@@ -0,0 +1,13 @@
{
"reset_password": "Passwort zurücksetzen",
"update_password": "Ändere Passwort",
"password_change": "Passwort wurde geändert",
"password_reset_successful": "<p>Das Passwort wurde erfolgreich zurückgesetzt. <a href=\"/login\">Log dich neu ein</a>.",
"wrong_reset_code.title": "Der Reset-Code ist falsch.",
"wrong_reset_code.message": "Der empfangene Reset-Code war falsch. Bitte versuche es erneut oder <a href=\"/reset\">fordere einen neuen Code an</a>.",
"new_password": "Neues Passwort",
"repeat_password": "Wiederhole das Passwort",
"enter_email": "Bitte gib Deine <strong>E-Mail Adresse</strong> ein und wir senden Dir eine Anleitung, wie Du Dein Passwort zurücksetzen kannst.",
"password_reset_sent": "Passwortzrücksetzung beantragt.",
"invalid_email": "Ungültige E-Mail / Adresse existiert nicht!"
}

View File

@@ -0,0 +1,43 @@
{
"topic": "Thema",
"topics": "Themen",
"no_topics_found": "Keine passende Themen gefunden.",
"profile": "Profil",
"posted_by": "geschrieben von",
"chat": "Chat",
"notify_me": "Werde bei neues Antworten auf dieses Thema benachrichtigt.",
"quote": "zitieren",
"reply": "antworten",
"edit": "bearbeiten",
"delete": "löschen",
"banned": "gesperrt",
"link": "Link",
"thread_tools.title": "Thread Tools",
"thread_tools.pin": "Thread pinnen",
"thread_tools.unpin": "Thread nicht mehr pinnen",
"thread_tools.lock": "Thread sperren",
"thread_tools.move": "Thread verschieben",
"thread_tools.delete": "Thread löschen",
"load_categories": "Kategorien laden",
"disabled_categories_note": "Deaktivierte Kategorien sind ausgegraut.",
"confirm_move": "verschieben",
"favourite": "Favorit",
"favourites": "Favoriten",
"favourites.not_logged_in.title": "Nicht eingeloggt!",
"favourites.not_logged_in.message": "Bitte logge dich ein, um diesen Beitrag favorisieren zu können.",
"favourites.has_no_favourites":"Du hast noch keine Favoriten.",
"posted_by": "Geposted von",
"loading": "Lade",
"more_posts": "Mehr Posts",
"move_topic": "Thema verschieben",
"topic_will_be_moved_to": "Dieses Thema wird verschoben nach",
"reputation": "Reputation",
"posts": "Posts"
}

View File

@@ -0,0 +1,5 @@
{
"no_unread_topics": "Es gibt keine ungelesenen Themen.",
"mark_all_read": "alle als gelesen markieren",
"load_more": "mehr laden"
}

View File

@@ -0,0 +1,38 @@
{
"banned": "Gebannt",
"offline": "offline",
"email": "E-Mail",
"fullname": "Kompletter Name",
"website": "Homepage",
"location": "Wohnort",
"age": "Alter",
"joined": "Beigetreten",
"profile_views": "Profilaufrufe",
"reputation": "Reputation",
"posts": "Posts",
"followers": "Follower",
"following": "Folgt",
"signature": "Signatur",
"gravatar": "Gravatar",
"birthday": "Geburtstag",
"change_picture": "Ändere Profilbild",
"edit": "Ändern",
"uploaded_picture": "Hochgeladene Bilder",
"upload_new_picture": "Neues Bild hochladen",
"change_password": "Ändere Passwort",
"confirm_password": "Passwort wiederholen",
"password": "Passwort",
"upload_picture": "Bild hochladen",
"upload_a_picture": "Ein Bild hochladen",
"image_spec": "Du solltest nur Dateien die PNG, JPG, oder GIF kleiner als 256kb hochladen.",
"settings": "Einstellungen",
"show_email": "Zeige meine E-Mail Adresse an.",
"has_no_follower": "Dieser User hat noch keine Follower.",
"follows_no_one": "Dieser User folgt noch niemanden."
}

View File

@@ -0,0 +1,9 @@
{
"latest_users": "neuste Benutzer",
"top_posters": "meiste Beiträge",
"most_reputation": "höhstes Ansehen",
"online": "Online",
"search": "Suchen",
"enter_username": "Benutzer durchsuchen",
"load_more": "mehr laden"
}

View File

@@ -3,5 +3,12 @@
"no_topics": "<strong>There are no topics in this category.</strong><br />Why don't you try posting one?", "no_topics": "<strong>There are no topics in this category.</strong><br />Why don't you try posting one?",
"sidebar.recent_replies": "Recent Replies", "sidebar.recent_replies": "Recent Replies",
"sidebar.active_participants": "Active Participants", "sidebar.active_participants": "Active Participants",
"sidebar.moderators": "Moderators" "sidebar.moderators": "Moderators",
} "posts": "posts",
"views": "views",
"posted": "posted",
"browsing": "browsing",
"no_replies": "No one has replied",
"replied": "replied",
"last_edited_by": "last edited by"
}

View File

@@ -1,9 +1,10 @@
{ {
"chat.chatting_with": "Chat with <span id='chat-with-name'></span>", "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", "chat.send": "Send",
"stats.online": "Online", "stats.online": "Online",
"stats.users": "Users", "stats.users": "Users",
"stats.topics": "Topics", "stats.topics": "Topics",
"stats.posts": "Posts" "stats.posts": "Posts",
} "success": "success"
}

View File

@@ -5,14 +5,27 @@
"403.title": "Access Denied", "403.title": "Access Denied",
"403.message": "You seem to have stumbled upon a page that you do not have access to. Perhaps you should <a href='/login'>try logging in</a>?", "403.message": "You seem to have stumbled upon a page that you do not have access to. Perhaps you should <a href='/login'>try logging in</a>?",
"404.title": "Not Found", "404.title": "Not Found",
"404.message": "You seem to have stumbled upon a page that does not exist. Return to the <a href='/''>home page</a>.", "404.message": "You seem to have stumbled upon a page that does not exist. Return to the <a href='/'>home page</a>.",
"500.title": "Internal error.",
"500.message": "Oops! Looks like something went wrong!",
"register": "Register",
"login": "Login",
"logout": "Logout", "logout": "Logout",
"logout.title": "You are now logged out.", "logout.title": "You are now logged out.",
"logout.message": "You have successfully logged out of NodeBB", "logout.message": "You have successfully logged out of NodeBB",
"save_changes": "Save Changes",
"close": "Close",
"header.admin": "Admin", "header.admin": "Admin",
"header.recent": "Recent", "header.recent": "Recent",
"header.unread": "Unread", "header.unread": "Unread",
"header.users": "Users", "header.users": "Users",
"header.search": "Search", "header.search": "Search",
"notifications.loading": "Loading Notifications" "header.profile": "Profile",
}
"notifications.loading": "Loading Notifications",
"chats.loading": "Loading Chats"
}

View File

@@ -7,4 +7,4 @@
"alternative_logins": "Alternative Logins", "alternative_logins": "Alternative Logins",
"failed_login_attempt": "Failed login attempt, please try again.", "failed_login_attempt": "Failed login attempt, please try again.",
"login_successful": "You have successfully logged in!" "login_successful": "You have successfully logged in!"
} }

View File

@@ -1,3 +1,9 @@
{ {
"title": "Notifications" "title": "Notifications",
} "back_to_home": "back to NodeBB",
"mark_all_as_read": "Mark All as Read",
"outgoing_link": "Outgoing Link",
"outgoing_link_message": "You are now leaving",
"continue_to": "Continue to",
"return_to": "Return to "
}

View File

@@ -0,0 +1,13 @@
{
"reset_password": "Reset Password",
"update_password": "Update Password",
"password_changed.title": "Password Changed",
"password_changed.message": "<p>Password successfully reset, please <a href=\"/login\">log in again</a>.",
"wrong_reset_code.title": "Incorrect Reset Code",
"wrong_reset_code.message": "The reset code received was incorrect. Please try again, or <a href=\"/reset\">request a new reset code</a>.",
"new_password": "New Password",
"repeat_password": "Confirm Password",
"enter_email": "Please enter your <strong>email address</strong> and we will send you an email with instructions on how to reset your account.",
"password_reset_sent": "Password Reset Sent",
"invalid_email": "Invalid Email / Email does not exist!"
}

View File

@@ -1,23 +1,43 @@
{ {
"topic": "Topic",
"topics": "Topics",
"no_topics_found": "No topics found!",
"profile": "Profile", "profile": "Profile",
"posted_by": "Posted by", "posted_by": "Posted by",
"chat": "Chat", "chat": "Chat",
"notify_me": "Be notified of new replies in this topic", "notify_me": "Be notified of new replies in this topic",
"favourite": "Favourite",
"quote": "Quote", "quote": "Quote",
"reply": "Reply", "reply": "Reply",
"edit": "Edit", "edit": "Edit",
"delete": "Delete", "delete": "Delete",
"fork": "Fork",
"banned": "banned", "banned": "banned",
"link": "Link", "link": "Link",
"thread_tools.title": "Thread Tools", "thread_tools.title": "Thread Tools",
"thread_tools.pin": "Pin Thread", "thread_tools.pin": "Pin Thread",
"thread_tools.lock": "Lock Thread", "thread_tools.lock": "Lock Thread",
"thread_tools.move": "Move Thread", "thread_tools.move": "Move Thread",
"thread_tools.delete": "Delete Thread", "thread_tools.delete": "Delete Thread",
"load_categories": "Loading Categories", "load_categories": "Loading Categories",
"disabled_categories_note": "Disabled Categories are greyed out", "disabled_categories_note": "Disabled Categories are greyed out",
"confirm_move": "Move", "confirm_move": "Move",
"favourite": "Favourite",
"favourites": "Favorites",
"favourites.not_logged_in.title": "Not Logged In", "favourites.not_logged_in.title": "Not Logged In",
"favourites.not_logged_in.message": "Please log in in order to favourite this post" "favourites.not_logged_in.message": "Please log in in order to favourite this post",
} "favourites.has_no_favourites": "You don't have any favourites, favourite some posts to see them here!",
"posted_by": "posted by",
"loading": "Loading",
"more_posts": "More Posts",
"move_topic": "Move Topic",
"topic_will_be_moved_to": "This topic will be moved to the category",
"reputation": "Reputation",
"posts": "Posts"
}

View File

@@ -0,0 +1,40 @@
{
"banned": "Banned",
"offline": "Offline",
"email": "Email",
"fullname": "Full Name",
"website": "Website",
"location": "Location",
"age": "Age",
"joined": "Joined",
"lastonline": "Last Online",
"profile_views": "Profile views",
"reputation": "Reputation",
"posts": "Posts",
"followers": "Followers",
"following": "Following",
"signature": "Signature",
"gravatar": "Gravatar",
"birthday": "Birthday",
"change_picture": "Change Picture",
"edit": "Edit",
"uploaded_picture": "Uploaded Picture",
"upload_new_picture": "Upload New Picture",
"change_password": "Change Password",
"confirm_password": "Confirm Password",
"password": "Password",
"upload_picture": "Upload picture",
"upload_a_picture": "Upload a picture",
"image_spec": "You may only upload PNG, JPG, or GIF files under 256kb.",
"settings": "settings",
"show_email": "Show My Email",
"has_no_follower": "This user doesn't have any followers :(",
"follows_no_one": "This user isn't following anyone :(",
"email_hidden": "Email Hidden",
"hidden": "hidden"
}

View File

@@ -0,0 +1,14 @@
{
"new_topic_button": "Nuevo Tema",
"no_topics": "<strong>No hay temas en esta categoría.</strong><br />Por que no te animas y publicas uno?",
"sidebar.recent_replies": "Respuestas recientes",
"sidebar.active_participants": "Miembros más activos",
"sidebar.moderators": "Moderadores",
"posts": "respuestas",
"views": "visitas",
"posted": "posted",
"browsing": "viendo ahora",
"no_replies": "Nadie ha respondido aún",
"replied": "respondio",
"last_edited_by": "ultima edición por"
}

View File

@@ -0,0 +1,10 @@
{
"chat.chatting_with": "Chatear con <span id='chat-with-name'></span>",
"chat.placeholder": "ingresa tu mensaje aqui, y presiona enter para enviar",
"chat.send": "Enviar",
"stats.online": "Online",
"stats.users": "Usuarios",
"stats.topics": "Temas",
"stats.posts": "Posts",
"success": "exito!"
}

View File

@@ -0,0 +1,31 @@
{
"home": "Inicio",
"search": "Buscar",
"buttons.close": "Cerrar",
"403.title": "Acceso denegado",
"403.message": "Al parecer no tienes premisos necesarios para estar en este lugar. Tal vez puedes <a href='/login'>intentar conectarte</a>?",
"404.title": "Ups... 404, no se encontra che!",
"404.message": "Al parecer lo que estas buscando no existe. Te recomendamos que vuelvas al <a href='/''>inicio</a>.",
"500.title": "Error Interno.",
"500.message": "Ooops! Algo salio mal!, No te alarmes. Nuestros simios hiperinteligentes lo solucionarán",
"register": "Registrarse",
"login": "Conectarse",
"logout": "Salir",
"logout.title": "Te has desconectado.",
"logout.message": "Haz sido desconectado correctamente",
"save_changes": "Guardar Cambios",
"close": "Cerrar",
"header.admin": "Admin",
"header.recent": "Recientes",
"header.unread": "No Leeidos",
"header.users": "Miembros",
"header.search": "Buscar",
"header.profile": "Perfil",
"notifications.loading": "Cargando Notificaciones",
"chats.loading": "Cargando Chats"
}

View File

@@ -0,0 +1,10 @@
{
"login": "Conectarse",
"username": "Usuario",
"password": "Contraseña",
"remember_me": "Recordarme?",
"forgot_password": "Olvidaste tu contraseña?",
"alternative_logins": "Conexiones Alternativas",
"failed_login_attempt": "Error al loguearte, intenta de nuevo.",
"login_successful": "Te has conectado con exito!"
}

View File

@@ -0,0 +1,9 @@
{
"title": "Notificaciones",
"back_to_home": "volver al Inicio",
"mark_all_as_read": "Marcar todo como leeido",
"outgoing_link": "Link Externo",
"outgoing_link_message": "Estas saliendo del sitio",
"continue_to": "Continuar",
"return_to": "Volver a "
}

View File

@@ -0,0 +1,5 @@
{
"day": "Día",
"week": "Semana",
"month": "Mes"
}

View File

@@ -0,0 +1,16 @@
{
"register": "Registrase",
"help.email": "Por defecto, tu email será oculto al publico.",
"help.username_restrictions": "El nombre de usuario debe tener entre %1 y %2 caracteres. Los miembros pueden responderte escribiendo @<span id='yourUsername'>usuario</span>.",
"help.minimum_password_length": "Tu contraseña debe tener al menos %1 caracteres.",
"email_address": "Email",
"email_address_placeholder": "Escribe tu email",
"username": "Usuario",
"username_placeholder": "Escribe tu usuario",
"password": "Contraseña",
"password_placeholder": "Escribe tu Contraseña",
"confirm_password": "Confirmar Contraseña",
"confirm_password_placeholder": "Confirmar Contraseña",
"register_now_button": "Registrarme ahora",
"alternative_registration": "Otros metodos interesantes para registrarse"
}

View File

@@ -0,0 +1,13 @@
{
"reset_password": "Resetear Contraseña",
"update_password": "Actualizar contraseña",
"password_changed.title": "Contraseña editada",
"password_changed.message": "<p>La contraseña fue modificada con exito, por favor <a href=\"/login\">conectate de nuevo</a>.",
"wrong_reset_code.title": "Código de Reseteo Incorrecto",
"wrong_reset_code.message": "El código de reseteo ingresado no es correcto. Por favor intentalo de nuevo o <a href=\"/reset\">pide un nuevo código</a>.",
"new_password": "Nueva Contraseña",
"repeat_password": "Confirmar Contraseña",
"enter_email": "Por favor ingresa tu <strong>email</strong> y te enviaremos un email de como resetear tu cuenta.",
"password_reset_sent": "Resteo de contraseña enviado",
"invalid_email": "Email Invalido o no existe!"
}

View File

@@ -0,0 +1,42 @@
{
"topic": "Tema",
"topics": "Temas",
"no_topics_found": "No se encontraron temas!",
"profile": "Perfil",
"posted_by": "Publicado por",
"chat": "Chat",
"notify_me": "Seras notificado cuando haya nuevas respuestas en este tema",
"quote": "Citar",
"reply": "Responder",
"edit": "Editar",
"delete": "Borrar",
"banned": "banneado",
"link": "Link",
"thread_tools.title": "Herramientas del Tema",
"thread_tools.pin": "Poner Sticky",
"thread_tools.lock": "Cerrar Tema",
"thread_tools.move": "Mover Tema",
"thread_tools.delete": "Borrar Tema",
"load_categories": "Cargando Categorias",
"disabled_categories_note": "Las categorías deshabilidas estan en gris",
"confirm_move": "Mover",
"favourite": "Favorito",
"favourites": "Favoritos",
"favourites.not_logged_in.title": "No estas conectado :(",
"favourites.not_logged_in.message": "Por favor, conectate para agregar a favorito este post.",
"favourites.has_no_favourites": "No tienes favoritos, puedes agregar alguno y volver a verlos aqui!",
"posted_by": "Publicado por",
"loading": "Cargando",
"more_posts": "Más posts",
"move_topic": "Mover Tema",
"topic_will_be_moved_to": "Este tema sera movido a la categoría",
"reputation": "Reputación",
"posts": "Posts"
}

View File

@@ -0,0 +1,5 @@
{
"no_unread_topics": "No hay temas nuevos para leer.",
"mark_all_read": "Marcar todo como leeido",
"load_more": "Cargar más"
}

View File

@@ -0,0 +1,36 @@
{
"banned": "Banneado",
"offline": "Desconectado",
"email": "Email",
"fullname": "Nombre Completo",
"website": "Website",
"location": "Ubicación",
"age": "Edad",
"joined": "Registro",
"profile_views": "Visitas en su perfil",
"reputation": "Reputación",
"posts": "Posts",
"followers": "Seguidores",
"following": "Siguiendo",
"signature": "Firma",
"gravatar": "Gravatar",
"birthday": "Cumpleaños",
"change_picture": "Cambiar Foto",
"edit": "Editar",
"uploaded_picture": "Fotos Cargadas",
"upload_new_picture": "Cargar Nueva Foto",
"change_password": "Cambiar Contraseña",
"confirm_password": "Confirmar Contraseña",
"password": "Contraseña",
"upload_picture": "Cargar foto",
"upload_a_picture": "Cargar una foto",
"image_spec": "Solo puedes usar PNG, JPG, o GIF hasta 256kb.",
"settings": "Opciones",
"show_email": "Mostrar mi Email",
"has_no_follower": "Este miembro no tiene seguidores :(",
"follows_no_one": "Este miembro no sigue a nadie, que pena :("
}

View File

@@ -0,0 +1,9 @@
{
"latest_users": "Ultimos Miembros",
"top_posters": "Top Posteadores",
"most_reputation": "Mayor Reputación",
"online": "Conectados",
"search": "Buscar",
"enter_username": "Ingresa el nombre de usuario para buscar",
"load_more": "Cargar más"
}

View File

@@ -0,0 +1,14 @@
{
"new_topic_button": "Nouveau Sujet",
"no_topics": "<strong>Il n'y a aucun topic dans cette catégorie.</strong><br />Pourquoi ne pas en créer un?",
"sidebar.recent_replies": "Réponses Récentes",
"sidebar.active_participants": "Participants Actifs",
"sidebar.moderators": "Modérateurs",
"posts": "messages",
"views": "vues",
"posted": "posté",
"browsing": "naviguer",
"no_replies": "Personne n'a répondu",
"replied": "répondu",
"last_edited_by": "dernière édition par"
}

View File

@@ -0,0 +1,10 @@
{
"chat.chatting_with": "Chat avec <span id=\"chat-with-name\"></span>",
"chat.placeholder": "taper le message ici, presser entrer pour envoyer",
"chat.send": "Envoyer",
"stats.online": "Online",
"stats.users": "Utilisateurs",
"stats.topics": "Sujets",
"stats.posts": "Message",
"success": "succès"
}

View File

@@ -0,0 +1,31 @@
{
"home": "Accueil",
"search": "Recherche",
"buttons.close": "Fermer",
"403.title": "Accès Refusé",
"403.message": "Il semble que vous vous soyez retrouvé sur une page dont vous n'avez pas accès. Peut-être devriez vous <a href='/login'>essayez de vous connecter</a>?",
"404.title": "Introuvable",
"404.message": "Il semble que vous vous soyez retrouvé sur une page qui n'existe pas. Retourner à <a href='/'>l'accueil</a>.",
"500.title": "Erreur Interne.",
"500.message": "Oops! Il semblerait que quelque chose se soit mal passé!",
"register": "S'inscrire",
"login": "Connecter",
"logout": "Déconnection",
"logout.title": "Vous êtes maintenant déconnecté.",
"logout.message": "Vous vous êtes déconnecté de NodeBB avec succès",
"save_changes": "Enregistrer les changements",
"close": "Fermer",
"header.admin": "Admin",
"header.recent": "Récent",
"header.unread": "Non Lu",
"header.users": "Utilisateurs",
"header.search": "Recherche",
"header.profile": "Profile",
"notifications.loading": "Chargement des Notifications",
"chats.loading": "Chargement des Chats"
}

View File

@@ -0,0 +1,10 @@
{
"login": "Connexion",
"username": "Identifiant",
"password": "Mot de passe",
"remember_me": "Se souvenir de moi?",
"forgot_password": "Mot de passe oublié?",
"alternative_logins": "Connexion Alternative",
"failed_login_attempt": "Echèc d'authentification, veuillez réessayer.",
"login_successful": "Vous êtes maintenant connecté!"
}

View File

@@ -0,0 +1,9 @@
{
"title": "Notifications",
"back_to_home": "retour à NodeBB",
"mark_all_as_read": "Tout marquer comme lu",
"outgoing_link": "Lien Sortant",
"outgoing_link_message": "Vous quitter NodeBB",
"continue_to": "Continuer vers",
"return_to": "Retour vers"
}

View File

@@ -0,0 +1,5 @@
{
"day": "Jour",
"week": "Semaine",
"month": "Mois"
}

View File

@@ -0,0 +1,16 @@
{
"register": "S'inscrire",
"help.email": "Par défault, votre email est masqué du public.",
"help.username_restrictions": "Un identifiant unique entre %1 et %2 charactères. Les autres utilisateurs peuvent vous citer avec @<span id='yourUsername'>username</span>.",
"help.minimum_password_length": "Votre mot de passe doit avoir au moins %1 charactères.",
"email_address": "Adresse Email",
"email_address_placeholder": "Entrer l'addresse Email",
"username": "Nom d'utilisateur",
"username_placeholder": "Entré le Nom d'utilisateur",
"password": "Mot de passe",
"password_placeholder": "Entrer le Mot de passe",
"confirm_password": "Confirmer le Mot de passe",
"confirm_password_placeholder": "Confirmer le Mot de passe",
"register_now_button": "S'enregistrer maintenant",
"alternative_registration": "Enregistrement Alternatif"
}

View File

@@ -0,0 +1,13 @@
{
"reset_password": "Réinitialiser le Mot de passe",
"update_password": "Mettre à jour le Mot de passe",
"password_changed.title": "Mot de passe modifié",
"password_changed.message": "<p>Mot de passe réinitialisé avec succès, veuillez vous <a href=\"/login\">reconnecter</a>.",
"wrong_reset_code.title": "Code de Réinisialisation Incorrect",
"wrong_reset_code.message": "Le Code de Réinisialisation est Incorrect. Veillez réessayer, ou <a href=\"/reset\">demander un nouveau Code de Réinisialisation</a>.",
"new_password": "Nouveau Mot de passe",
"repeat_password": "Confirmer le Mot de passe",
"enter_email": "Veuillez entrer votre <strong>adresse email</strong> et vous recevrez un email avec les instruction pour réinitialiser votre compte.",
"password_reset_sent": "Réinitialisation de Mot de Passe Envoyée",
"invalid_email": "Email Invalide / L'Email n'existe pas!"
}

View File

@@ -0,0 +1,42 @@
{
"topic": "Sujet",
"topics": "Sujets",
"no_topics_found": "Aucun sujet trouvé!",
"profile": "Profile",
"posted_by": "Envoyé by",
"chat": "Chat",
"notify_me": "Être notifié des réponses dans ce sujet",
"quote": "Citer",
"reply": "Répondre",
"edit": "Editer",
"delete": "Supprimer",
"banned": "bannir",
"link": "Lien",
"thread_tools.title": "Outils du Fil",
"thread_tools.pin": "Epingler le fil",
"thread_tools.lock": "Verrouiller le fil",
"thread_tools.move": "Déplacer le fil",
"thread_tools.delete": "Supprimer le fil",
"load_categories": "Chargement des Categories",
"disabled_categories_note": "Les Catégories Désactivées sont grisées",
"confirm_move": "Déplacer",
"favourite": "Favoris",
"favourites": "Favoris",
"favourites.not_logged_in.title": "Non Connecté",
"favourites.not_logged_in.message": "Veuillez vous connecter avant de mettre ce message en Favoris",
"favourites.has_no_favourites": "Vous n'avez aucun Favoris, mettre en favoris des messages pour les voir apparaître ici!",
"posted_by": "posté par",
"loading": "Chargement",
"more_posts": "d'autres Messages",
"move_topic": "Déplacer le Sujet",
"topic_will_be_moved_to": "Ce sujet sera déplacé vers la catégorie",
"reputation": "réputation",
"posts": "messages"
}

View File

@@ -0,0 +1,5 @@
{
"no_unread_topics": "Aucun sujet non lu.",
"mark_all_read": "Marquer tout comme lu",
"load_more": "Charger la suite"
}

View File

@@ -0,0 +1,36 @@
{
"banned": "Banni",
"offline": "Hors-ligne",
"email": "email",
"fullname": "Nom",
"website": "Site Web",
"location": "Emplacement",
"age": "age",
"joined": "adhésion",
"profil_views": "vues du profil",
"reputation": "réputation",
"posts": "messages",
"followers": "suiveurs",
"following": "suivis",
"signature": "signature",
"gravatar": "gravatar",
"birthday": "anniversaire",
"change_picture": "changer d'image",
"edit": "editer",
"uploaded_picture": "images uploadées",
"upload_new_picture": "uploader une nouvelle image",
"change_password": "chnger le mot de passe",
"confirm_password": "confirmer le mot de passe",
"password": "mot de passe",
"upload_picture": "Uploader un image",
"upload_a_picture": "Uploader un image",
"image_spec": "Vous pouvez uploader seulement des fichiers de types PNG, JPG, ou GIF en dessous de 256kb.",
"settings": "paramètres",
"show_my_email": "montrer mon email",
"has_no_follower": "Cet utilisateur n'a aucun suiver :(",
"follows_no_one": "Cet utilisateur ne suit personne :("
}

View File

@@ -0,0 +1,9 @@
{
"latest_users": "Derniers Utilisateurs",
"top_posters": "Meilleurs Publieur",
"most_reputation": "Meilleur Réputation",
"online": "En Ligne",
"search": "Rechercher",
"enter_username": "Entrer un nom d'utilisateur pour rechercher",
"load_more": "Charger la suite"
}

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -25,20 +25,24 @@ var ajaxify = {};
window.onpopstate = function (event) { window.onpopstate = function (event) {
// "quiet": If set to true, will not call pushState // "quiet": If set to true, will not call pushState
if (event !== null && event.state && event.state.url !== undefined) { if (event !== null && event.state && event.state.url !== undefined) {
ajaxify.go(event.state.url, null, null, true); ajaxify.go(event.state.url, null, true);
} }
}; };
var pagination; var pagination, paginator_bar;
ajaxify.go = function (url, callback, template, quiet) { ajaxify.currentPage = null;
ajaxify.go = function (url, callback, quiet) {
// start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs // start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs
$(window).off('scroll'); $(window).off('scroll');
app.enterRoom('global'); app.enterRoom('global');
pagination = pagination || document.getElementById('pagination'); pagination = pagination || document.getElementById('pagination');
paginator_bar = pagination ? document.body.querySelector('.progress-container') : undefined;
if (pagination) { if (pagination) {
pagination.parentNode.style.display = 'none'; pagination.parentNode.style.display = 'none';
paginator_bar.style.display = 'none';
} }
window.onscroll = null; window.onscroll = null;
@@ -67,30 +71,30 @@ var ajaxify = {};
} }
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) { if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
//if (quiet !== true) { ajaxify.currentPage = tpl_url;
if (window.history && window.history.pushState) {
window.history.pushState({
url: url
}, url, RELATIVE_PATH + '/' + url);
$.ajax(RELATIVE_PATH + '/plugins/fireHook', { if (window.history && window.history.pushState) {
type: 'PUT', window.history[!quiet ? 'pushState' : 'replaceState']({
data: { url: url
_csrf: $('#csrf_token').val(), }, url, RELATIVE_PATH + '/' + url);
hook: 'page.load',
args: { $.ajax(RELATIVE_PATH + '/plugins/fireHook', {
template: tpl_url, type: 'PUT',
url: url, data: {
uid: app.uid _csrf: $('#csrf_token').val(),
} hook: 'page.load',
args: {
template: tpl_url,
url: url,
uid: app.uid
} }
}); }
} });
//} }
translator.load(tpl_url); translator.load(tpl_url);
jQuery('#footer, #content').fadeOut(100); jQuery('#footer, #content').removeClass('hide').addClass('ajaxifying');
templates.flush(); templates.flush();
templates.load_template(function () { templates.load_template(function () {
@@ -107,21 +111,21 @@ var ajaxify = {};
app.processPage(); app.processPage();
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () { jQuery('#content, #footer').stop(true, true).removeClass('ajaxifying');
if (window.location.hash) {
hash = window.location.hash;
}
if (hash) { if (window.location.hash) {
require(['forum/topic'], function(topic) { hash = window.location.hash;
topic.scrollToPost(hash.substr(1)) }
});
}
});
utils.refreshTitle(url); if (hash) {
require(['forum/topic'], function(topic) {
topic.scrollToPost(hash.substr(1));
});
}
}, url, template); app.refreshTitle(url);
}, url);
return true; return true;
} }
@@ -129,6 +133,10 @@ var ajaxify = {};
return false; return false;
}; };
ajaxify.refresh = function() {
ajaxify.go(ajaxify.currentPage);
};
$('document').ready(function () { $('document').ready(function () {
if (!window.history || !window.history.pushState) { if (!window.history || !window.history.pushState) {
return; // no ajaxification for old browsers return; // no ajaxification for old browsers
@@ -154,7 +162,7 @@ var ajaxify = {};
return; return;
} }
if (!e.ctrlKey && e.which === 1) { if ((!e.ctrlKey && !e.shiftKey) && e.which === 1) {
if (this.host === window.location.host) { if (this.host === window.location.host) {
// Internal link // Internal link
var url = this.href.replace(rootUrl + '/', ''); var url = this.href.replace(rootUrl + '/', '');

View File

@@ -1,16 +1,16 @@
var socket, var socket,
config, config,
app = { app = {
'username': null, "username": null,
'uid': null "uid": null,
"isFocused": true,
"currentRoom": null
}; };
(function () { (function () {
var showWelcomeMessage = false; var showWelcomeMessage = false;
app.loadConfig = function() { app.loadConfig = function() {
$.ajax({ $.ajax({
url: RELATIVE_PATH + '/api/config', url: RELATIVE_PATH + '/api/config',
success: function (data) { success: function (data) {
@@ -47,11 +47,40 @@ var socket,
socket.on('connect', function (data) { socket.on('connect', function (data) {
if (reconnecting) { if (reconnecting) {
reconnectEl.html('<i class="icon-ok"></i> Connected!'); reconnectEl.tooltip('destroy');
reconnectEl.html('<i class="fa fa-check"></i>');
reconnecting = false; reconnecting = false;
// Rejoin room that was left when we disconnected
var url_parts = document.location.pathname.slice(RELATIVE_PATH.length).split('/').slice(1),
room;
switch(url_parts[0]) {
case 'user':
room = 'user/' + templates.get('theirid');
case 'topic':
room = 'topic_' + url_parts[1];
break;
case 'category':
room = 'category_' + url_parts[1];
break;
case 'recent': // intentional fall-through
case 'unread':
room = 'recent_posts';
break;
case 'admin':
room = 'admin';
break;
default:
room = 'global';
break;
}
app.enterRoom(room, true);
socket.emit('reconnected');
setTimeout(function() { setTimeout(function() {
reconnectEl.removeClass('active'); reconnectEl.removeClass('active').addClass("hide");
}, 3000); }, 3000);
} }
@@ -74,8 +103,12 @@ var socket,
if (!reconnectEl) reconnectEl = $('#reconnect'); if (!reconnectEl) reconnectEl = $('#reconnect');
reconnecting = true; reconnecting = true;
reconnectEl.addClass('active'); if (!reconnectEl.hasClass('active')) reconnectEl.html('<i class="fa fa-spinner fa-spin"></i>');
reconnectEl.html('<i class="icon-spinner icon-spin"></i> Reconnecting...'); reconnectEl.addClass('active').removeClass("hide");
reconnectEl.tooltip({
placement: 'bottom'
});
}); });
socket.on('api:user.get_online_users', function (users) { socket.on('api:user.get_online_users', function (users) {
@@ -88,10 +121,10 @@ var socket,
if (uid && jQuery.inArray(uid, users) !== -1) { if (uid && jQuery.inArray(uid, users) !== -1) {
el.find('i').remove(); el.find('i').remove();
el.prepend('<i class="icon-circle"></i>'); el.prepend('<i class="fa fa-circle"></i>');
} else { } else {
el.find('i').remove(); el.find('i').remove();
el.prepend('<i class="icon-circle-blank"></i>'); el.prepend('<i class="fa fa-circle-o"></i>');
} }
el.processed = true; el.processed = true;
@@ -130,31 +163,20 @@ var socket,
}, },
async: false async: false
}); });
} };
app.logout = function() { app.logout = function() {
$.post(RELATIVE_PATH + '/logout', { $.post(RELATIVE_PATH + '/logout', {
_csrf: $('#csrf_token').val() _csrf: $('#csrf_token').val()
}, function() { }, function() {
window.location.reload(false); window.location.href = RELATIVE_PATH + '/';
}); });
} };
// takes a string like 1000 and returns 1,000 // takes a string like 1000 and returns 1,000
app.addCommas = function (text) { app.addCommas = function (text) {
return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"); return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
} };
// Willingly stolen from: http://phpjs.org/functions/strip_tags/
app.strip_tags = function (input, allowed) {
allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
});
}
// use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance // use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance
// type : error, success, info, warning/notify // type : error, success, info, warning/notify
@@ -217,7 +239,7 @@ var socket,
}); });
} }
} }
} };
app.alertSuccess = function (message, timeout) { app.alertSuccess = function (message, timeout) {
if (!timeout) if (!timeout)
@@ -229,7 +251,7 @@ var socket,
type: 'success', type: 'success',
timeout: timeout timeout: timeout
}); });
} };
app.alertError = function (message, timeout) { app.alertError = function (message, timeout) {
if (!timeout) if (!timeout)
@@ -241,12 +263,11 @@ var socket,
type: 'danger', type: 'danger',
timeout: timeout timeout: timeout
}); });
} };
app.currentRoom = null; app.enterRoom = function (room, force) {
app.enterRoom = function (room) {
if (socket) { if (socket) {
if (app.currentRoom === room) { if (app.currentRoom === room && !force) {
return; return;
} }
@@ -267,7 +288,7 @@ var socket,
}); });
socket.emit('api:user.get_online_users', uids); socket.emit('api:user.get_online_users', uids);
} };
function highlightNavigationLink() { function highlightNavigationLink() {
var path = window.location.pathname, var path = window.location.pathname,
@@ -286,7 +307,7 @@ var socket,
} }
}); });
} }
} };
app.createUserTooltips = function() { app.createUserTooltips = function() {
$('img[title].teaser-pic,img[title].user-img').each(function() { $('img[title].teaser-pic,img[title].user-img').each(function() {
@@ -295,7 +316,13 @@ var socket,
title: $(this).attr('title') title: $(this).attr('title')
}); });
}); });
} };
app.makeNumbersHumanReadable = function(elements) {
elements.each(function() {
$(this).html(utils.makeNumberHumanReadable($(this).attr('title')));
});
};
app.processPage = function () { app.processPage = function () {
app.populateOnlineUsers(); app.populateOnlineUsers();
@@ -305,12 +332,14 @@ var socket,
$('span.timeago').timeago(); $('span.timeago').timeago();
$('.post-content img').addClass('img-responsive'); $('.post-content img').addClass('img-responsive');
app.makeNumbersHumanReadable($('.human-readable-number'));
app.createUserTooltips(); app.createUserTooltips();
setTimeout(function () { setTimeout(function () {
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes. window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
}, 100); }, 100);
} };
app.showLoginMessage = function () { app.showLoginMessage = function () {
function showAlert() { function showAlert() {
@@ -330,13 +359,13 @@ var socket,
showAlert(); showAlert();
} }
} }
} };
app.addCommasToNumbers = function () { app.addCommasToNumbers = function () {
$('.formatted-number').each(function (index, element) { $('.formatted-number').each(function (index, element) {
$(element).html(app.addCommas($(element).html())); $(element).html(app.addCommas($(element).html()));
}); });
} };
app.openChat = function (username, touid) { app.openChat = function (username, touid) {
if (username === app.username) { if (username === app.username) {
@@ -371,8 +400,8 @@ var socket,
chat.load(chatModal.attr('UUID')); chat.load(chatModal.attr('UUID'));
chat.center(chatModal); chat.center(chatModal);
}); });
} };
app.scrollToTop = function () { app.scrollToTop = function () {
$('body,html').animate({ $('body,html').animate({
scrollTop: 0 scrollTop: 0
@@ -383,19 +412,66 @@ var socket,
$('body,html').animate({ $('body,html').animate({
scrollTop: $('html').height() - 100 scrollTop: $('html').height() - 100
}); });
} };
var titleObj = {
active: false,
interval: undefined,
titles: []
};
app.alternatingTitle = function (title) {
if (typeof title !== 'string') return;
if (title.length > 0) {
titleObj.titles[1] = title;
if (titleObj.interval) {
clearInterval(titleObj.interval);
}
titleObj.interval = setInterval(function() {
window.document.title = titleObj.titles[titleObj.titles.indexOf(window.document.title) ^ 1];
}, 2000);
} else {
if (titleObj.interval) {
clearInterval(titleObj.interval);
}
if (titleObj.titles[0]) window.document.title = titleObj.titles[0];
}
};
app.refreshTitle = function(url) {
if (!url) {
var a = document.createElement('a');
a.href = document.location;
url = a.pathname.slice(1);
}
socket.emit('api:meta.buildTitle', url, function(title, numNotifications) {
titleObj.titles[0] = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title;
app.alternatingTitle('');
});
};
jQuery('document').ready(function () { jQuery('document').ready(function () {
$('#search-form').on('submit', function () { $('#search-form').on('submit', function () {
var input = $(this).find('input'); var input = $(this).find('input');
ajaxify.go("search/" + input.val(), null, "search"); ajaxify.go("search/" + input.val());
input.val(''); input.val('');
return false; return false;
}); });
$(window).blur(function(){
app.isFocused = false;
});
$(window).focus(function(){
app.isFocused = true;
app.alternatingTitle('');
});
}); });
showWelcomeMessage = location.href.indexOf('loggedin') !== -1; showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
app.loadConfig(); app.loadConfig();
app.alternatingTitle('');
}()); }());

View File

@@ -17,18 +17,21 @@ define(['forum/accountheader'], function(header) {
var followBtn = $('#follow-btn'); var followBtn = $('#follow-btn');
var unfollowBtn = $('#unfollow-btn'); var unfollowBtn = $('#unfollow-btn');
var chatBtn = $('#chat-btn');
if (yourid !== theirid && yourid !== "0") { if (yourid !== theirid && yourid !== "0") {
if (isFollowing) { if (isFollowing) {
followBtn.hide(); followBtn.addClass('hide');
unfollowBtn.show(); unfollowBtn.removeClass('hide');
} else { } else {
followBtn.show(); followBtn.removeClass('hide');
unfollowBtn.hide(); unfollowBtn.addClass('hide');
} }
chatBtn.removeClass('hide');
} else { } else {
followBtn.hide(); followBtn.addClass('hide');
unfollowBtn.hide(); unfollowBtn.addClass('hide');
chatBtn.addClass('hide');
} }
followBtn.on('click', function() { followBtn.on('click', function() {
@@ -36,8 +39,8 @@ define(['forum/accountheader'], function(header) {
uid: theirid uid: theirid
}, function(success) { }, function(success) {
if (success) { if (success) {
followBtn.hide(); followBtn.addClass('hide');
unfollowBtn.show(); unfollowBtn.removeClass('hide');
app.alertSuccess('You are now following ' + username + '!'); app.alertSuccess('You are now following ' + username + '!');
} else { } else {
app.alertError('There was an error following' + username + '!'); app.alertError('There was an error following' + username + '!');
@@ -51,8 +54,8 @@ define(['forum/accountheader'], function(header) {
uid: theirid uid: theirid
}, function(success) { }, function(success) {
if (success) { if (success) {
followBtn.show(); followBtn.removeClass('hide');
unfollowBtn.hide(); unfollowBtn.addClass('hide');
app.alertSuccess('You are no longer following ' + username + '!'); app.alertSuccess('You are no longer following ' + username + '!');
} else { } else {
app.alertError('There was an error unfollowing ' + username + '!'); app.alertError('There was an error unfollowing ' + username + '!');
@@ -61,6 +64,10 @@ define(['forum/accountheader'], function(header) {
return false; return false;
}); });
chatBtn.on('click', function() {
app.openChat(username, theirid);
});
$('.user-recent-posts .topic-row').on('click', function() { $('.user-recent-posts .topic-row').on('click', function() {
ajaxify.go($(this).attr('topic-url')); ajaxify.go($(this).attr('topic-url'));
}); });
@@ -72,6 +79,7 @@ define(['forum/accountheader'], function(header) {
socket.on('event:new_post', function(data) { socket.on('event:new_post', function(data) {
var html = templates.prepare(templates['account'].blocks['posts']).parse(data); var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
$('.user-recent-posts').prepend(html); $('.user-recent-posts').prepend(html);
$('.user-recent-posts span.timeago').timeago();
}); });
}); });
@@ -82,12 +90,12 @@ define(['forum/accountheader'], function(header) {
if (data.online) { if (data.online) {
onlineStatus.find('span span').text('online'); onlineStatus.find('span span').text('online');
onlineStatus.find('i').attr('class', 'icon-circle'); onlineStatus.find('i').attr('class', 'fa fa-circle');
} else { } else {
onlineStatus.find('span span').text('offline'); onlineStatus.find('span span').text('offline');
onlineStatus.find('i').attr('class', 'icon-circle-blank'); onlineStatus.find('i').attr('class', 'fa fa-circle-o');
} }
}; };
return Account; return Account;
}); });

View File

@@ -1,11 +1,13 @@
define(['forum/accountheader', 'uploader'], function(header, uploader) { define(['forum/accountheader', 'uploader'], function(header, uploader) {
var AccountEdit = {}; var AccountEdit = {},
gravatarPicture = '',
uploadedPicture = '';
AccountEdit.init = function() { AccountEdit.init = function() {
header.init(); header.init();
var gravatarPicture = templates.get('gravatarpicture'); gravatarPicture = templates.get('gravatarpicture');
var uploadedPicture = templates.get('uploadedpicture'); uploadedPicture = templates.get('uploadedpicture');
var selectedImageType = ''; var selectedImageType = '';
@@ -39,6 +41,17 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
return false; return false;
}); });
function getSignatureCharsLeft() {
return '(' + $('#inputSignature').val().length + '/' + config.maximumSignatureLength + ')';
}
$('#signatureCharCountLeft').html(getSignatureCharsLeft());
$('#inputSignature').on('keyup change', function(ev) {
$('#signatureCharCountLeft').html(getSignatureCharsLeft());
});
$('#changePictureBtn').on('click', function() { $('#changePictureBtn').on('click', function() {
selectedImageType = ''; selectedImageType = '';
AccountEdit.updateImages(); AccountEdit.updateImages();
@@ -50,14 +63,14 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
}); });
$('#gravatar-box').on('click', function() { $('#gravatar-box').on('click', function() {
$('#gravatar-box .icon-ok').show(); $('#gravatar-box .fa-check').show();
$('#uploaded-box .icon-ok').hide(); $('#uploaded-box .fa-check').hide();
selectedImageType = 'gravatar'; selectedImageType = 'gravatar';
}); });
$('#uploaded-box').on('click', function() { $('#uploaded-box').on('click', function() {
$('#gravatar-box .icon-ok').hide(); $('#gravatar-box .fa-check').hide();
$('#uploaded-box .icon-ok').show(); $('#uploaded-box .fa-check').show();
selectedImageType = 'uploaded'; selectedImageType = 'uploaded';
}); });
@@ -82,7 +95,9 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
$('#uploadPictureBtn').on('click', function() { $('#uploadPictureBtn').on('click', function() {
$('#change-picture-modal').modal('hide'); $('#change-picture-modal').modal('hide');
uploader.open(config.relative_path + '/user/uploadpicture', function(imageUrlOnServer) { uploader.open(RELATIVE_PATH + '/user/uploadpicture', {}, function(imageUrlOnServer) {
imageUrlOnServer = imageUrlOnServer + '?' + new Date().getTime();
$('#user-current-picture').attr('src', imageUrlOnServer); $('#user-current-picture').attr('src', imageUrlOnServer);
$('#user-uploaded-picture').attr('src', imageUrlOnServer); $('#user-uploaded-picture').attr('src', imageUrlOnServer);
@@ -97,6 +112,22 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
return false; return false;
}); });
function showError(element, msg) {
element.html(msg);
element.parent()
.removeClass('alert-success')
.addClass('alert-danger');
element.show();
validationError = true;
}
function showSuccess(element, msg) {
element.html(msg);
element.parent()
.removeClass('alert-danger')
.addClass('alert-success');
element.show();
}
(function handlePasswordChange() { (function handlePasswordChange() {
var currentPassword = $('#inputCurrentPassword'); var currentPassword = $('#inputCurrentPassword');
@@ -106,42 +137,29 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
var password_confirm = $('#inputNewPasswordAgain'); var password_confirm = $('#inputNewPasswordAgain');
var passwordvalid = false; var passwordvalid = false;
var passwordsmatch = false; var passwordsmatch = false;
var successIcon = '<i class="fa fa-check"></i>';
function onPasswordChanged() { function onPasswordChanged() {
passwordvalid = utils.isPasswordValid(password.val()); passwordvalid = utils.isPasswordValid(password.val());
if (password.val().length < config.minimumPasswordLength) { if (password.val().length < config.minimumPasswordLength) {
password_notify.html('Password too short'); showError(password_notify, 'Password too short!');
password_notify.attr('class', 'alert alert-danger');
password_notify.removeClass('hide');
} else if (!passwordvalid) { } else if (!passwordvalid) {
password_notify.html('Invalid password'); showError(password_notify, 'Invalid password!');
password_notify.attr('class', 'alert alert-danger');
password_notify.removeClass('hide');
} else { } else {
password_notify.html('OK!'); showSuccess(password_notify, successIcon);
password_notify.attr('class', 'alert alert-success');
password_notify.removeClass('hide');
} }
onPasswordConfirmChanged();
} }
function onPasswordConfirmChanged() { function onPasswordConfirmChanged() {
if (password_notify.hasClass('alert-danger') || !password_confirm.val()) { if(password.val()) {
password_confirm_notify.addClass('hide'); if (password.val() !== password_confirm.val()) {
return; showError(password_confirm_notify, 'Passwords must match!')
} passwordsmatch = false;
if (password.val() !== password_confirm.val()) { } else {
password_confirm_notify.html('Passwords must match!'); showSuccess(password_confirm_notify, successIcon);
password_confirm_notify.attr('class', 'alert alert-danger'); passwordsmatch = true;
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;
} }
} }
@@ -159,8 +177,6 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
currentPassword.val(''); currentPassword.val('');
password.val(''); password.val('');
password_confirm.val(''); password_confirm.val('');
password_notify.addClass('hide');
password_confirm_notify.addClass('hide');
passwordsmatch = false; passwordsmatch = false;
passwordvalid = false; passwordvalid = false;
@@ -194,31 +210,33 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
AccountEdit.updateImages = function() { AccountEdit.updateImages = function() {
var currentPicture = $('#user-current-picture').attr('src'); var currentPicture = $('#user-current-picture').attr('src');
var gravatarPicture = templates.get('gravatarpicture');
var uploadedPicture = templates.get('uploadedpicture');
if (gravatarPicture) { if (gravatarPicture) {
$('#user-gravatar-picture').attr('src', gravatarPicture); $('#user-gravatar-picture').attr('src', gravatarPicture);
$('#gravatar-box').show(); $('#gravatar-box').show();
} else } else {
$('#gravatar-box').hide(); $('#gravatar-box').hide();
}
if (uploadedPicture) { if (uploadedPicture) {
$('#user-uploaded-picture').attr('src', uploadedPicture); $('#user-uploaded-picture').attr('src', uploadedPicture);
$('#uploaded-box').show(); $('#uploaded-box').show();
} else } else {
$('#uploaded-box').hide(); $('#uploaded-box').hide();
}
if (currentPicture == gravatarPicture) if (currentPicture == gravatarPicture) {
$('#gravatar-box .icon-ok').show(); $('#gravatar-box .fa-check').show();
else } else {
$('#gravatar-box .icon-ok').hide(); $('#gravatar-box .fa-check').hide();
}
if (currentPicture == uploadedPicture) if (currentPicture == uploadedPicture) {
$('#uploaded-box .icon-ok').show(); $('#uploaded-box .fa-check').show();
else } else {
$('#uploaded-box .icon-ok').hide(); $('#uploaded-box .fa-check').hide();
}
} }
return AccountEdit; return AccountEdit;

View File

@@ -10,8 +10,8 @@ define(['forum/accountheader'], function(header) {
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0 showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
}; };
socket.emit('api:user.saveSettings', settings, function(success) { socket.emit('api:user.saveSettings', settings, function(err) {
if (success) { if (!err) {
app.alertSuccess('Settings saved!'); app.alertSuccess('Settings saved!');
} else { } else {
app.alertError('There was an error saving settings!'); app.alertError('There was an error saving settings!');

View File

@@ -1,4 +1,4 @@
define(function() { define(['uploader'], function(uploader) {
var Categories = {}; var Categories = {};
Categories.init = function() { Categories.init = function() {
@@ -18,27 +18,31 @@ define(function() {
} }
function select_icon(el) { function select_icon(el) {
var selected = el.attr('class').replace(' icon-2x', ''); var selected = el.attr('class').replace(' fa-2x', '');
jQuery('#icons .selected').removeClass('selected'); $('#icons .selected').removeClass('selected');
if (selected) if (selected)
jQuery('#icons .' + selected).parent().addClass('selected'); $('#icons .' + selected).parent().addClass('selected');
bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) { bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) {
if (confirm) { if (confirm) {
var iconClass = jQuery('.bootbox .selected').children(':first').attr('class'); var iconClass = $('.bootbox .selected').children(':first').attr('class');
el.attr('class', iconClass + ' icon-2x');
el.val(iconClass); el.attr('class', iconClass + ' fa-2x');
el.attr('value', iconClass);
// remove the 'fa ' from the class name, just need the icon name itself
var categoryIconClass = iconClass.replace('fa ', '');
el.val(categoryIconClass);
el.attr('value', categoryIconClass);
modified(el); modified(el);
} }
}); });
setTimeout(function() { //bootbox was rewritten for BS3 and I had to add this timeout for the previous code to work. TODO: to look into 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() { $('.bootbox .col-md-3').on('click', function() {
jQuery('.bootbox .selected').removeClass('selected'); $('.bootbox .selected').removeClass('selected');
jQuery(this).addClass('selected'); $(this).addClass('selected');
}); });
}, 500); }, 500);
} }
@@ -58,13 +62,13 @@ define(function() {
} }
} }
jQuery('#entry-container').sortable({ $('#entry-container').sortable({
stop: function( event, ui ) { stop: function( event, ui ) {
updateCategoryOrders(); updateCategoryOrders();
} }
}); });
jQuery('.blockclass').each(function() { $('.blockclass').each(function() {
jQuery(this).val(this.getAttribute('data-value')); $(this).val(this.getAttribute('data-value'));
}); });
@@ -77,7 +81,9 @@ define(function() {
name: $('#inputName').val(), name: $('#inputName').val(),
description: $('#inputDescription').val(), description: $('#inputDescription').val(),
icon: $('#new-category-modal i').val(), icon: $('#new-category-modal i').val(),
blockclass: $('#inputBlockclass').val() bgColor: '#0059b2',
color: '#fff',
order: $('.admin-categories #entry-container').children().length + 1
}; };
socket.emit('api:admin.categories.create', category, function(err, data) { socket.emit('api:admin.categories.create', category, function(err, data) {
@@ -100,54 +106,50 @@ define(function() {
}); });
} }
jQuery('document').ready(function() { $('document').ready(function() {
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
active = parts[parts.length - 1]; active = parts[parts.length - 1];
jQuery('.nav-pills li').removeClass('active'); $('.nav-pills li').removeClass('active');
jQuery('.nav-pills li a').each(function() { $('.nav-pills li a').each(function() {
if (this.getAttribute('href').match(active)) { if (this.getAttribute('href').match(active)) {
jQuery(this.parentNode).addClass('active'); $(this.parentNode).addClass('active');
return false; return false;
} }
}); });
jQuery('#save').on('click', save); $('#save').on('click', save);
jQuery('#addNew').on('click', showCreateCategoryModal); $('#addNew').on('click', showCreateCategoryModal);
jQuery('#create-category-btn').on('click', createNewCategory); $('#create-category-btn').on('click', createNewCategory);
jQuery('#entry-container').on('click', '.icon', function(ev) { $('#entry-container').on('click', '.icon', function(ev) {
select_icon($(this).find('i')); select_icon($(this).find('i'));
}); });
jQuery('#new-category-modal').on('click', '.icon', function(ev) { $('#new-category-modal').on('click', '.icon', function(ev) {
select_icon($(this).find('i')); select_icon($(this).find('i'));
}); });
jQuery('.blockclass').on('change', function(ev) { $('.admin-categories form input').on('change', function(ev) {
update_blockclass(ev.target);
});
jQuery('.category_name, .category_description, .blockclass .category_bgColor').on('change', function(ev) {
modified(ev.target); modified(ev.target);
}); });
jQuery('.entry-row button').each(function(index, element) { $('.dropdown li[data-disabled]').each(function(index, element) {
var disabled = $(element).attr('data-disabled'); var disabled = $(element).attr('data-disabled');
if (disabled == "0" || disabled == "") if (disabled == "0" || disabled == "") {
$(element).html('Disable'); $(element).html('<a href="#"><i class="fa fa-power-off"></i> Disable</a>');
else } else {
$(element).html('Enable'); $(element).html('<a href="#"><i class="fa fa-power-off"></i> Enable</a>');
}
}); });
jQuery('#entry-container').on('click', '.disable-btn', function(ev) { $('.dropdown').on('click', '[data-disabled]', function(ev) {
var btn = jQuery(this); var btn = $(this);
var categoryRow = btn.parents('li'); var categoryRow = btn.parents('li');
var cid = categoryRow.attr('data-cid'); var cid = categoryRow.attr('data-cid');
var disabled = btn.html() == "Disable" ? "1" : "0"; var disabled = this.getAttribute('data-disabled') === '0' ? '1' : '0';
categoryRow.remove(); categoryRow.remove();
modified_categories[cid] = modified_categories[cid] || {}; modified_categories[cid] = modified_categories[cid] || {};
modified_categories[cid]['disabled'] = disabled; modified_categories[cid]['disabled'] = disabled;
@@ -159,19 +161,190 @@ define(function() {
// Colour Picker // Colour Picker
$('[data-name="bgColor"], [data-name="color"]').each(function(idx, inputEl) { $('[data-name="bgColor"], [data-name="color"]').each(function(idx, inputEl) {
var jinputEl = $(this), var jinputEl = $(this),
parentEl = jinputEl.parents('[data-cid]'); previewEl = jinputEl.parents('[data-cid]').find('.preview-box');
jinputEl.ColorPicker({ jinputEl.ColorPicker({
color: this.value || '#000', color: this.value || '#000',
onChange: function(hsb, hex) { onChange: function(hsb, hex) {
jinputEl.val('#' + hex); jinputEl.val('#' + hex);
if (inputEl.getAttribute('data-name') === 'bgColor') parentEl.css('background', '#' + hex); if (inputEl.getAttribute('data-name') === 'bgColor') previewEl.css('background', '#' + hex);
else if (inputEl.getAttribute('data-name') === 'color') parentEl.css('color', '#' + hex); else if (inputEl.getAttribute('data-name') === 'color') previewEl.css('color', '#' + hex);
modified(inputEl); modified(inputEl);
} }
}); });
}); });
// Permissions modal
$('.permissions').on('click', function() {
var cid = $(this).parents('li[data-cid]').attr('data-cid');
Categories.launchPermissionsModal(cid);
});
$('.upload-button').on('click', function() {
var inputEl = this;
var cid = $(this).parents('li[data-cid]').attr('data-cid');
uploader.open(RELATIVE_PATH + '/admin/category/uploadpicture', {cid:cid}, function(imageUrlOnServer) {
inputEl.value = imageUrlOnServer;
$(inputEl).parents('li[data-cid]').find('.preview-box').css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')');
modified(inputEl);
});
});
$('.admin-categories').delegate('.delete-image', 'click', function() {
var parent = $(this).parents('li[data-cid]'),
inputEl = parent.find('.upload-button'),
preview = parent.find('.preview-box'),
bgColor = parent.find('.category_bgColor').val();
inputEl.value = '';
modified(inputEl);
preview.css('background', bgColor);
$(this).hide();
});
});
};
Categories.launchPermissionsModal = function(cid) {
var modal = $('#category-permissions-modal'),
searchEl = modal.find('#permission-search'),
resultsEl = modal.find('.search-results'),
groupsResultsEl = modal.find('.groups-results'),
searchDelay;
searchEl.off().on('keyup', function() {
var searchEl = this,
resultsFrag = document.createDocumentFragment(),
liEl = document.createElement('li');
clearTimeout(searchDelay);
searchDelay = setTimeout(function() {
socket.emit('api:admin.categories.search', searchEl.value, cid, function(err, results) {
var numResults = results.length,
resultObj;
for(var x=0;x<numResults;x++) {
resultObj = results[x];
liEl.setAttribute('data-uid', resultObj.uid);
liEl.innerHTML = '<div class="pull-right">' +
'<div class="btn-group">' +
'<button type="button" data-priv="+r" class="btn btn-default' + (resultObj.privileges['+r'] ? ' active' : '') + '">Read</button>' +
'<button type="button" data-priv="+w" class="btn btn-default' + (resultObj.privileges['+w'] ? ' active' : '') + '">Write</button>' +
'</div>' +
'</div>' +
'<img src="' + resultObj.picture + '" /> ' + resultObj.username;
resultsFrag.appendChild(liEl.cloneNode(true));
}
resultsEl.html(resultsFrag);
});
}, 250);
});
Categories.refreshPrivilegeList(cid);
resultsEl.off().on('click', '[data-priv]', function(e) {
var btnEl = $(this),
uid = btnEl.parents('li[data-uid]').attr('data-uid'),
privilege = this.getAttribute('data-priv');
e.preventDefault();
socket.emit('api:admin.categories.setPrivilege', cid, uid, privilege, !btnEl.hasClass('active'), function(err, privileges) {
btnEl.toggleClass('active', privileges[privilege]);
Categories.refreshPrivilegeList(cid);
});
});
modal.off().on('click', '.members li > img', function() {
searchEl.val(this.getAttribute('title'));
searchEl.keyup();
});
// User Groups and privileges
socket.emit('api:admin.categories.groupsearch', cid, function(err, results) {
var groupsFrag = document.createDocumentFragment(),
liEl = document.createElement('li');
var numResults = results.length,
resultObj;
for(var x=0;x<numResults;x++) {
resultObj = results[x];
liEl.setAttribute('data-gid', resultObj.gid);
liEl.innerHTML = '<div class="pull-right">' +
'<div class="btn-group">' +
'<button type="button" data-gpriv="+gr" class="btn btn-default' + (resultObj.privileges['+gr'] ? ' active' : '') + '">Read</button>' +
'<button type="button" data-gpriv="+gw" class="btn btn-default' + (resultObj.privileges['+gw'] ? ' active' : '') + '">Write</button>' +
'</div>' +
'</div>' +
' '+resultObj.name;
groupsFrag.appendChild(liEl.cloneNode(true));
}
groupsResultsEl.html(groupsFrag);
});
groupsResultsEl.off().on('click', '[data-gpriv]', function(e) {
var btnEl = $(this),
gid = btnEl.parents('li[data-gid]').attr('data-gid'),
privilege = this.getAttribute('data-gpriv');
e.preventDefault();
socket.emit('api:admin.categories.setGroupPrivilege', cid, gid, privilege, !btnEl.hasClass('active'), function(err, privileges) {
btnEl.toggleClass('active', privileges[privilege]);
});
})
modal.modal();
};
Categories.refreshPrivilegeList = function (cid) {
var modalEl = $('#category-permissions-modal'),
readMembers = modalEl.find('#category-permissions-read'),
writeMembers = modalEl.find('#category-permissions-write');
socket.emit('api:admin.categories.getPrivilegeSettings', cid, function(err, privilegeList) {
var readLength = privilegeList['+r'].length,
writeLength = privilegeList['+w'].length,
readFrag = document.createDocumentFragment(),
writeFrag = document.createDocumentFragment(),
liEl = document.createElement('li'),
x, userObj;
if (readLength > 0) {
for(x=0;x<readLength;x++) {
userObj = privilegeList['+r'][x];
liEl.setAttribute('data-uid', userObj.uid);
liEl.innerHTML = '<img src="' + userObj.picture + '" title="' + userObj.username + '" />';
readFrag.appendChild(liEl.cloneNode(true));
}
} else {
liEl.className = 'empty';
liEl.innerHTML = 'No users are in this list';
readFrag.appendChild(liEl.cloneNode(true));
}
if (writeLength > 0) {
for(x=0;x<writeLength;x++) {
userObj = privilegeList['+w'][x];
liEl.setAttribute('data-uid', userObj.uid);
liEl.innerHTML = '<img src="' + userObj.picture + '" title="' + userObj.username + '" />';
writeFrag.appendChild(liEl.cloneNode(true));
}
} else {
liEl.className = 'empty';
liEl.innerHTML = 'No users are in this list';
writeFrag.appendChild(liEl.cloneNode(true));
}
readMembers.html(readFrag);
writeMembers.html(writeFrag);
}); });
}; };
return Categories; return Categories;
}); });

View File

@@ -1,7 +1,7 @@
define(function() { define(function() {
var Admin = {}; var Admin = {};
Admin.init = function() { Admin.init = function() {
ajaxify.register_events(['api:get_all_rooms']); ajaxify.register_events(['api:get_all_rooms']);
socket.on('api:get_all_rooms', function(data) { socket.on('api:get_all_rooms', function(data) {
@@ -22,6 +22,14 @@ define(function() {
app.enterRoom('admin'); app.enterRoom('admin');
socket.emit('api:get_all_rooms'); socket.emit('api:get_all_rooms');
$('#logout-link').on('click', function() {
$.post(RELATIVE_PATH + '/logout', {
_csrf: $('#csrf_token').val()
}, function() {
window.location.href = RELATIVE_PATH + '/';
});
})
}; };
return Admin; return Admin;

View File

@@ -13,7 +13,7 @@ define(function() {
socket.on('api:admin.plugins.toggle', function(status) { socket.on('api:admin.plugins.toggle', function(status) {
pluginTgl = document.querySelector('.plugins li[data-plugin-id="' + status.id + '"] button'); pluginTgl = document.querySelector('.plugins li[data-plugin-id="' + status.id + '"] button');
pluginTgl.innerHTML = '<i class="icon-off"></i> ' + (status.active ? 'Dea' : 'A') + 'ctivate'; pluginTgl.innerHTML = '<i class="fa fa-power-off"></i> ' + (status.active ? 'Dea' : 'A') + 'ctivate';
app.alert({ app.alert({
alert_id: 'plugin_toggled_' + status.id, alert_id: 'plugin_toggled_' + status.id,

View File

@@ -32,12 +32,14 @@ define(['uploader'], function(uploader) {
break; break;
case 'checkbox': case 'checkbox':
fields[x].checked = app.config[key] === '1' ? true : false; fields[x].checked = parseInt(app.config[key], 10) === 1;
break; break;
} }
} }
} else if (fields[x].nodeName === 'TEXTAREA') { } else if (fields[x].nodeName === 'TEXTAREA') {
if (app.config[key]) fields[x].value = app.config[key]; if (app.config[key]) fields[x].value = app.config[key];
} else if (fields[x].nodeName === 'SELECT') {
if (app.config[key]) fields[x].value = app.config[key];
} }
} }
@@ -61,6 +63,8 @@ define(['uploader'], function(uploader) {
} }
} else if (fields[x].nodeName === 'TEXTAREA') { } else if (fields[x].nodeName === 'TEXTAREA') {
value = fields[x].value; value = fields[x].value;
} else if (fields[x].nodeName === 'SELECT') {
value = fields[x].value;
} }
socket.emit('api:config.set', { socket.emit('api:config.set', {
@@ -71,13 +75,20 @@ define(['uploader'], function(uploader) {
}); });
$('#uploadLogoBtn').on('click', function() { $('#uploadLogoBtn').on('click', function() {
uploader.open(RELATIVE_PATH + '/admin/uploadlogo', {}, function(image) {
uploader.open(config.relative_path + '/admin/uploadlogo', function(image) {
$('#logoUrl').val(image); $('#logoUrl').val(image);
}); });
uploader.hideAlerts(); uploader.hideAlerts();
}); });
$('#uploadFaviconBtn').on('click', function() {
uploader.open(RELATIVE_PATH + '/admin/uploadfavicon', {}, function(icon) {
$('#faviconUrl').val(icon);
});
uploader.hideAlerts();
});
}; };
Settings.remove = function(key) { Settings.remove = function(key) {

View File

@@ -46,11 +46,11 @@ define(function() {
if(!topics.length) { if(!topics.length) {
return; return;
} }
var lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid')); var lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics'; this.innerHTML = '<i class="fa fa-refresh fa-spin"></i> Retrieving topics';
socket.emit('api:admin.topics.getMore', { socket.emit('api:admin.topics.getMore', {
limit: 10, limit: 10,
after: lastTid after: lastTid

View File

@@ -19,7 +19,7 @@ define(function() {
return parent.attr('data-uid'); return parent.attr('data-uid');
} }
function updateUserButtons() { function updateUserBanButtons() {
jQuery('.ban-btn').each(function(index, element) { jQuery('.ban-btn').each(function(index, element) {
var banBtn = $(element); var banBtn = $(element);
var uid = getUID(banBtn); var uid = getUID(banBtn);
@@ -27,15 +27,37 @@ define(function() {
banBtn.addClass('disabled'); banBtn.addClass('disabled');
else if (isUserBanned(banBtn)) else if (isUserBanned(banBtn))
banBtn.addClass('btn-warning'); banBtn.addClass('btn-warning');
else if (!isUserAdmin(banBtn))
banBtn.removeClass('disabled');
else else
banBtn.removeClass('btn-warning'); banBtn.removeClass('btn-warning');
updateUserAdminButtons();
});
}
function updateUserAdminButtons() {
jQuery('.admin-btn').each(function(index, element) {
var adminBtn = $(element);
var uid = getUID(adminBtn);
if (isUserAdmin(adminBtn)) {
adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
if (uid === yourid) {
adminBtn.addClass('disabled');
}
}
else if (isUserBanned(adminBtn))
adminBtn.addClass('disabled');
else if (!isUserBanned(adminBtn))
adminBtn.removeClass('disabled');
else
adminBtn.removeClass('btn-warning');
}); });
} }
function initUsers() { function initUsers() {
updateUserBanButtons();
updateUserButtons(); updateUserAdminButtons();
$('#users-container').on('click', '.ban-btn', function() { $('#users-container').on('click', '.ban-btn', function() {
var banBtn = $(this); var banBtn = $(this);
@@ -49,12 +71,14 @@ define(function() {
socket.emit('api:admin.user.unbanUser', uid); socket.emit('api:admin.user.unbanUser', uid);
banBtn.removeClass('btn-warning'); banBtn.removeClass('btn-warning');
parent.attr('data-banned', 0); parent.attr('data-banned', 0);
updateUserAdminButtons();
} else { } else {
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) { bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) {
if (confirm) { if (confirm) {
socket.emit('api:admin.user.banUser', uid); socket.emit('api:admin.user.banUser', uid);
banBtn.addClass('btn-warning'); banBtn.addClass('btn-warning');
parent.attr('data-banned', 1); parent.attr('data-banned', 1);
updateUserAdminButtons();
} }
}); });
} }
@@ -62,6 +86,77 @@ define(function() {
return false; return false;
}); });
$('#users-container').on('click', '.admin-btn', function() {
var adminBtn = $(this);
var isAdmin = isUserAdmin(adminBtn);
var parent = adminBtn.parents('.users-box');
var isBanned = isUserBanned(adminBtn);
var uid = getUID(adminBtn);
if(uid === yourid){
app.alert({
title: 'Error',
message: 'You can\'t remove yourself as Administrator!',
type: 'danger',
timeout: 5000
});
}
else if (!isAdmin) {
socket.emit('api:admin.user.makeAdmin', uid);
adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
parent.attr('data-admin', 1);
updateUserBanButtons();
} else if(uid !== yourid) {
bootbox.confirm('Do you really want to remove this user as admin "' + parent.attr('data-username') + '"?', function(confirm) {
if (confirm) {
socket.emit('api:admin.user.removeAdmin', uid);
adminBtn.attr('value', 'Make Admin').html('Make Admin');
parent.attr('data-admin', 0);
updateUserBanButtons();
}
});
}
return false;
});
}
function handleUserCreate() {
$('#createUser').on('click', function() {
$('#create-modal').modal('show');
});
$('#create-modal-go').on('click', function() {
var username = $('#create-user-name').val(),
email = $('#create-user-email').val(),
password = $('#create-user-password').val(),
passwordAgain = $('#create-user-password-again').val(),
errorEl = $('#create-modal-error');
if(password !== passwordAgain) {
return errorEl.html('<strong>Error</strong><p>Passwords must match!</p>').removeClass('hide');
}
var user = {
username: username,
email: email,
password: password
};
socket.emit('api:admin.user.createUser', user, function(err, data) {
if(err) {
return errorEl.html('<strong>Error</strong><p>' + err + '</p>').removeClass('hide');
}
$('#create-modal').modal('hide');
app.alertSuccess('User created!');
ajaxify.go('admin/users');
});
});
} }
@@ -91,7 +186,7 @@ define(function() {
timeoutId = setTimeout(function() { timeoutId = setTimeout(function() {
var username = $('#search-user').val(); var username = $('#search-user').val();
jQuery('.icon-spinner').removeClass('none'); jQuery('.fa-spinner').removeClass('none');
socket.emit('api:admin.user.search', username); socket.emit('api:admin.user.search', username);
}, 250); }, 250);
@@ -99,6 +194,8 @@ define(function() {
initUsers(); initUsers();
handleUserCreate();
socket.removeAllListeners('api:admin.user.search'); socket.removeAllListeners('api:admin.user.search');
socket.on('api:admin.user.search', function(data) { socket.on('api:admin.user.search', function(data) {
@@ -108,7 +205,7 @@ define(function() {
userListEl = document.querySelector('.users'); userListEl = document.querySelector('.users');
userListEl.innerHTML = html; userListEl.innerHTML = html;
jQuery('.icon-spinner').addClass('none'); jQuery('.fa-spinner').addClass('none');
if (data && data.length === 0) { if (data && data.length === 0) {
$('#user-notfound-notify').html('User not found!') $('#user-notfound-notify').html('User not found!')

View File

@@ -1,5 +1,6 @@
define(function () { define(['composer'], function(composer) {
var Category = {}; var Category = {},
loadingMoreTopics = false;
Category.init = function() { Category.init = function() {
var cid = templates.get('category_id'), var cid = templates.get('category_id'),
@@ -8,8 +9,7 @@ define(function () {
googleEl = jQuery('#google-share'), googleEl = jQuery('#google-share'),
twitter_url = templates.get('twitter-intent-url'), twitter_url = templates.get('twitter-intent-url'),
facebook_url = templates.get('facebook-share-url'), facebook_url = templates.get('facebook-share-url'),
google_url = templates.get('google-share-url'), google_url = templates.get('google-share-url');
loadingMoreTopics = false;
app.enterRoom('category_' + cid); app.enterRoom('category_' + cid);
@@ -27,9 +27,7 @@ define(function () {
}); });
$('#new_post').on('click', function () { $('#new_post').on('click', function () {
require(['composer'], function (cmp) { composer.newTopic(cid);
cmp.push(0, cid);
});
}); });
ajaxify.register_events([ ajaxify.register_events([
@@ -83,36 +81,39 @@ define(function () {
Category.onNewTopic = function(data) { Category.onNewTopic = function(data) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({ var html = templates.prepare(templates['category'].blocks['topics']).parse({
topics: [data] topics: [data]
}), });
topic = $(html),
container = $('#topics-container'),
topics = $('#topics-container').children('.category-item'),
numTopics = topics.length;
jQuery('#topics-container, .category-sidebar').removeClass('hidden'); translator.translate(html, function(translatedHTML) {
jQuery('#category-no-topics').remove(); var topic = $(translatedHTML),
container = $('#topics-container'),
topics = $('#topics-container').children('.category-item'),
numTopics = topics.length;
if (numTopics > 0) { jQuery('#topics-container, .category-sidebar').removeClass('hidden');
for (var x = 0; x < numTopics; x++) { jQuery('#category-no-topics').remove();
if ($(topics[x]).find('.icon-pushpin').length) {
if(x === numTopics - 1) { if (numTopics > 0) {
topic.insertAfter(topics[x]); for (var x = 0; x < numTopics; x++) {
if ($(topics[x]).find('.fa-thumb-tack').length) {
if(x === numTopics - 1) {
topic.insertAfter(topics[x]);
}
continue;
} }
continue; topic.insertBefore(topics[x]);
break;
} }
topic.insertBefore(topics[x]); } else {
break; container.append(topic);
} }
} else {
container.append(topic);
}
topic.hide().fadeIn('slow'); topic.hide().fadeIn('slow');
socket.emit('api:categories.getRecentReplies', templates.get('category_id')); socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
addActiveUser(data); addActiveUser(data);
$('#topics-container span.timeago').timeago(); $('#topics-container span.timeago').timeago();
});
} }
function addActiveUser(data) { function addActiveUser(data) {
@@ -131,22 +132,29 @@ define(function () {
} }
Category.onTopicsLoaded = function(topics) { Category.onTopicsLoaded = function(topics) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({ var html = templates.prepare(templates['category'].blocks['topics']).parse({
topics: topics topics: topics
}), });
container = $('#topics-container');
jQuery('#topics-container, .category-sidebar').removeClass('hidden'); translator.translate(html, function(translatedHTML) {
jQuery('#category-no-topics').remove(); var container = $('#topics-container');
container.append(html); jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove();
$('#topics-container span.timeago').timeago(); html = $(translatedHTML);
container.append(html);
$('#topics-container span.timeago').timeago();
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
});
} }
Category.loadMoreTopics = function(cid) { Category.loadMoreTopics = function(cid) {
if (loadingMoreTopics) {
return;
}
loadingMoreTopics = true; loadingMoreTopics = true;
socket.emit('api:category.loadMore', { socket.emit('api:category.loadMore', {
cid: cid, cid: cid,

View File

@@ -3,10 +3,6 @@ define(['forum/accountheader'], function(header) {
AccountHeader.init = function() { AccountHeader.init = function() {
header.init(); header.init();
$('.user-favourite-posts .topic-row').on('click', function() {
ajaxify.go($(this).attr('topic-url'));
});
}; };
return AccountHeader; return AccountHeader;

View File

@@ -5,14 +5,15 @@
}); });
socket.on('api:updateHeader', function(data) { socket.on('api:updateHeader', function(data) {
jQuery('#search-button').on('click', function() {
jQuery('#search-fields').removeClass('hide').show();
jQuery(this).hide();
jQuery('#search-fields input').focus()
jQuery('#search-form').on('submit', function() { $('#search-button').on('click', function() {
jQuery('#search-fields').hide(); $('#search-fields').removeClass('hide').show();
jQuery('#search-button').show(); $(this).hide();
$('#search-fields input').focus();
$('#search-form').on('submit', function() {
$('#search-fields').hide();
$('#search-button').show();
}); });
$('#search-fields input').on('blur', function() { $('#search-fields input').on('blur', function() {
@@ -22,16 +23,17 @@
}); });
var loggedInMenu = $('#logged-in-menu'), var loggedInMenu = $('#logged-in-menu'),
isLoggedIn = data.uid > 0; isLoggedIn = data.uid > 0,
allowGuestSearching = (data.config || {}).allowGuestSearching === '1';
if (isLoggedIn) { if (isLoggedIn) {
jQuery('.nodebb-loggedin').show(); $('.nodebb-loggedin').show();
jQuery('.nodebb-loggedout').hide(); $('.nodebb-loggedout').hide();
$('#logged-out-menu').addClass('hide'); $('#logged-out-menu').addClass('hide');
$('#logged-in-menu').removeClass('hide'); $('#logged-in-menu').removeClass('hide');
$('#search-button').show(); $('#search-button').removeClass("hide").show();
var userLabel = loggedInMenu.find('#user_label'); var userLabel = loggedInMenu.find('#user_label');
@@ -46,10 +48,14 @@
$('#logout-link').on('click', app.logout); $('#logout-link').on('click', app.logout);
} }
} else { } else {
$('#search-button').hide(); if (allowGuestSearching) {
$('#search-button').removeClass("hide").show();
} else {
$('#search-button').addClass("hide").hide();
}
jQuery('.nodebb-loggedin').hide(); $('.nodebb-loggedin').hide();
jQuery('.nodebb-loggedout').show(); $('.nodebb-loggedout').show();
$('#logged-out-menu').removeClass('hide'); $('#logged-out-menu').removeClass('hide');
$('#logged-in-menu').addClass('hide'); $('#logged-in-menu').addClass('hide');
@@ -66,7 +72,8 @@
var notifContainer = document.getElementsByClassName('notifications')[0], var notifContainer = document.getElementsByClassName('notifications')[0],
notifTrigger = notifContainer.querySelector('a'), notifTrigger = notifContainer.querySelector('a'),
notifList = document.getElementById('notif-list'), notifList = document.getElementById('notif-list'),
notifIcon = document.querySelector('.notifications a i'); notifIcon = $('.notifications a');
notifTrigger.addEventListener('click', function(e) { notifTrigger.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault();
if (notifContainer.className.indexOf('open') === -1) { if (notifContainer.className.indexOf('open') === -1) {
@@ -77,7 +84,6 @@
numUnread = data.unread.length, numUnread = data.unread.length,
x; x;
notifList.innerHTML = ''; notifList.innerHTML = '';
console.log(data);
if ((data.read.length + data.unread.length) > 0) { if ((data.read.length + data.unread.length) > 0) {
for (x = 0; x < numUnread; x++) { for (x = 0; x < numUnread; x++) {
notifEl.setAttribute('data-nid', data.unread[x].nid); notifEl.setAttribute('data-nid', data.unread[x].nid);
@@ -105,12 +111,19 @@
notifList.appendChild(notifFrag); notifList.appendChild(notifFrag);
if (data.unread.length > 0) notifIcon.className = 'icon-circle active'; if (data.unread.length > 0) {
else notifIcon.className = 'icon-circle-blank'; notifIcon.toggleClass('active', true);
} else {
notifIcon.toggleClass('active', false);
}
socket.emit('api:notifications.mark_all_read', null, function() { socket.emit('api:notifications.mark_all_read', null, function() {
notifIcon.className = 'icon-circle-blank'; notifIcon.toggleClass('active', false);
utils.refreshTitle(); app.refreshTitle();
// Update favicon + local count
Tinycon.setBubble(0);
localStorage.setItem('notifications:count', 0);
}); });
}); });
} }
@@ -135,8 +148,29 @@
} }
}); });
var updateNotifCount = function(count) {
// Update notification icon, if necessary
if (count > 0) {
notifIcon.toggleClass('active', true);
} else {
notifIcon.toggleClass('active', false);
}
// Update the favicon + saved local count
Tinycon.setBubble(count);
localStorage.setItem('notifications:count', count);
};
socket.emit('api:notifications.getCount', function(err, count) {
if (!err) {
updateNotifCount(count);
} else {
updateNotifCount(0);
}
});
socket.on('event:new_notification', function() { socket.on('event:new_notification', function() {
document.querySelector('.notifications a i').className = 'icon-circle active'; notifIcon.toggleClass('active', true);
app.alert({ app.alert({
alert_id: 'new_notif', alert_id: 'new_notif',
title: 'New notification', title: 'New notification',
@@ -144,26 +178,93 @@
type: 'warning', type: 'warning',
timeout: 2000 timeout: 2000
}); });
utils.refreshTitle(); app.refreshTitle();
if (ajaxify.currentPage === 'notifications') {
ajaxify.refresh();
}
// Update the favicon + local storage
var savedCount = parseInt(localStorage.getItem('notifications:count'),10) || 0;
updateNotifCount(savedCount+1);
});
socket.on('event:notifications.updateCount', function(count) {
updateNotifCount(count);
}); });
// Chats Dropdown
var chatsToggleEl = $('#chat_dropdown'),
chatsListEl = $('#chat-list'),
chatDropdownEl = chatsToggleEl.parent();
chatsToggleEl.on('click', function() {
if (chatDropdownEl.hasClass('open')) {
return;
}
socket.on('chatMessage', function(data) { socket.emit('api:chats.list', function(chats) {
var chatsFrag = document.createDocumentFragment(),
chatEl = document.createElement('li'),
numChats = chats.length,
x, userObj;
if (numChats > 0) {
for(x=0;x<numChats;x++) {
userObj = chats[x];
chatEl.setAttribute('data-uid', userObj.uid);
chatEl.innerHTML = '<a href="javascript:app.openChat(\'' + userObj.username + '\', ' + userObj.uid + ');"><img src="' + userObj.picture + '" title="' + userObj.username + '" />' + userObj.username + '</a>';
chatsFrag.appendChild(chatEl.cloneNode(true));
}
} else {
chatEl.innerHTML = '<a href="#">No Recent Chats</a>';
chatsFrag.appendChild(chatEl.cloneNode(true));
}
chatsListEl.empty();
chatsListEl.html(chatsFrag);
});
});
socket.on('event:chats.receive', function(data) {
require(['chat'], function(chat) { require(['chat'], function(chat) {
var modal = null; var modal = null;
if (chat.modalExists(data.fromuid)) { if (chat.modalExists(data.fromuid)) {
modal = chat.getModal(data.fromuid); modal = chat.getModal(data.fromuid);
chat.appendChatMessage(modal, data.message, data.timestamp); chat.appendChatMessage(modal, data.message, data.timestamp);
if (modal.is(":visible")) {
chat.load(modal.attr('UUID'));
} else {
chat.toggleNew(modal.attr('UUID'), true);
}
if (!modal.is(":visible") || !app.isFocused) {
app.alternatingTitle(data.username + ' has messaged you');
}
} else { } else {
modal = chat.createModal(data.username, data.fromuid); modal = chat.createModal(data.username, data.fromuid);
chat.toggleNew(modal.attr('UUID'), true);
app.alternatingTitle(data.username + ' has messaged you');
} }
chat.load(modal.attr('UUID'));
}); });
}); });
require(['mobileMenu'], function(mobileMenu) { function updateUnreadCount(count) {
mobileMenu.init(); var badge = $('#numUnreadBadge');
}); badge.html(count > 20 ? '20+' : count);
}());
if (count > 0) {
badge
.removeClass('badge-inverse')
.addClass('badge-important');
} else {
badge
.removeClass('badge-important')
.addClass('badge-inverse');
}
}
socket.on('event:unread.updateCount', updateUnreadCount);
socket.emit('api:unread.count', updateUnreadCount);
}());

View File

@@ -2,23 +2,6 @@ define(function() {
var Login = {}; var Login = {};
Login.init = function() { 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');
}
});
$('#login').on('click', function() { $('#login').on('click', function() {
var loginData = { var loginData = {
'username': $('#username').val(), 'username': $('#username').val(),
@@ -26,18 +9,22 @@ define(function() {
'_csrf': $('#csrf-token').val() '_csrf': $('#csrf-token').val()
}; };
$('#login').attr('disabled', 'disabled').html('Logging in...');
$('#login-error-notify').hide();
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: RELATIVE_PATH + '/login', url: RELATIVE_PATH + '/login',
data: loginData, data: loginData,
success: function(data, textStatus, jqXHR) { success: function(data, textStatus, jqXHR) {
if (!data.success) { if (!data.success) {
$('#login-error-notify').show(); $('#login-error-notify').show();
$('#login').removeAttr('disabled').html('Login');
} else { } else {
$('#login-error-notify').hide(); $('#login').html('Redirecting...');
if(!app.previousUrl) {
if(!app.previousUrl) { app.previousUrl = '/';
app.previousUrl = '/';
} }
if(app.previousUrl.indexOf('/reset/') != -1) if(app.previousUrl.indexOf('/reset/') != -1)
@@ -50,10 +37,10 @@ define(function() {
}, },
error: function(data, textStatus, jqXHR) { error: function(data, textStatus, jqXHR) {
$('#login-error-notify').show(); $('#login-error-notify').show();
$('#login').removeAttr('disabled').html('Login');
}, },
dataType: 'json', dataType: 'json',
async: true, async: true
timeout: 2000
}); });
return false; return false;

View File

@@ -81,16 +81,20 @@ define(function() {
} }
Recent.onTopicsLoaded = function(topics) { Recent.onTopicsLoaded = function(topics) {
var html = templates.prepare(templates['recent'].blocks['topics']).parse({ var html = templates.prepare(templates['recent'].blocks['topics']).parse({
topics: topics topics: topics
}), });
container = $('#topics-container');
translator.translate(html, function(translatedHTML) {
var container = $('#topics-container');
$('#category-no-topics').remove(); $('#category-no-topics').remove();
container.append(html); html = $(html);
$('span.timeago').timeago(); container.append(html);
$('span.timeago').timeago();
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
});
} }
Recent.loadMoreTopics = function() { Recent.loadMoreTopics = function() {

View File

@@ -12,7 +12,7 @@ define(function() {
password_notify = $('#password-notify'), password_notify = $('#password-notify'),
password_confirm_notify = $('#password-confirm-notify'), password_confirm_notify = $('#password-confirm-notify'),
validationError = false, validationError = false,
successIcon = '<i class="icon icon-ok"></i>'; successIcon = '<i class="fa fa-check"></i>';
$('#referrer').val(app.previousUrl); $('#referrer').val(app.previousUrl);
@@ -41,10 +41,11 @@ define(function() {
if (!utils.isEmailValid(emailEl.val())) { if (!utils.isEmailValid(emailEl.val())) {
showError(email_notify, 'Invalid email address.'); showError(email_notify, 'Invalid email address.');
} else } else {
socket.emit('user.email.exists', { socket.emit('user.email.exists', {
email: emailEl.val() email: emailEl.val()
}); });
}
} }
emailEl.on('blur', function() { emailEl.on('blur', function() {
@@ -134,11 +135,6 @@ define(function() {
} }
}); });
// Alternate Logins
$('.alt-logins li').on('click', function(e) {
document.location.href = $(this).attr('data-url');
});
function validateForm() { function validateForm() {
validationError = false; validationError = false;
@@ -151,7 +147,9 @@ define(function() {
} }
register.on('click', function(e) { register.on('click', function(e) {
if (validateForm()) e.preventDefault(); if (validateForm()) {
e.preventDefault();
}
}); });
}; };

View File

@@ -1,22 +1,23 @@
define(function() { define(['composer'], function(composer) {
var Topic = {}, var Topic = {},
infiniteLoaderActive = false; infiniteLoaderActive = false,
pagination;
function showBottomPostBar() {
if($('#post-container .post-row').length > 1) {
$('.topic-main-buttons').removeClass('hide').parent().removeClass('hide');
}
}
Topic.init = function() { Topic.init = function() {
var expose_tools = templates.get('expose_tools'), var expose_tools = templates.get('expose_tools'),
tid = templates.get('topic_id'), tid = templates.get('topic_id'),
postListEl = document.getElementById('post-container'),
editBtns = document.querySelectorAll('#post-container .post-buttons .edit, #post-container .post-buttons .edit i'),
thread_state = { thread_state = {
locked: templates.get('locked'), locked: templates.get('locked'),
deleted: templates.get('deleted'), deleted: templates.get('deleted'),
pinned: templates.get('pinned') pinned: templates.get('pinned')
}, },
topic_name = templates.get('topic_name'), topic_name = templates.get('topic_name');
twitter_url = templates.get('twitter-intent-url'),
facebook_url = templates.get('facebook-share-url'),
google_url = templates.get('google-share-url');
function fixDeleteStateForPosts() { function fixDeleteStateForPosts() {
@@ -29,31 +30,13 @@ define(function() {
} }
} }
jQuery('document').ready(function() { jQuery('document').ready(function() {
app.addCommasToNumbers(); app.addCommasToNumbers();
app.enterRoom('topic_' + tid); app.enterRoom('topic_' + tid);
if($('#post-container .sub-posts').length) { showBottomPostBar();
$('.topic-main-buttons').removeClass('hide').parent().removeClass('hide');
}
$('.twitter-share').on('click', function () {
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
return false;
});
$('.facebook-share').on('click', function () {
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
return false;
});
$('.google-share').on('click', function () {
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
return false;
});
// Resetting thread state // Resetting thread state
if (thread_state.locked === '1') set_locked_state(true); if (thread_state.locked === '1') set_locked_state(true);
@@ -119,7 +102,7 @@ define(function() {
var loadingEl = document.getElementById('categories-loading'); var loadingEl = document.getElementById('categories-loading');
if (loadingEl) { if (loadingEl) {
socket.once('api:categories.get', function(data) { socket.emit('api:categories.get', function(data) {
// Render categories // Render categories
var categoriesFrag = document.createDocumentFragment(), var categoriesFrag = document.createDocumentFragment(),
categoryEl = document.createElement('li'), categoryEl = document.createElement('li'),
@@ -135,8 +118,10 @@ define(function() {
categoriesEl.className = 'category-list'; categoriesEl.className = 'category-list';
for (x = 0; x < numCategories; x++) { for (x = 0; x < numCategories; x++) {
info = data.categories[x]; info = data.categories[x];
categoryEl.className = info.blockclass + (info.disabled === '1' ? ' disabled' : ''); categoryEl.style.background = info.bgColor;
categoryEl.innerHTML = '<i class="' + info.icon + '"></i> ' + info.name; categoryEl.style.color = info.color || '#fff';
categoryEl.className = info.disabled === '1' ? ' disabled' : '';
categoryEl.innerHTML = '<i class="fa ' + info.icon + '"></i> ' + info.name;
categoryEl.setAttribute('data-cid', info.cid); categoryEl.setAttribute('data-cid', info.cid);
categoriesFrag.appendChild(categoryEl.cloneNode(true)); categoriesFrag.appendChild(categoryEl.cloneNode(true));
} }
@@ -158,7 +143,7 @@ define(function() {
commitEl.disabled = true; commitEl.disabled = true;
$(cancelEl).fadeOut(250); $(cancelEl).fadeOut(250);
$(moveThreadModal).find('.modal-header button').fadeOut(250); $(moveThreadModal).find('.modal-header button').fadeOut(250);
commitEl.innerHTML = 'Moving <i class="icon-spin icon-refresh"></i>'; commitEl.innerHTML = 'Moving <i class="fa-spin fa-refresh"></i>';
socket.once('api:topic.move', function(data) { socket.once('api:topic.move', function(data) {
moveThreadModal.modal('hide'); moveThreadModal.modal('hide');
@@ -187,7 +172,6 @@ define(function() {
} }
}); });
}); });
socket.emit('api:categories.get');
} }
}); });
} }
@@ -196,7 +180,7 @@ define(function() {
// Follow Thread State // Follow Thread State
var followEl = $('.main-post .follow'), var followEl = $('.posts .follow'),
set_follow_state = function(state, quiet) { set_follow_state = function(state, quiet) {
if (state && !followEl.hasClass('btn-success')) { if (state && !followEl.hasClass('btn-success')) {
followEl.addClass('btn-success'); followEl.addClass('btn-success');
@@ -278,13 +262,19 @@ define(function() {
if ($(selection.baseNode).parents('.post-content').length > 0) { if ($(selection.baseNode).parents('.post-content').length > 0) {
var snippet = selection.toString(); var snippet = selection.toString();
if (snippet.length > 0) selectionText = '> ' + snippet.replace(/\n/g, '\n> '); if (snippet.length > 0) {
selectionText = '> ' + snippet.replace(/\n/g, '\n> ');
}
}
var username = '',
post = $(this).parents('li[data-pid]');
if(post.length) {
username = '@' + post.attr('data-username') + ' ';
} }
if (thread_state.locked !== '1') { if (thread_state.locked !== '1') {
require(['composer'], function(cmp) { composer.newReply(tid, topic_name, selectionText.length > 0 ? selectionText + '\n\n' + username : '' + username);
cmp.push(tid, null, null, selectionText.length > 0 ? selectionText + '\n\n' : '');
});
} }
}); });
@@ -292,15 +282,11 @@ define(function() {
if (thread_state.locked !== '1') { if (thread_state.locked !== '1') {
var pid = $(this).parents('li').attr('data-pid'); var pid = $(this).parents('li').attr('data-pid');
socket.once('api:posts.getRawPost', function(data) { socket.emit('api:posts.getRawPost', {pid: pid}, function(data) {
quoted = '> ' + data.post.replace(/\n/g, '\n> ') + '\n\n'; quoted = '> ' + data.post.replace(/\n/g, '\n> ') + '\n\n';
require(['composer'], function(cmp) {
cmp.push(tid, null, null, quoted); composer.newReply(tid, topic_name, quoted);
});
});
socket.emit('api:posts.getRawPost', {
pid: pid
}); });
} }
}); });
@@ -309,8 +295,7 @@ define(function() {
var pid = $(this).parents('li').attr('data-pid'); var pid = $(this).parents('li').attr('data-pid');
var uid = $(this).parents('li').attr('data-uid'); var uid = $(this).parents('li').attr('data-uid');
var element = $(this).find('i'); if ($(this).attr('data-favourited') == 'false') {
if (element.attr('class') == 'icon-star-empty') {
socket.emit('api:posts.favourite', { socket.emit('api:posts.favourite', {
pid: pid, pid: pid,
room_id: app.currentRoom room_id: app.currentRoom
@@ -331,16 +316,31 @@ define(function() {
}); });
}); });
$('#post-container').delegate('.edit', 'click', function(e) { $('#post-container').on('click', '.twitter-share', function () {
var pid = $(this).parents('li').attr('data-pid'), var pid = $(this).parents('li').attr('data-pid');
main = $(this).parents('.main-post'); window.open('https://twitter.com/intent/tweet?url=' + encodeURIComponent(window.location.href + '#' + pid) + '&text=' + topic_name, '_blank', 'width=550,height=420,scrollbars=no,status=no');
return false;
require(['composer'], function(cmp) {
cmp.push(null, null, pid);
});
}); });
$('#post-container').delegate('.delete', 'click', function(e) { $('#post-container').on('click', '.facebook-share', function () {
var pid = $(this).parents('li').attr('data-pid');
window.open('https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(window.location.href + '#' + pid), '_blank', 'width=626,height=436,scrollbars=no,status=no');
return false;
});
$('#post-container').on('click', '.google-share', function () {
var pid = $(this).parents('li').attr('data-pid');
window.open('https://plus.google.com/share?url=' + encodeURIComponent(window.location.href + '#' + pid), '_blank', 'width=500,height=570,scrollbars=no,status=no');
return false;
});
$('#post-container').on('click', '.edit', function(e) {
var pid = $(this).parents('li').attr('data-pid');
composer.editPost(pid);
});
$('#post-container').on('click', '.delete', function(e) {
var pid = $(this).parents('li').attr('data-pid'), var pid = $(this).parents('li').attr('data-pid'),
postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')), postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
deleteAction = !postEl.hasClass('deleted') ? true : false, deleteAction = !postEl.hasClass('deleted') ? true : false,
@@ -369,11 +369,27 @@ define(function() {
} }
}); });
$('#post-container').on('click', '.fork-post', function(e) {
var post = $(this).parents('li'),
pid = post.attr('data-pid');
socket.emit('api:topic.createTopicFromPost', {pid:pid}, function(err) {
if(err) {
return app.alertError(err.message);
}
post.fadeOut(500, function() {
post.remove();
});
});
});
$('#post-container').on('click', '.chat', function(e) { $('#post-container').on('click', '.chat', function(e) {
var username = $(this).parents('li.row').attr('data-username'); var username = $(this).parents('li.row').attr('data-username');
var touid = $(this).parents('li.row').attr('data-uid'); var touid = $(this).parents('li.row').attr('data-uid');
app.openChat(username, touid); app.openChat(username, touid);
$(this).parents('.btn-group').find('.dropdown-toggle').click();
return false;
}); });
ajaxify.register_events([ ajaxify.register_events([
@@ -446,7 +462,7 @@ define(function() {
activeEl.find('.anonymous-box').remove(); activeEl.find('.anonymous-box').remove();
if(anonymousCount || remainingUsers) { if(anonymousCount || remainingUsers) {
var anonLink = $('<div class="anonymous-box inline-block"><i class="icon-user"></i></div>'); var anonLink = $('<div class="anonymous-box inline-block"><i class="fa fa-user"></i></div>');
activeEl.append(anonLink); activeEl.append(anonLink);
var title = ''; var title = '';
@@ -474,7 +490,21 @@ define(function() {
adjust_rep(-1, data.pid, data.uid); adjust_rep(-1, data.pid, data.uid);
}); });
socket.on('event:new_post', createNewPosts); socket.on('event:new_post', function(data) {
var posts = data.posts;
for (var p in posts) {
if (posts.hasOwnProperty(p)) {
var post = posts[p],
postcount = jQuery('.user_postcount_' + post.uid),
ptotal = parseInt(postcount.html(), 10);
ptotal += 1;
postcount.html(ptotal);
}
}
createNewPosts(data);
});
socket.on('event:topic_deleted', function(data) { socket.on('event:topic_deleted', function(data) {
if (data.tid === tid && data.status === 'ok') { if (data.tid === tid && data.status === 'ok') {
@@ -539,20 +569,22 @@ define(function() {
socket.on('api:posts.favourite', function(data) { socket.on('api:posts.favourite', function(data) {
if (data.status === 'ok' && data.pid) { if (data.status === 'ok' && data.pid) {
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling; var favBtn = $('li[data-pid="' + data.pid + '"] .favourite');
if (favEl) { if(favBtn.length) {
favEl.className = 'icon-star'; favBtn.addClass('btn-warning')
$(favEl).parent().addClass('btn-warning'); .attr('data-favourited', true)
.find('i').attr('class', 'fa fa-star');
} }
} }
}); });
socket.on('api:posts.unfavourite', function(data) { socket.on('api:posts.unfavourite', function(data) {
if (data.status === 'ok' && data.pid) { if (data.status === 'ok' && data.pid) {
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling; var favBtn = $('li[data-pid="' + data.pid + '"] .favourite');
if (favEl) { if(favBtn.length) {
favEl.className = 'icon-star-empty'; favBtn.removeClass('btn-warning')
$(favEl).parent().removeClass('btn-warning'); .attr('data-favourited', false)
.find('i').attr('class', 'fa fa-star-o');
} }
} }
}); });
@@ -598,11 +630,11 @@ define(function() {
x; x;
if (locked === true) { if (locked === true) {
lockThreadEl.html('<i class="icon-unlock"></i> Unlock Thread'); lockThreadEl.html('<i class="fa fa-unlock"></i> Unlock Thread');
threadReplyBtn.attr('disabled', true); threadReplyBtn.attr('disabled', true);
threadReplyBtn.html('Locked <i class="icon-lock"></i>'); threadReplyBtn.html('Locked <i class="fa fa-lock"></i>');
for (x = 0; x < numPosts; x++) { for (x = 0; x < numPosts; x++) {
postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>'; postReplyBtns[x].innerHTML = 'Locked <i class="fa fa-lock"></i>';
quoteBtns[x].style.display = 'none'; quoteBtns[x].style.display = 'none';
editBtns[x].style.display = 'none'; editBtns[x].style.display = 'none';
deleteBtns[x].style.display = 'none'; deleteBtns[x].style.display = 'none';
@@ -620,11 +652,11 @@ define(function() {
thread_state.locked = '1'; thread_state.locked = '1';
} else { } else {
lockThreadEl.html('<i class="icon-lock"></i> Lock Thread'); lockThreadEl.html('<i class="fa fa-lock"></i> Lock Thread');
threadReplyBtn.attr('disabled', false); threadReplyBtn.attr('disabled', false);
threadReplyBtn.html('Reply'); threadReplyBtn.html('Reply');
for (x = 0; x < numPosts; x++) { for (x = 0; x < numPosts; x++) {
postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>'; postReplyBtns[x].innerHTML = 'Reply <i class="fa fa-reply"></i>';
quoteBtns[x].style.display = 'inline-block'; quoteBtns[x].style.display = 'inline-block';
editBtns[x].style.display = 'inline-block'; editBtns[x].style.display = 'inline-block';
deleteBtns[x].style.display = 'inline-block'; deleteBtns[x].style.display = 'inline-block';
@@ -652,7 +684,7 @@ define(function() {
deleteNotice = document.getElementById('thread-deleted') || document.createElement('div'); deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
if (deleted) { if (deleted) {
deleteTextEl.html('<i class="icon-comment"></i> Restore Thread'); deleteTextEl.html('<i class="fa fa-comment"></i> Restore Thread');
threadEl.addClass('deleted'); threadEl.addClass('deleted');
// Spawn a 'deleted' notice at the top of the page // Spawn a 'deleted' notice at the top of the page
@@ -663,7 +695,7 @@ define(function() {
thread_state.deleted = '1'; thread_state.deleted = '1';
} else { } else {
deleteTextEl.html('<i class="icon-trash"></i> Delete Thread'); deleteTextEl.html('<i class="fa fa-trash-o"></i> Delete Thread');
threadEl.removeClass('deleted'); threadEl.removeClass('deleted');
deleteNotice.parentNode.removeChild(deleteNotice); deleteNotice.parentNode.removeChild(deleteNotice);
@@ -675,7 +707,7 @@ define(function() {
var pinEl = $('.pin_thread'); var pinEl = $('.pin_thread');
if (pinned) { if (pinned) {
pinEl.html('<i class="icon-pushpin"></i> Unpin Thread'); pinEl.html('<i class="fa fa-thumb-tack"></i> Unpin Thread');
if (alert) { if (alert) {
app.alert({ app.alert({
'alert_id': 'thread_pin', 'alert_id': 'thread_pin',
@@ -688,7 +720,7 @@ define(function() {
thread_state.pinned = '1'; thread_state.pinned = '1';
} else { } else {
pinEl.html('<i class="icon-pushpin"></i> Pin Thread'); pinEl.html('<i class="fa fa-thumb-tack"></i> Pin Thread');
if (alert) { if (alert) {
app.alert({ app.alert({
'alert_id': 'thread_pin', 'alert_id': 'thread_pin',
@@ -764,77 +796,76 @@ define(function() {
var postAuthorImage, mobileAuthorOverlay, pagination;
var postcount = templates.get('postcount');
function updateHeader() { Topic.postCount = templates.get('postcount');
if (pagination == null) {
jQuery('.pagination-block i:first').on('click', function() {
app.scrollToTop();
});
jQuery('.pagination-block i:last').on('click', function() {
app.scrollToBottom();
});
}
jQuery('.mobile-author-overlay').css('bottom', '0px');
postAuthorImage = postAuthorImage || document.getElementById('mobile-author-image');
mobileAuthorOverlay = mobileAuthorOverlay || document.getElementById('mobile-author-overlay');
pagination = pagination || document.getElementById('pagination');
pagination.parentNode.style.display = 'block';
var windowHeight = jQuery(window).height();
var scrollTop = jQuery(window).scrollTop();
var scrollBottom = scrollTop + windowHeight;
if (scrollTop < 50 && postcount > 1) {
localStorage.removeItem("topic:" + tid + ":bookmark");
postAuthorImage.src = (jQuery('.main-post .avatar img').attr('src'));
mobileAuthorOverlay.innerHTML = 'Posted by ' + jQuery('.main-post').attr('data-username') + ', ' + jQuery('.main-post').find('.relativeTimeAgo').html();
pagination.innerHTML = '0 out of ' + postcount;
return;
}
var count = 0, smallestNonNegative = 0;
jQuery('.sub-posts').each(function() {
count++;
this.postnumber = count;
var el = jQuery(this);
var elTop = el.offset().top;
var height = Math.floor(el.height());
var elBottom = elTop + (height < 300 ? height : 300);
var inView = ((elBottom >= scrollTop) && (elTop <= scrollBottom) && (elBottom <= scrollBottom) && (elTop >= scrollTop));
if (inView) {
if(elTop - scrollTop > smallestNonNegative) {
localStorage.setItem("topic:" + tid + ":bookmark", el.attr('data-pid'));
smallestNonNegative = Number.MAX_VALUE;
}
pagination.innerHTML = this.postnumber + ' out of ' + postcount;
postAuthorImage.src = (jQuery(this).find('.profile-image-block img').attr('src'));
mobileAuthorOverlay.innerHTML = 'Posted by ' + jQuery(this).attr('data-username') + ', ' + jQuery(this).find('.relativeTimeAgo').html();
}
});
setTimeout(function() {
if (scrollTop + windowHeight == jQuery(document).height()) {
pagination.innerHTML = postcount + ' out of ' + postcount;
}
}, 100);
}
window.onscroll = updateHeader; window.onscroll = updateHeader;
window.onload = updateHeader; window.onload = updateHeader;
}; };
function updateHeader() {
if (pagination == null) {
jQuery('.pagination-block i:first').on('click', function() {
app.scrollToTop();
});
jQuery('.pagination-block i:last').on('click', function() {
app.scrollToBottom();
});
}
pagination = pagination || document.getElementById('pagination');
var windowHeight = jQuery(window).height();
var scrollTop = jQuery(window).scrollTop();
var scrollBottom = scrollTop + windowHeight;
var progressBar = $('.progress-bar');
var progressBarContainer = $('.progress-container');
var tid = templates.get('topic_id');
pagination.parentNode.style.display = 'block';
progressBarContainer.css('display', '');
if (scrollTop < jQuery('.posts > .post-row:first-child').height() && Topic.postCount > 1) {
localStorage.removeItem("topic:" + tid + ":bookmark");
pagination.innerHTML = '1 out of ' + Topic.postCount;
progressBar.width(0);
return;
}
var count = 0, smallestNonNegative = 0;
jQuery('.posts > .post-row:not(".deleted")').each(function() {
count++;
this.postnumber = count;
var el = jQuery(this);
var elTop = el.offset().top;
var height = Math.floor(el.height());
var elBottom = elTop + (height < 300 ? height : 300);
var inView = ((elBottom >= scrollTop) && (elTop <= scrollBottom) && (elBottom <= scrollBottom) && (elTop >= scrollTop));
if (inView) {
if(elTop - scrollTop > smallestNonNegative) {
localStorage.setItem("topic:" + tid + ":bookmark", el.attr('data-pid'));
smallestNonNegative = Number.MAX_VALUE;
}
pagination.innerHTML = (this.postnumber-1) + ' out of ' + Topic.postCount;
progressBar.width(((this.postnumber-1) / Topic.postCount * 100) + '%');
}
});
setTimeout(function() {
if (scrollTop + windowHeight == jQuery(document).height()) {
pagination.innerHTML = Topic.postCount + ' out of ' + Topic.postCount;
progressBar.width('100%');
}
}, 100);
}
Topic.scrollToPost = function(pid) { Topic.scrollToPost = function(pid) {
if (!pid) { if (!pid) {
return; return;
@@ -871,11 +902,8 @@ define(function() {
} }
function createNewPosts(data, infiniteLoaded) { function createNewPosts(data, infiniteLoaded) {
if(!data || (data.posts && !data.posts.length)) if(!data || (data.posts && !data.posts.length)) {
return; return;
if (data.posts[0].uid !== app.uid) {
data.posts[0].display_moderator_tools = 'none';
} }
function removeAlreadyAddedPosts() { function removeAlreadyAddedPosts() {
@@ -890,7 +918,7 @@ define(function() {
$('#post-container li[data-pid]').each(function() { $('#post-container li[data-pid]').each(function() {
if(parseInt(firstPid, 10) > parseInt($(this).attr('data-pid'), 10)) { if(parseInt(firstPid, 10) > parseInt($(this).attr('data-pid'), 10)) {
after = $(this); after = $(this);
if(after.hasClass('main-post')) { if(after.next().length && after.next().hasClass('post-bar')) {
after = after.next(); after = after.next();
} }
} else { } else {
@@ -904,13 +932,16 @@ define(function() {
if(!data.posts.length) { if(!data.posts.length) {
return; return;
} }
var insertAfter = findInsertionPoint(); var insertAfter = findInsertionPoint();
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data); var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
var regexp = new RegExp("<!--[\\s]*IF @first[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF @first[\\s]*-->", 'g');
html = html.replace(regexp, '');
translator.translate(html, function(translatedHTML) { translator.translate(html, function(translatedHTML) {
var translated = $(translatedHTML); var translated = $(translatedHTML);
if(!infiniteLoaded) { if(!infiniteLoaded) {
translated.removeClass('infiniteloaded'); translated.removeClass('infiniteloaded');
} }
@@ -930,11 +961,18 @@ define(function() {
$('span.timeago').timeago(); $('span.timeago').timeago();
$('.post-content img').addClass('img-responsive'); $('.post-content img').addClass('img-responsive');
updatePostCount(); updatePostCount();
showBottomPostBar();
}); });
} }
function updatePostCount() { function updatePostCount() {
$('#topic-post-count').html($('#post-container li[data-pid]:not(.deleted)').length); socket.emit('api:topics.postcount', templates.get('topic_id'), function(err, postcount) {
if(!err) {
Topic.postCount = postcount;
$('#topic-post-count').html(Topic.postCount);
updateHeader();
}
})
} }
function loadMorePosts(tid, callback) { function loadMorePosts(tid, callback) {
@@ -952,7 +990,7 @@ define(function() {
socket.emit('api:topic.loadMore', { socket.emit('api:topic.loadMore', {
tid: tid, tid: tid,
after: $('#post-container .post-row.infiniteloaded').length after: parseInt($('#post-container .post-row.infiniteloaded').last().attr('data-index'), 10) + 1
}, function (data) { }, function (data) {
infiniteLoaderActive = false; infiniteLoaderActive = false;
if (data.posts.length) { if (data.posts.length) {

View File

@@ -1,10 +1,9 @@
define(function() { define(function() {
var Unread = {}; var Unread = {},
loadingMoreTopics = false;
Unread.init = function() { Unread.init = function() {
var loadingMoreTopics = false; app.enterRoom('recent_posts');
app.enter_room('recent_posts');
ajaxify.register_events([ ajaxify.register_events([
'event:new_topic', 'event:new_topic',
@@ -75,13 +74,18 @@ define(function() {
var html = templates.prepare(templates['unread'].blocks['topics']).parse({ var html = templates.prepare(templates['unread'].blocks['topics']).parse({
topics: topics topics: topics
}), });
container = $('#topics-container');
$('#category-no-topics').remove(); translator.translate(html, function(translatedHTML) {
var container = $('#topics-container');
container.append(html); $('#category-no-topics').remove();
$('span.timeago').timeago();
html = $(translatedHTML);
container.append(html);
$('span.timeago').timeago();
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
});
} }
function loadMoreTopics() { function loadMoreTopics() {
@@ -109,8 +113,9 @@ define(function() {
}); });
if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20) if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20) {
$('#load-more-btn').show(); $('#load-more-btn').show();
}
$('#load-more-btn').on('click', function() { $('#load-more-btn').on('click', function() {
loadMoreTopics(); loadMoreTopics();

View File

@@ -36,7 +36,7 @@ define(function() {
var username = $('#search-user').val(); var username = $('#search-user').val();
if (username == '') { if (username == '') {
jQuery('#user-notfound-notify').html('<i class="icon icon-circle-blank"></i>'); jQuery('#user-notfound-notify').html('<i class="fa fa-circle-o"></i>');
jQuery('#user-notfound-notify').parent().removeClass('btn-warning label-warning btn-success label-success'); jQuery('#user-notfound-notify').parent().removeClass('btn-warning label-warning btn-success label-success');
return; return;
} }
@@ -44,7 +44,7 @@ define(function() {
if (lastSearch === username) return; if (lastSearch === username) return;
lastSearch = username; lastSearch = username;
jQuery('#user-notfound-notify').html('<i class="icon-spinner icon-spin"></i>'); jQuery('#user-notfound-notify').html('<i class="fa fa-spinner fa-spin"></i>');
setTimeout(function() { setTimeout(function() {
socket.emit('api:admin.user.search', username); socket.emit('api:admin.user.search', username);

View File

@@ -1,4 +1,4 @@
define(['taskbar'], function(taskbar) { define(['taskbar', 'string'], function(taskbar, S) {
var module = {}; var module = {};
@@ -67,7 +67,8 @@ define(['taskbar'], function(taskbar) {
chatModal.find('.close').on('click', function(e) { chatModal.find('.close').on('click', function(e) {
clearInterval(chatModal.intervalId); clearInterval(chatModal.intervalId);
chatModal.intervalId = 0; chatModal.intervalId = 0;
chatModal.hide(); chatModal.remove();
chatModal.data('modal', null);
taskbar.discard('chat', uuid); taskbar.discard('chat', uuid);
}); });
@@ -81,13 +82,16 @@ define(['taskbar'], function(taskbar) {
checkOnlineStatus(chatModal); checkOnlineStatus(chatModal);
}); });
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with ' + username}); taskbar.push('chat', chatModal.attr('UUID'), {
title:'<i class="fa fa-comment"></i> ' + username,
state: ''
});
return chatModal; return chatModal;
} }
module.center = function(chatModal) { module.center = function(chatModal) {
chatModal.css("position", "fixed"); chatModal.css("position", "fixed");
chatModal.css("top", "100px");
chatModal.css("left", Math.max(0, (($(window).width() - $(chatModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px"); chatModal.css("left", Math.max(0, (($(window).width() - $(chatModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px");
return chatModal; return chatModal;
} }
@@ -97,6 +101,9 @@ define(['taskbar'], function(taskbar) {
chatModal.show(); chatModal.show();
module.bringModalToTop(chatModal); module.bringModalToTop(chatModal);
checkOnlineStatus(chatModal); checkOnlineStatus(chatModal);
taskbar.updateActive(uuid);
scrollToBottom(chatModal.find('#chat-content'));
chatModal.find('#chat-message-input').focus();
} }
module.minimize = function(uuid) { module.minimize = function(uuid) {
@@ -108,7 +115,7 @@ define(['taskbar'], function(taskbar) {
} }
function getChatMessages(chatModal, callback) { function getChatMessages(chatModal, callback) {
socket.emit('getChatMessages', {touid:chatModal.touid}, function(messages) { socket.emit('api:chats.get', {touid:chatModal.touid}, function(messages) {
for(var i = 0; i<messages.length; ++i) { for(var i = 0; i<messages.length; ++i) {
module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp); module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
} }
@@ -132,10 +139,10 @@ define(['taskbar'], function(taskbar) {
} }
function sendMessage(chatModal) { function sendMessage(chatModal) {
var msg = app.strip_tags(chatModal.find('#chat-message-input').val()); var msg = S(chatModal.find('#chat-message-input').val()).stripTags().s;
if(msg.length) { if(msg.length) {
msg = msg +'\n'; msg = msg +'\n';
socket.emit('sendChatMessage', { touid:chatModal.touid, message:msg}); socket.emit('api:chats.send', { touid:chatModal.touid, message:msg});
chatModal.find('#chat-message-input').val(''); chatModal.find('#chat-message-input').val('');
} }
} }
@@ -146,10 +153,18 @@ define(['taskbar'], function(taskbar) {
var date = new Date(parseInt(timestamp, 10)); var date = new Date(parseInt(timestamp, 10));
chatContent.append('[' + date.toLocaleTimeString() + '] ' + message); chatContent.append('[' + date.toLocaleTimeString() + '] ' + message);
scrollToBottom(chatContent);
};
function scrollToBottom(chatContent) {
chatContent.scrollTop( chatContent.scrollTop(
chatContent[0].scrollHeight - chatContent.height() chatContent[0].scrollHeight - chatContent.height()
); );
} }
module.toggleNew = function(uuid, state) {
taskbar.toggleNew(uuid, state);
};
return module; return module;
}); });

View File

@@ -1,76 +1,444 @@
define(['taskbar'], function(taskbar) { define(['taskbar'], function(taskbar) {
var composer = { var composer = {
initialized: false,
active: undefined, active: undefined,
taskbar: taskbar, posts: {}
posts: {},
postContainer: undefined,
}; };
var uploadsInProgress = []; function allowed() {
if(!(parseInt(app.uid, 10) > 0 || parseInt(config.allowGuestPosting, 10) === 1)) {
app.alert({
type: 'danger',
timeout: 5000,
alert_id: 'post_error',
title: 'Please Log In to Post',
message: 'Posting is currently restricted to registered members only, click here to log in',
clickfn: function() {
ajaxify.go('login');
}
});
return false;
}
return true;
}
function createImagePlaceholder(img) { composer.newTopic = function(cid) {
var text = $('.post-window textarea').val(), if(allowed()) {
textarea = $('.post-window textarea'), push({
imgText = "!["+img.name+"](uploading...)"; cid: cid,
title: '',
body: '',
modified: false
});
}
}
text += imgText; composer.newReply = function(tid, title, text) {
textarea.val(text + " "); if(allowed()) {
uploadsInProgress.push(1); push({
socket.emit("api:posts.uploadImage", img, function(err, data) { tid: tid,
title: title,
body: text,
modified: false
});
}
}
var currentText = textarea.val(); composer.editPost = function(pid) {
imgText = "!["+data.name+"](uploading...)"; if(allowed()) {
socket.emit('api:composer.push', {
pid: pid
}, function(threadData) {
push({
pid: pid,
title: threadData.title,
body: threadData.body,
modified: false
});
});
}
}
if(!err) function push(post) {
textarea.val(currentText.replace(imgText, "!["+data.name+"]("+data.url+")")); var uuid = utils.generateUUID();
else
textarea.val(currentText.replace(imgText, "!["+data.name+"](upload error)")); taskbar.push('composer', uuid, {
uploadsInProgress.pop(); title: post.title ? post.title : 'New Topic',
icon: post.picture
});
composer.posts[uuid] = post;
composer.load(uuid);
}
composer.load = function(post_uuid) {
if($('#cmp-uuid-' + post_uuid).length) {
composer.activateReposition(post_uuid);
} else {
composer.createNewComposer(post_uuid);
}
}
composer.createNewComposer = function(post_uuid) {
templates.preload_template('composer', function() {
var composerTemplate = templates['composer'].parse({});
composerTemplate = $(composerTemplate);
composerTemplate.attr('id', 'cmp-uuid-' + post_uuid);
$(document.body).append(composerTemplate);
composer.activateReposition(post_uuid);
var postContainer = $(composerTemplate[0]);
if(config.allowFileUploads || config.imgurClientIDSet)
initializeFileReader(post_uuid);
var postData = composer.posts[post_uuid],
titleEl = postContainer.find('.title'),
bodyEl = postContainer.find('textarea');
if (parseInt(postData.tid) > 0) {
titleEl.val('Replying to: ' + postData.title);
titleEl.prop('readOnly', true);
} else if (parseInt(postData.pid) > 0) {
titleEl.val(postData.title);
titleEl.prop('readOnly', true);
socket.emit('api:composer.editCheck', postData.pid, function(editCheck) {
if (editCheck.titleEditable) {
postContainer.find('input').prop('readonly', false);
}
});
} else {
titleEl.val(postData.title);
titleEl.prop('readOnly', false);
}
bodyEl.val(postData.body);
postContainer.on('change', 'input, textarea', function() {
composer.posts[post_uuid].modified = true;
});
postContainer.on('click', '.action-bar button', function() {
var action = $(this).attr('data-action');
switch(action) {
case 'post':
composer.post(post_uuid);
break;
case 'discard':
if (composer.posts[post_uuid].modified) {
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
if (discard) {
composer.discard(post_uuid);
}
});
} else {
composer.discard(post_uuid);
}
break;
}
});
postContainer.on('click', '.formatting-bar span', function() {
var postContentEl = postContainer.find('textarea'),
iconClass = $(this).find('i').attr('class'),
cursorEnd = postContentEl.val().length,
selectionStart = postContentEl[0].selectionStart,
selectionEnd = postContentEl[0].selectionEnd,
selectionLength = selectionEnd - selectionStart;
function insertIntoInput(element, value) {
var start = postContentEl[0].selectionStart;
element.val(element.val().slice(0, start) + value + element.val().slice(start, element.val().length));
postContentEl[0].selectionStart = postContentEl[0].selectionEnd = start + value.length;
}
switch(iconClass) {
case 'fa fa-bold':
if (selectionStart === selectionEnd) {
// Nothing selected
insertIntoInput(postContentEl, "**bolded text**");
} else {
// Text selected
postContentEl.val(postContentEl.val().slice(0, selectionStart) + '**' + postContentEl.val().slice(selectionStart, selectionEnd) + '**' + postContentEl.val().slice(selectionEnd));
postContentEl[0].selectionStart = selectionStart + 2;
postContentEl[0].selectionEnd = selectionEnd + 2;
}
break;
case 'fa fa-italic':
if (selectionStart === selectionEnd) {
// Nothing selected
insertIntoInput(postContentEl, "*italicised text*");
} else {
// Text selected
postContentEl.val(postContentEl.val().slice(0, selectionStart) + '*' + postContentEl.val().slice(selectionStart, selectionEnd) + '*' + postContentEl.val().slice(selectionEnd));
postContentEl[0].selectionStart = selectionStart + 1;
postContentEl[0].selectionEnd = selectionEnd + 1;
}
break;
case 'fa fa-list':
// Nothing selected
insertIntoInput(postContentEl, "\n\n* list item");
break;
case 'fa fa-link':
if (selectionStart === selectionEnd) {
// Nothing selected
insertIntoInput(postContentEl, "[link text](link url)");
} else {
// Text selected
postContentEl.val(postContentEl.val().slice(0, selectionStart) + '[' + postContentEl.val().slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.val().slice(selectionEnd));
postContentEl[0].selectionStart = selectionStart + selectionLength + 3;
postContentEl[0].selectionEnd = selectionEnd + 11;
}
break;
}
});
postContainer.on('click', '.formatting-bar span .fa-picture-o, .formatting-bar span .fa-upload', function() {
$('#files').click();
});
$('#files').on('change', function(e) {
var files = e.target.files;
if(files) {
for (var i=0; i<files.length; i++) {
loadFile(post_uuid, files[i]);
}
}
$('#fileForm')[0].reset();
});
var resizeActive = false,
resizeCenterX = 0,
resizeOffset = 0,
resizeStart = function(e) {
bodyRect = document.body.getBoundingClientRect();
resizeRect = resizeEl.getBoundingClientRect();
resizeCenterX = resizeRect.left + (resizeRect.width/2);
resizeOffset = resizeCenterX - e.clientX;
resizeSnaps.half = bodyRect.width / 2;
resizeSnaps.none = bodyRect.width;
resizeActive = true;
$(document.body).on('mousemove', resizeAction);
document.body.addEventListener('touchmove', resizeTouchAction);
},
resizeStop = function() {
resizeActive = false;
$(document.body).off('mousemove', resizeAction);
document.body.removeEventListener('touchmove', resizeTouchAction);
},
resizeTouchAction = function(e) {
e.preventDefault();
resizeAction(e.touches[0]);
},
resizeAction = function(e) {
if (resizeActive) {
position = (e.clientX + resizeOffset);
if (Math.abs(position - resizeSnaps.half) <= 15) {
// Half snap
postContainer.css('width', resizeSnaps.half);
resizeSavePosition(resizeSnaps.half);
} else if (Math.abs(position - resizeSnaps.none) <= 30) {
// Minimize snap
postContainer.css('width', bodyRect.width - resizeSnaps.none + 15);
resizeSavePosition(resizeSnaps.none);
} else if (position <= 30) {
// Full snap
postContainer.css('width', bodyRect.width - 15);
resizeSavePosition(bodyRect.width - 15);
} else {
// OH SNAP, NO SNAPS!
postContainer.css('width', bodyRect.width - position);
resizeSavePosition(bodyRect.width - position);
}
}
},
resizeSavePosition = function(px) {
var percentage = px/bodyRect.width;
localStorage.setItem('composer:resizePercentage', percentage);
},
resizeSnaps = {
none: 0,
half: 0,
full: 0
},
resizeRect, bodyRect;
var resizeEl = postContainer.find('.resizer')[0];
resizeEl.addEventListener('mousedown', resizeStart);
resizeEl.addEventListener('mouseup', resizeStop);
resizeEl.addEventListener('touchstart', function(e) {
e.preventDefault();
resizeStart(e.touches[0]);
});
resizeEl.addEventListener('touchend', function(e) {
e.preventDefault();
resizeStop();
});
// .on('mousedown touchstart', resizeStart)
// .on('mouseup touchend', resizeStop)
window.addEventListener('resize', function() {
if (composer.active !== undefined) {
composer.activateReposition(composer.active);
}
});
}); });
} }
function loadFile(file) { composer.activateReposition = function(post_uuid) {
var reader = new FileReader(),
dropDiv = $('.post-window .imagedrop'),
uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
$(reader).on('loadend', function(e) { if(composer.active && composer.active !== post_uuid) {
var bin = this.result; composer.minimize(composer.active);
bin = bin.split(',')[1]; }
var img = { var percentage = localStorage.getItem('composer:resizePercentage'),
name: file.name, bodyRect = document.body.getBoundingClientRect(),
data: bin postContainer = $('#cmp-uuid-' + post_uuid);
};
createImagePlaceholder(img); composer.active = post_uuid;
dropDiv.hide(); if (percentage) {
}); if (bodyRect.width >= 768) {
postContainer.css('width', Math.floor(bodyRect.width * percentage) + 'px');
} else {
postContainer.css('width', '100%');
}
}
reader.readAsDataURL(file); if(config.imgurClientIDSet) {
postContainer.find('.upload-instructions').removeClass('hide');
postContainer.find('.img-upload-btn').removeClass('hide');
}
if(config.allowFileUploads) {
postContainer.find('.file-upload-btn').removeClass('hide');
}
postContainer.css('visibility', 'visible');
composer.focusElements(post_uuid);
} }
function initializeFileReader() { composer.focusElements = function(post_uuid) {
jQuery.event.props.push( "dataTransfer" ); var postContainer = $('#cmp-uuid-' + post_uuid),
postData = composer.posts[post_uuid],
titleEl = postContainer.find('.title'),
bodyEl = postContainer.find('textarea');
if ((parseInt(postData.tid) || parseInt(postData.pid)) > 0) {
bodyEl.focus();
bodyEl.selectionStart = bodyEl.val().length;
bodyEl.selectionEnd = bodyEl.val().length;
} else if (parseInt(postData.cid) > 0) {
titleEl.focus();
}
}
composer.post = function(post_uuid) {
var postData = composer.posts[post_uuid],
postContainer = $('#cmp-uuid-' + post_uuid),
titleEl = postContainer.find('.title'),
bodyEl = postContainer.find('textarea');
titleEl.val(titleEl.val().trim());
bodyEl.val(bodyEl.val().trim());
if(postData.uploadsInProgress && postData.uploadsInProgress.length) {
return composerAlert('Still uploading', 'Please wait for uploads to complete.');
} else if (titleEl.val().length < parseInt(config.minimumTitleLength, 10)) {
return composerAlert('Title too short', 'Please enter a longer title. At least ' + config.minimumTitleLength+ ' characters.');
} else if (titleEl.val().length > parseInt(config.maximumTitleLength, 10)) {
return composerAlert('Title too long', 'Please enter a shorter title. Titles can\'t be longer than ' + config.maximumTitleLength + ' characters.');
} else if (bodyEl.val().length < parseInt(config.minimumPostLength, 10)) {
return composerAlert('Content too short', 'Please enter a longer post. At least ' + config.minimumPostLength + ' characters.');
}
// Still here? Let's post.
if (parseInt(postData.cid) > 0) {
socket.emit('api:topics.post', {
'title' : titleEl.val(),
'content' : bodyEl.val(),
'category_id' : postData.cid
});
} else if (parseInt(postData.tid) > 0) {
socket.emit('api:posts.reply', {
'topic_id' : postData.tid,
'content' : bodyEl.val()
});
} else if (parseInt(postData.pid) > 0) {
socket.emit('api:posts.edit', {
pid: postData.pid,
content: bodyEl.val(),
title: titleEl.val()
});
}
composer.discard(post_uuid);
}
function composerAlert(title, message) {
app.alert({
type: 'danger',
timeout: 2000,
title: title,
message: message,
alert_id: 'post_error'
});
}
composer.discard = function(post_uuid) {
if (composer.posts[post_uuid]) {
$('#cmp-uuid-' + post_uuid).remove();
delete composer.posts[post_uuid];
composer.active = undefined;
taskbar.discard('composer', post_uuid);
}
}
composer.minimize = function(post_uuid) {
var postContainer = $('#cmp-uuid-' + post_uuid);
postContainer.css('visibility', 'hidden');
composer.active = undefined;
taskbar.minimize('composer', post_uuid);
}
function initializeFileReader(post_uuid) {
if(jQuery.event.props.indexOf('dataTransfer') === -1) {
jQuery.event.props.push('dataTransfer');
}
var draggingDocument = false; var draggingDocument = false;
if(window.FileReader) { if(window.FileReader) {
var drop = $('.post-window .imagedrop'), var postContainer = $('#cmp-uuid-' + post_uuid),
textarea = $('.post-window textarea'); drop = postContainer.find('.imagedrop'),
textarea = postContainer.find('textarea');
$(document).on('dragstart', function(e) { $(document).off('dragstart').on('dragstart', function(e) {
draggingDocument = true; draggingDocument = true;
}).on('dragend', function(e) { }).off('dragend').on('dragend', function(e) {
draggingDocument = false; draggingDocument = false;
}); });
textarea.on('dragenter', function(e) { textarea.on('dragenter', function(e) {
if(draggingDocument) if(draggingDocument) {
return; return;
}
drop.css('top', textarea.position().top + 'px'); drop.css('top', textarea.position().top + 'px');
drop.css('height', textarea.height());
drop.css('line-height', textarea.height() + 'px');
drop.show(); drop.show();
drop.on('dragleave', function(ev) { drop.on('dragleave', function(ev) {
@@ -89,320 +457,85 @@ define(['taskbar'], function(taskbar) {
drop.on('drop', function(e) { drop.on('drop', function(e) {
e.preventDefault(); e.preventDefault();
var uuid = drop.parents('[data-uuid]').attr('data-uuid'), var dt = e.dataTransfer,
dt = e.dataTransfer,
files = dt.files; files = dt.files;
for (var i=0; i<files.length; i++) { for (var i=0; i<files.length; i++) {
loadFile(files[i]); loadFile(post_uuid, files[i]);
} }
if(!files.length) if(!files.length) {
drop.hide(); drop.hide();
}
return false; return false;
}); });
} }
} }
composer.init = function() { function loadFile(post_uuid, file) {
if (!composer.initialized) { var reader = new FileReader(),
var taskbar = document.getElementById('taskbar'); dropDiv = $('#cmp-uuid-' + post_uuid).find('.imagedrop');
composer.postContainer = document.createElement('div'); $(reader).on('loadend', function(e) {
composer.postContainer.className = 'post-window row'; var regex = /^data:.*;base64,(.*)$/;
composer.postContainer.innerHTML = '<div class="col-md-5">' + var matches = this.result.match(regex);
'<input type="text" tabIndex="1" placeholder="Enter your topic title here..." />' +
'<div class="btn-toolbar formatting-bar">' +
'<div class="btn-group">' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-bold"></i></span>' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-italic"></i></span>' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-list"></i></span>' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-link"></i></span>' +
'</div>' +
'</div>' +
'<textarea tabIndex="2"></textarea>' +
'<div class="imagedrop"><div>Drag and Drop Images Here</div></div>'+
'<div class="btn-toolbar action-bar">' +
'<div class="btn-group" style="float: right; margin-right: -8px">' +
'<button data-action="minimize" class="btn hidden-xs" tabIndex="4"><i class="icon-download-alt"></i> Minimize</button>' +
'<button class="btn" data-action="discard" tabIndex="5"><i class="icon-remove"></i> Discard</button>' +
'<button data-action="post" class="btn" tabIndex="3"><i class="icon-ok"></i> Submit</button>' +
'</div>' +
'</div>' +
'</div>';
document.body.insertBefore(composer.postContainer, taskbar); var fileData = {
name: file.name,
data: matches[1]
};
if(config.imgurClientIDSet) dropDiv.hide();
initializeFileReader();
socket.on('api:composer.push', function(threadData) { if(file.type.match('image.*')) {
if (!threadData.error) { uploadFile('api:posts.uploadImage', post_uuid, fileData);
var uuid = utils.generateUUID(); } else {
if(file.size > parseInt(config.maximumFileSize, 10) * 1024) {
composer.taskbar.push('composer', uuid, { return composerAlert('File too big', 'Maximum allowed file size is ' + config.maximumFileSize + 'kbs');
title: (!threadData.cid ? (threadData.title || '') : 'New Topic'),
icon: threadData.picture
});
composer.posts[uuid] = {
tid: threadData.tid,
cid: threadData.cid,
pid: threadData.pid,
title: threadData.title || '',
body: threadData.body || '',
modified: false
};
composer.load(uuid);
} else {
app.alert({
type: 'danger',
timeout: 5000,
alert_id: 'post_error',
title: 'Please Log In to Post',
message: 'Posting is currently restricted to registered members only, click here to log in',
clickfn: function() {
ajaxify.go('login');
}
});
} }
}); uploadFile('api:posts.uploadFile', post_uuid, fileData);
}
});
socket.on('api:composer.editCheck', function(editCheck) { reader.readAsDataURL(file);
if (editCheck.titleEditable === true) composer.postContainer.querySelector('input').readOnly = false;
});
// Post Window events
var jPostContainer = $(composer.postContainer),
postContentEl = composer.postContainer.querySelector('textarea');
jPostContainer.on('change', 'input, textarea', function() {
var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid');
if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value;
else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value;
// Mark this post window as having been changed
composer.posts[uuid].modified = true;
});
jPostContainer.on('click', '.action-bar button', function() {
var action = this.getAttribute('data-action'),
uuid = $(this).parents('.post-window').attr('data-uuid');
switch(action) {
case 'post': composer.post(uuid); break;
case 'minimize': composer.minimize(uuid); break;
case 'discard':
if (composer.posts[uuid].modified) {
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
if (discard) composer.discard(uuid);
});
} else {
composer.discard(uuid);
}
break;
}
});
jPostContainer.on('click', '.formatting-bar span', function() {
var iconClass = this.querySelector('i').className,
cursorEnd = postContentEl.value.length,
selectionStart = postContentEl.selectionStart,
selectionEnd = postContentEl.selectionEnd,
selectionLength = selectionEnd - selectionStart;
function insertIntoInput(element, value) {
var start = postContentEl.selectionStart;
element.value = element.value.slice(0, start) + value + element.value.slice(start, element.value.length);
postContentEl.selectionStart = postContentEl.selectionEnd = start + value.length;
}
switch(iconClass) {
case 'icon-bold':
if (selectionStart === selectionEnd) {
// Nothing selected
insertIntoInput(postContentEl, "**bolded text**");
} else {
// Text selected
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '**' + postContentEl.value.slice(selectionStart, selectionEnd) + '**' + postContentEl.value.slice(selectionEnd);
postContentEl.selectionStart = selectionStart + 2;
postContentEl.selectionEnd = selectionEnd + 2;
}
break;
case 'icon-italic':
if (selectionStart === selectionEnd) {
// Nothing selected
insertIntoInput(postContentEl, "*italicised text*");
} else {
// Text selected
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '*' + postContentEl.value.slice(selectionStart, selectionEnd) + '*' + postContentEl.value.slice(selectionEnd);
postContentEl.selectionStart = selectionStart + 1;
postContentEl.selectionEnd = selectionEnd + 1;
}
break;
case 'icon-list':
// Nothing selected
insertIntoInput(postContentEl, "\n\n* list item");
break;
case 'icon-link':
if (selectionStart === selectionEnd) {
// Nothing selected
insertIntoInput(postContentEl, "[link text](link url)");
} else {
// Text selected
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '[' + postContentEl.value.slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.value.slice(selectionEnd);
postContentEl.selectionStart = selectionStart + selectionLength + 3;
postContentEl.selectionEnd = selectionEnd + 11;
}
break;
}
});
window.addEventListener('resize', function() {
if (composer.active !== undefined) composer.reposition(composer.active);
});
composer.initialized = true;
}
} }
composer.push = function(tid, cid, pid, text) {
socket.emit('api:composer.push', { function uploadFile(method, post_uuid, img) {
tid: tid, // Replying var linkStart = method === 'api:posts.uploadImage' ? '!' : '',
cid: cid, // Posting postContainer = $('#cmp-uuid-' + post_uuid),
pid: pid, // Editing textarea = postContainer.find('textarea'),
body: text // Predefined text text = textarea.val(),
imgText = linkStart + '[' + img.name + '](uploading...)';
text += imgText;
textarea.val(text + " ");
if(!composer.posts[post_uuid].uploadsInProgress) {
composer.posts[post_uuid].uploadsInProgress = [];
}
composer.posts[post_uuid].uploadsInProgress.push(1);
socket.emit(method, img, function(err, data) {
var currentText = textarea.val();
if(err) {
textarea.val(currentText.replace(imgText, linkStart + '[' + img.name + '](upload error)'));
return app.alertError(err.message);
}
textarea.val(currentText.replace(imgText, linkStart + '[' + data.name + '](' + data.url + ')'));
composer.posts[post_uuid].uploadsInProgress.pop();
}); });
} }
composer.load = function(post_uuid) {
var post_data = composer.posts[post_uuid],
titleEl = composer.postContainer.querySelector('input'),
bodyEl = composer.postContainer.querySelector('textarea');
composer.reposition(post_uuid);
composer.active = post_uuid;
composer.postContainer.setAttribute('data-uuid', post_uuid);
if (parseInt(post_data.tid) > 0) {
titleEl.value = 'Replying to: ' + post_data.title;
titleEl.readOnly = true;
} else if (parseInt(post_data.pid) > 0) {
titleEl.value = post_data.title;
titleEl.readOnly = true;
socket.emit('api:composer.editCheck', post_data.pid);
} else {
titleEl.value = post_data.title;
titleEl.readOnly = false;
}
bodyEl.value = post_data.body;
// Direct user focus to the correct element
if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) {
bodyEl.focus();
bodyEl.selectionStart = bodyEl.value.length;
bodyEl.selectionEnd = bodyEl.value.length;
} else if (parseInt(post_data.cid) > 0) {
titleEl.focus();
}
}
composer.reposition = function(post_uuid) {
var postWindowEl = composer.postContainer.querySelector('.col-md-5'),
taskbarBtn = document.querySelector('#taskbar [data-uuid="' + post_uuid + '"]'),
btnRect = taskbarBtn.getBoundingClientRect(),
taskbarRect = document.getElementById('taskbar').getBoundingClientRect(),
windowRect, leftPos;
composer.postContainer.style.display = 'block';
windowRect = postWindowEl.getBoundingClientRect();
leftPos = btnRect.left + btnRect.width - windowRect.width;
postWindowEl.style.left = (leftPos > 0 ? leftPos : 0) + 'px';
composer.postContainer.style.bottom = taskbarRect.height + "px";
}
composer.post = function(post_uuid) {
// Check title and post length
var postData = composer.posts[post_uuid],
titleEl = composer.postContainer.querySelector('input'),
bodyEl = composer.postContainer.querySelector('textarea');
titleEl.value = titleEl.value.trim();
bodyEl.value = bodyEl.value.trim();
if(uploadsInProgress.length) {
return app.alert({
type: 'warning',
timeout: 2000,
title: 'Still uploading',
message: "Please wait for uploads to complete.",
alert_id: 'post_error'
});
}
if (titleEl.value.length < config.minimumTitleLength) {
return app.alert({
type: 'danger',
timeout: 2000,
title: 'Title too short',
message: "Please enter a longer title. At least " + config.minimumTitleLength+ " characters.",
alert_id: 'post_error'
});
}
if (bodyEl.value.length < config.minimumPostLength) {
return app.alert({
type: 'danger',
timeout: 2000,
title: 'Content too short',
message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
alert_id: 'post_error'
});
}
// Still here? Let's post.
if (parseInt(postData.cid) > 0) {
socket.emit('api:topics.post', {
'title' : titleEl.value,
'content' : bodyEl.value,
'category_id' : postData.cid
});
} else if (parseInt(postData.tid) > 0) {
socket.emit('api:posts.reply', {
'topic_id' : postData.tid,
'content' : bodyEl.value
});
} else if (parseInt(postData.pid) > 0) {
socket.emit('api:posts.edit', {
pid: postData.pid,
content: bodyEl.value,
title: titleEl.value
});
}
composer.discard(post_uuid);
}
composer.discard = function(post_uuid) {
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);
}
}
composer.minimize = function(uuid) {
composer.postContainer.style.display = 'none';
composer.active = undefined;
taskbar.minimize('composer', uuid);
}
composer.init();
return { return {
push: composer.push, newTopic: composer.newTopic,
newReply: composer.newReply,
editPost: composer.editPost,
load: composer.load, load: composer.load,
minimize: composer.minimize minimize: composer.minimize
}; };

View File

@@ -1,122 +0,0 @@
define(function() {
var mobileMenu = {};
var categories = null,
overlay = null,
menuBtn = null,
postBtn = null,
initialized = false;
function loadCategories(callback) {
if (categories) {
callback(true);
return;
}
jQuery.getJSON(RELATIVE_PATH+'/api/home', function(data) {
categories = data.categories;
initialized = true;
if (callback) {
callback(true);
}
});
}
function displayCategories() {
var baseIcon = document.createElement('a'),
baseImage = document.createElement('i'),
baseName = document.createElement('span');
baseIcon.className = 'mobile-menu-icon';
for (var i=0, ii=categories.length; i<ii; i++) {
var icon = baseIcon.cloneNode(true),
image = baseImage.cloneNode(true),
name = baseName.cloneNode(true);
var category = categories[i];
image.className = category.icon + ' icon-3x';
name.innerHTML = '<br />' + category.name;
icon.appendChild(image);
icon.appendChild(name);
icon.src = 'category/' + category.slug;
icon.onclick = function() {
jQuery('.mobile-menu-icon').toggleClass('menu-visible');
setTimeout(function() {
jQuery(overlay).toggleClass('menu-visible');
}, 200);
ajaxify.go(this.src);
}
overlay.appendChild(icon);
}
}
function animateIcons() {
jQuery(overlay).toggleClass('menu-visible');
setTimeout(function() {
jQuery('.mobile-menu-icon').toggleClass('menu-visible');
}, 100);
}
mobileMenu.onNavigate = function() {
if (initialized == false) return false;
var cid = templates.get('category_id'),
tid = templates.get('topic_id');
if (cid) {
postBtn.style.display = 'inline-block';
postBtn.onclick = function() {
require(['composer'], function(cmp) {
cmp.push(0, cid);
});
};
postBtn.children[0].className = 'icon-plus icon-2x';
} else if (tid) {
postBtn.style.display = 'inline-block';
postBtn.onclick = function() {
require(['composer'], function(cmp) {
cmp.push(tid);
});
};
postBtn.children[0].className = 'icon-reply icon-2x'
} else {
postBtn.style.display = 'none';
}
};
mobileMenu.init = function() {
return; // disabling until this can be pluginified.
overlay = overlay || document.getElementById('mobile-menu-overlay');
menuBtn = menuBtn || document.getElementById('mobile-menu-btn');
postBtn = postBtn || document.getElementById('mobile-post-btn');
menuBtn.onclick = function() {
animateIcons();
}
loadCategories(function() {
displayCategories();
mobileMenu.onNavigate();
});
}
return {
init: mobileMenu.init,
onNavigate: mobileMenu.onNavigate
}
});

File diff suppressed because one or more lines are too long

View File

@@ -25,6 +25,8 @@ define(function() {
if (_btn.className.indexOf('active') === -1) { if (_btn.className.indexOf('active') === -1) {
taskbar.minimizeAll(); taskbar.minimizeAll();
module.load(uuid); module.load(uuid);
taskbar.toggleNew(uuid, false);
app.alternatingTitle('');
// Highlight the button // Highlight the button
$(taskbar.tasklist).removeClass('active'); $(taskbar.tasklist).removeClass('active');
@@ -69,7 +71,7 @@ define(function() {
'</a>'; '</a>';
btnEl.setAttribute('data-module', module); btnEl.setAttribute('data-module', module);
btnEl.setAttribute('data-uuid', uuid); btnEl.setAttribute('data-uuid', uuid);
btnEl.className = options.state || 'active'; btnEl.className = options.state !== undefined ? options.state : 'active';
if (!options.state || options.state === 'active') taskbar.minimizeAll(); if (!options.state || options.state === 'active') taskbar.minimizeAll();
taskbar.tasklist.appendChild(btnEl); taskbar.tasklist.appendChild(btnEl);
@@ -82,14 +84,27 @@ define(function() {
}, },
minimizeAll: function() { minimizeAll: function() {
$(taskbar.tasklist.querySelectorAll('.active')).removeClass('active'); $(taskbar.tasklist.querySelectorAll('.active')).removeClass('active');
},
toggleNew: function(uuid, state) {
var btnEl = $(taskbar.tasklist.querySelector('[data-uuid="' + uuid + '"]'));
btnEl.toggleClass('new', state);
},
updateActive: function(uuid) {
var tasks = $(taskbar.tasklist).find('li');
tasks.removeClass('active');
tasks.filter('[data-uuid="' + uuid + '"]').addClass('active');
} }
} }
if (!taskbar.initialized) taskbar.init(); if (!taskbar.initialized) {
taskbar.init();
}
return { return {
push: taskbar.push, push: taskbar.push,
discard: taskbar.discard, discard: taskbar.discard,
minimize: taskbar.minimize minimize: taskbar.minimize,
toggleNew: taskbar.toggleNew,
updateActive: taskbar.updateActive
} }
}); });

View File

@@ -2,12 +2,13 @@ define(function() {
var module = {}; var module = {};
module.open = function(route, callback) { module.open = function(route, params, callback) {
$('#upload-picture-modal').modal('show').removeClass('hide'); $('#upload-picture-modal').modal('show').removeClass('hide');
module.hideAlerts(); module.hideAlerts();
$('#uploadForm')[0].reset(); $('#uploadForm')[0].reset();
$('#uploadForm').attr('action', route); $('#uploadForm').attr('action', route);
$('#uploadForm').find('#params').val(JSON.stringify(params));
$('#pictureUploadSubmitBtn').off('click').on('click', function() { $('#pictureUploadSubmitBtn').off('click').on('click', function() {
$('#uploadForm').submit(); $('#uploadForm').submit();

View File

@@ -48,7 +48,7 @@
} }
}; };
templates.prepare = function (raw_tpl, data) { templates.prepare = function (raw_tpl) {
var template = {}; var template = {};
template.html = raw_tpl; template.html = raw_tpl;
template.parse = parse; template.parse = parse;
@@ -78,7 +78,9 @@
global.templates[file] = new template; global.templates[file] = new template;
loaded--; loaded--;
if (loaded == 0) templates.ready(); if (loaded === 0) {
templates.ready();
}
}); });
}(templatesToLoad[t])); }(templatesToLoad[t]));
} }
@@ -122,12 +124,37 @@
return ''; return '';
} }
templates.preload_template = function(tpl_name, callback) {
if(templates[tpl_name]) {
return callback();
}
// TODO: This should be "load_template", and the current load_template
// should be named something else
// TODO: The "Date.now()" in the line below is only there for development purposes.
// It should be removed at some point.
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_name + '.tpl?v=' + Date.now(), function (html) {
var template = function () {
this.toString = function () {
return this.html;
};
}
template.prototype.parse = parse;
template.prototype.html = String(html);
template.prototype.blocks = {};
templates[tpl_name] = new template;
callback();
});
}
templates.load_template = function (callback, url, template) { templates.load_template = function (callback, url, template) {
var location = document.location || window.location, var location = document.location || window.location,
api_url = (url === '' || url === '/') ? 'home' : url, api_url = (url === '' || url === '/') ? 'home' : url,
tpl_url = templates.get_custom_map(api_url.split('?')[0]), tpl_url = templates.get_custom_map(api_url.split('?')[0]);
trimmed = api_url;
if (!tpl_url) { if (!tpl_url) {
tpl_url = templates.getTemplateNameFromUrl(api_url); tpl_url = templates.getTemplateNameFromUrl(api_url);
@@ -138,19 +165,7 @@
var timestamp = new Date().getTime(); //debug var timestamp = new Date().getTime(); //debug
if (!templates[tpl_url]) { if (!templates[tpl_url]) {
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_url + '.tpl?v=' + timestamp, function (html) { templates.preload_template(tpl_url, function() {
var template = function () {
this.toString = function () {
return this.html;
};
}
template.prototype.parse = parse;
template.prototype.html = String(html);
template.prototype.blocks = {};
templates[tpl_url] = new template;
parse_template(); parse_template();
}); });
} else { } else {
@@ -167,11 +182,13 @@
template_data = data; template_data = data;
parse_template(); parse_template();
}).fail(function (data) { }).fail(function (data) {
if(data && data.status == 404) { if (data && data.status == 404) {
ajaxify.go('404'); return ajaxify.go('404');
return; } else if (data && data.status === 403) {
return ajaxify.go('403');
} else {
app.alertError(data.responseJSON.error);
} }
app.alertError("Can't load template data!");
}); });
function parse_template() { function parse_template() {
@@ -239,7 +256,7 @@
} }
function makeConditionalRegex(block) { function makeConditionalRegex(block) {
return new RegExp("<!--[\\s]*IF " + block + "[\\s]*-->[\\s\\S]*<!--[\\s]*ENDIF " + block + "[\\s]*-->", 'g'); return new RegExp("<!--[\\s]*IF " + block + "[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF " + block + "[\\s]*-->", 'g');
} }
function getBlock(regex, block, template) { function getBlock(regex, block, template) {
@@ -248,9 +265,12 @@
if (self.blocks && block !== undefined) self.blocks[block] = data[0]; if (self.blocks && block !== undefined) self.blocks[block] = data[0];
var begin = new RegExp("(\r\n)*<!-- BEGIN " + block + " -->(\r\n)*", "g"),
end = new RegExp("(\r\n)*<!-- END " + block + " -->(\r\n)*", "g"),
data = data[0] data = data[0]
.replace("<!--[\\s]*BEGIN " + block + "[\\s]*-->", "") .replace(begin, "")
.replace("<!--[\\s]*END " + block + "[\\s]*-->", ""); .replace(end, "");
return data; return data;
} }
@@ -262,7 +282,7 @@
var template = this.html, var template = this.html,
regex, block; regex, block;
return (function parse(data, namespace, template) { return (function parse(data, namespace, template, blockInfo) {
if (!data || data.length == 0) { if (!data || data.length == 0) {
template = ''; template = '';
} }
@@ -289,44 +309,51 @@
result = ""; result = "";
do { do {
result += parse(data[d][i], namespace, block); result += parse(data[d][i], namespace, block, {iterator: i, total: numblocks});
} while (i++ < numblocks); } while (i++ < numblocks);
namespace = namespace.replace(d + '.', ''); namespace = namespace.replace(d + '.', '');
template = setBlock(regex, result, template); template = setBlock(regex, result, template);
} else if (data[d] instanceof Object) { } else if (data[d] instanceof Object) {
namespace += d + '.'; template = parse(data[d], d + '.', template);
regex = makeRegex(d),
block = getBlock(regex, namespace, template)
if (block == null) continue;
block = parse(data[d], namespace, block);
template = setBlock(regex, block, template);
} else { } else {
var conditional = makeConditionalRegex(namespace + d); function checkConditional(key, value) {
var conditional = makeConditionalRegex(key),
matches = template.match(conditional);
var conditionalBlock = conditional.exec(template); if (matches !== null) {
for (var i = 0, ii = matches.length; i < ii; i++) {
if (conditionalBlock !== null) { var conditionalBlock = matches[i].split(/<!-- ELSE -->/);
conditionalBlock = conditionalBlock[0].split(/<!-- ELSE -->/);
if (conditionalBlock[1]) {
if (conditionalBlock[1]) { // there is an else statement
// there is an else statement if (!value) {
if (!data[d]) { template = template.replace(matches[i], conditionalBlock[1].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
template = template.replace(conditional, conditionalBlock[1]); } else {
} else { template = template.replace(matches[i], conditionalBlock[0].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
template = template.replace(conditional, conditionalBlock[0]); }
} } else {
// regular if statement
} else { if (!value) {
// regular if template = template.replace(matches[i], '');
if (!data[d]) { } else {
template = template.replace(conditional, ''); template = template.replace(matches[i], matches[i].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
}
}
} }
} }
} }
checkConditional(namespace + d, data[d]);
checkConditional('!' + namespace + d, !data[d]);
if (blockInfo) {
checkConditional('@first', blockInfo.iterator === 0);
checkConditional('!@first', blockInfo.iterator !== 0);
checkConditional('@last', blockInfo.iterator === blockInfo.total);
checkConditional('!@last', blockInfo.iterator !== blockInfo.total);
}
template = replace(namespace + d, data[d], template); template = replace(namespace + d, data[d], template);
} }
} }
@@ -335,8 +362,12 @@
if (namespace) { if (namespace) {
var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g'); var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
template = template.replace(regex, ''); template = template.replace(regex, '');
namespace = '';
} }
// clean up all undefined conditionals
template = template.replace(/<!-- IF([^@]*?)ENDIF([^@]*?)-->/gi, '');
return template; return template;
})(data, "", template); })(data, "", template);

View File

@@ -82,7 +82,7 @@
var parsedKey = keys[key].replace('[[', '').replace(']]', '').split(':'); var parsedKey = keys[key].replace('[[', '').replace(']]', '').split(':');
if (!(parsedKey[0] && parsedKey[1])) continue; if (!(parsedKey[0] && parsedKey[1])) continue;
var languageFile = parsedKey[0]; var languageFile = parsedKey[0];
parsedKey = parsedKey[1].split(',')[0]; parsedKey = parsedKey[1].split(',')[0];

View File

@@ -1,10 +1,11 @@
(function(module) { (function(module) {
'use strict'; 'use strict';
var utils, fs; var utils, fs, XRegExp;
if ('undefined' === typeof window) { if ('undefined' === typeof window) {
fs = require('fs'); fs = require('fs');
XRegExp = require('xregexp').XRegExp;
} }
@@ -56,6 +57,10 @@
var now = +new Date(), var now = +new Date(),
difference = now - Math.floor(parseFloat(timestamp)); difference = now - Math.floor(parseFloat(timestamp));
if(difference < 0) {
difference = 0;
}
difference = Math.floor(difference / 1000); difference = Math.floor(difference / 1000);
if (difference < 60) { if (difference < 60) {
@@ -88,19 +93,13 @@
//http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/ //http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
slugify: function(str) { slugify: function(str) {
var invalidChars = XRegExp('[^\\p{L} 0-9\-]', 'g');
str = str.replace(/^\s+|\s+$/g, ''); // trim str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase(); str = str.toLowerCase();
str = XRegExp.replace(str, invalidChars, '');
// remove accents, swap ñ for n, etc str = str.replace(/\s+/g, '-') // collapse whitespace and replace by -
var from = "àáäâèéëêìíïîıòóöôùúüûñçşğ·/_,:;"; str = str.replace(/-+/g, '-'); // collapse dashes
var to = "aaaaeeeeiiiiioooouuuuncsg------";
for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str; return str;
}, },
@@ -119,18 +118,6 @@
isPasswordValid: function(password) { isPasswordValid: function(password) {
return password && password.indexOf(' ') === -1; return password && password.indexOf(' ') === -1;
}, },
// Blatently stolen from: http://phpjs.org/functions/strip_tags/
'strip_tags': function(input, allowed) {
allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, '').replace(tags, function($0, $1) {
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
});
},
buildMetaTags: function(tagsArr) { buildMetaTags: function(tagsArr) {
var tags = '', var tags = '',
tag; tag;
@@ -169,52 +156,23 @@
return tags; return tags;
}, },
refreshTitle: function(url) {
if (!url) {
var a = document.createElement('a');
a.href = document.location;
url = a.pathname.slice(1);
}
var notificationIcon;
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';
}
});
jQuery.getJSON(RELATIVE_PATH + '/api/unread/total', function(data) {
var badge = jQuery('#numUnreadBadge');
badge.html(data.count > 20 ? '20+' : data.count);
if (data.count > 0) {
badge
.removeClass('badge-inverse')
.addClass('badge-important');
} else {
badge
.removeClass('badge-important')
.addClass('badge-inverse');
}
});
},
isRelativeUrl: function(url) { isRelativeUrl: function(url) {
var firstChar = url.slice(0, 1); var firstChar = url.slice(0, 1);
return (firstChar === '.' || firstChar === '/'); return (firstChar === '.' || firstChar === '/');
}, },
makeNumberHumanReadable: function(num) { makeNumberHumanReadable: function(num) {
num = parseInt(num, 10); var n = parseInt(num, 10);
if (num > 999999) { if(!n) {
return (num / 1000000).toFixed(1) + 'm'; return num;
} }
else if(num > 999) { if (n > 999999) {
return (num / 1000).toFixed(1) + 'k'; return (n / 1000000).toFixed(1) + 'm';
} }
return num; else if(n > 999) {
return (n / 1000).toFixed(1) + 'k';
}
return n;
} }
}; };

5
public/templates/500.tpl Normal file
View File

@@ -0,0 +1,5 @@
<div class="alert alert-danger">
<strong>[[global:500.title]]</strong>
<p>[[global:500.message]]</p>
<p>{errorMessage}</p>
</div>

View File

@@ -13,69 +13,96 @@
<img src="{picture}" class="user-profile-picture img-thumbnail"/> <img src="{picture}" class="user-profile-picture img-thumbnail"/>
</div> </div>
<div class="account-online-status"> <div class="account-online-status">
<span><i class="icon-circle-blank"></i> <span>offline</span></span> <span><i class="fa fa-circle-o"></i> <span>[[user:offline]]</span></span>
</div> </div>
<div class="{show_banned}"> <!-- IF banned -->
<span class="label label-danger">banned</span> <div>
<span class="label label-danger">[[user:banned]]</span>
</div>
<!-- ENDIF banned -->
<div>
<a id="chat-btn" href="#" class="btn btn-default hide">Chat</a>
</div> </div>
<div id="user-actions"> <div id="user-actions">
<a id="follow-btn" href="#" class="btn btn-default">Follow</a> <a id="follow-btn" href="#" class="btn btn-default hide">Follow</a>
<a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a> <a id="unfollow-btn" href="#" class="btn btn-default hide">Unfollow</a>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="inline-block"> <div class="inline-block">
<div class="account-bio-block"> <div class="account-bio-block">
<span class="account-bio-label">email</span><i class="icon-eye-close {emailClass}" title="Email hidden"></i> <span class="account-bio-label">[[user:email]]</span><i class="fa fa-eye-slash {emailClass}" title="[[user:email_hidden]]"></i>
<!-- IF email -->
<span>{email}</span> <span>{email}</span>
<!-- ELSE -->
<i class="fa fa-eye-slash" title="[[user:email_hidden]]"></i> [[user:hidden]]
<!-- ENDIF email -->
<br/> <br/>
<span class="account-bio-label">full name</span>
<!-- IF fullname -->
<span class="account-bio-label">[[user:fullname]]</span>
<span>{fullname}</span> <span>{fullname}</span>
<br/> <br/>
<!-- ENDIF fullname -->
<span class="account-bio-label">website</span> <!-- IF website -->
<span class="account-bio-label">[[user:website]]</span>
<span><a href="{website}">{websiteName}</a></span> <span><a href="{website}">{websiteName}</a></span>
<br/> <br/>
<!-- ENDIF website -->
<span class="account-bio-label">location</span> <!-- IF location -->
<span class="account-bio-label">[[user:location]]</span>
<span>{location}</span> <span>{location}</span>
<br/> <br/>
<!-- ENDIF location -->
<span class="account-bio-label">age</span> <!-- IF age -->
<span class="account-bio-label">[[user:age]]</span>
<span>{age}</span> <span>{age}</span>
<br/> <br/>
<!-- ENDIF age -->
<hr/> <hr/>
<span class="account-bio-label">joined</span> <span class="account-bio-label">[[user:joined]]</span>
<span class="timeago" title="{joindate}"></span> <span class="timeago" title="{joindate}"></span>
<br/> <br/>
<span class="account-bio-label">profile views</span> <span class="account-bio-label">[[user:lastonline]]</span>
<span class="timeago" title="{lastonline}"></span>
<br/>
<span class="account-bio-label">[[user:profile_views]]</span>
<span class="formatted-number">{profileviews}</span> <span class="formatted-number">{profileviews}</span>
<br/> <br/>
<span class="account-bio-label">reputation</span> <span class="account-bio-label">[[user:reputation]]</span>
<span class="formatted-number">{reputation}</span> <span class="formatted-number">{reputation}</span>
<br/> <br/>
<span class="account-bio-label">posts</span> <span class="account-bio-label">[[user:posts]]</span>
<span class="formatted-number">{postcount}</span> <span class="formatted-number">{postcount}</span>
<br/> <br/>
<span class="account-bio-label">followers</span> <span class="account-bio-label">[[user:followers]]</span>
<span class="formatted-number">{followerCount}</span> <span class="formatted-number">{followerCount}</span>
<br/> <br/>
<span class="account-bio-label">following</span> <span class="account-bio-label">[[user:following]]</span>
<span class="formatted-number">{followingCount}</span> <span class="formatted-number">{followingCount}</span>
<br/> <br/>
<hr/> <hr/>
<span class="account-bio-label">signature</span> <!-- IF !disableSignatures -->
<!-- IF signature -->
<span class="account-bio-label">[[user:signature]]</span>
<div class="post-signature"> <div class="post-signature">
<span id='signature'>{signature}</span> <span id='signature'>{signature}</span>
</div> </div>
<!-- ENDIF signature -->
<!-- ENDIF !disableSignatures -->
</div> </div>
</div> </div>
</div> </div>
@@ -97,4 +124,4 @@
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />
<input type="hidden" template-variable="theirid" value="{theirid}" /> <input type="hidden" template-variable="theirid" value="{theirid}" />
<input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" /> <input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" />

View File

@@ -1,27 +1,26 @@
<div class="well account">
<div class="well"> <div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="[[user:change_picture]]" aria-hidden="true">
<div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Change Picture" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Change Picture</h3> <h3 id="myModalLabel">[[user:change_picture]]</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="gravatar-box"> <div id="gravatar-box">
<img id="user-gravatar-picture" src="" class="img-thumbnail user-profile-picture"> <img id="user-gravatar-picture" src="" class="img-thumbnail user-profile-picture">
<span class="user-picture-label">Gravatar</span> <span class="user-picture-label">[[user:gravatar]]</span>
<i class='icon-ok icon-2x'></i> <i class='fa fa-check fa-2x'></i>
</div> </div>
<br/> <br/>
<div id="uploaded-box"> <div id="uploaded-box">
<img id="user-uploaded-picture" src="" class="img-thumbnail user-profile-picture"> <img id="user-uploaded-picture" src="" class="img-thumbnail user-profile-picture">
<span class="user-picture-label">Uploaded picture</span> <span class="user-picture-label">[[user:uploaded_picture]]</span>
<i class='icon-ok icon-2x'></i> <i class='fa fa-check fa-2x'></i>
</div> </div>
<a id="uploadPictureBtn" href="#">Upload new picture</a> <a id="uploadPictureBtn" href="#">[[user:upload_new_picture]]</a>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button> <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
@@ -33,8 +32,8 @@
<div class="account-username-box" data-userslug="{userslug}"> <div class="account-username-box" data-userslug="{userslug}">
<span class="account-username"> <span class="account-username">
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i> <a href="/user/{userslug}">{username}</a> <i class="fa fa-chevron-right"></i>
<a href="/user/{userslug}/edit">edit</a> <a href="/user/{userslug}/edit">[[user:edit]]</a>
</span> </span>
</div> </div>
@@ -42,7 +41,7 @@
<div class="col-md-2" style="text-align: center; margin-bottom:20px;"> <div class="col-md-2" style="text-align: center; margin-bottom:20px;">
<div class="account-picture-block text-center"> <div class="account-picture-block text-center">
<img id="user-current-picture" class="user-profile-picture img-thumbnail" src="{picture}" /><br /><br /> <img id="user-current-picture" class="user-profile-picture img-thumbnail" src="{picture}" /><br /><br />
<a id="changePictureBtn" href="#" class="btn btn-primary">change picture</a> <a id="changePictureBtn" href="#" class="btn btn-primary">[[user:change_picture]]</a>
</div> </div>
</div> </div>
@@ -50,51 +49,53 @@
<div> <div>
<form class='form-horizontal'> <form class='form-horizontal'>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputEmail">Email</label> <label class="control-label" for="inputEmail">[[user:email]]</label>
<div class="controls"> <div class="controls">
<input class="form-control" type="text" id="inputEmail" placeholder="Email" value="{email}"> <input class="form-control" type="text" id="inputEmail" placeholder="Email" value="{email}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputFullname">Full Name</label> <label class="control-label" for="inputFullname">[[user:fullname]]</label>
<div class="controls"> <div class="controls">
<input class="form-control" type="text" id="inputFullname" placeholder="Full Name" value="{fullname}"> <input class="form-control" type="text" id="inputFullname" placeholder="Full Name" value="{fullname}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputWebsite">Website</label> <label class="control-label" for="inputWebsite">[[user:website]]</label>
<div class="controls"> <div class="controls">
<input class="form-control" type="text" id="inputWebsite" placeholder="http://website.com" value="{website}"> <input class="form-control" type="text" id="inputWebsite" placeholder="http://website.com" value="{website}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputLocation">Location</label> <label class="control-label" for="inputLocation">[[user:location]]</label>
<div class="controls"> <div class="controls">
<input class="form-control" type="text" id="inputLocation" placeholder="Location" value="{location}"> <input class="form-control" type="text" id="inputLocation" placeholder="Location" value="{location}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputBirthday">Birthday</label> <label class="control-label" for="inputBirthday">[[user:birthday]]</label>
<div class="controls"> <div class="controls">
<input class="form-control" type="date" id="inputBirthday" placeholder="mm/dd/yyyy" value="{birthday}"> <input class="form-control" type="date" id="inputBirthday" placeholder="mm/dd/yyyy" value="{birthday}">
</div> </div>
</div> </div>
<!-- IF !disableSignatures -->
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputSignature">Signature</label> <label class="control-label" for="inputSignature">[[user:signature]]</label> <small><label id="signatureCharCountLeft"></label></small>
<div class="controls"> <div class="controls">
<textarea class="form-control" id="inputSignature" placeholder="max 150 chars" rows="5">{signature}</textarea> <textarea class="form-control" id="inputSignature" rows="5">{signature}</textarea>
</div> </div>
</div> </div>
<!-- ENDIF !disableSignatures -->
<input type="hidden" id="inputUID" value="{uid}"><br /> <input type="hidden" id="inputUID" value="{uid}"><br />
<div class="form-actions"> <div class="form-actions">
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a> <a id="submitBtn" href="#" class="btn btn-primary">[[global:save_changes]]</a>
</div> </div>
</form> </form>
@@ -114,23 +115,27 @@
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputNewPassword">Password</label> <label class="control-label" for="inputNewPassword">[[user:password]]</label>
<div class="controls"> <div class="input-group">
<input class="form-control" type="password" id="inputNewPassword" placeholder="New Password" value=""> <input class="form-control" type="password" id="inputNewPassword" placeholder="New Password" value="">
<div id="password-notify" class="alert alert-danger hide"></div> <span class="input-group-addon">
<span id="password-notify"><i class="fa fa-circle-o"></i></span>
</span>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputNewPasswordAgain">Confirm Password</label> <label class="control-label" for="inputNewPasswordAgain">[[user:confirm_password]]</label>
<div class="controls"> <div class="input-group">
<input class="form-control" type="password" id="inputNewPasswordAgain" placeholder="Confirm Password" value=""> <input class="form-control" type="password" id="inputNewPasswordAgain" placeholder="Confirm Password" value="">
<div id="password-confirm-notify" class="alert alert-danger hide"></div> <span class="input-group-addon">
<span id="password-confirm-notify"><i class="fa fa-circle-o"></i></span>
</span>
</div> </div>
</div> </div>
<br/> <br/>
<div class="form-actions"> <div class="form-actions">
<a id="changePasswordBtn" href="#" class="btn btn-primary">Change Password</a> <a id="changePasswordBtn" href="#" class="btn btn-primary">[[user:change_password]]</a>
</div> </div>
</form> </form>

View File

@@ -1,10 +1,9 @@
<div class="well account">
<div class="well">
<div class="account-username-box" data-userslug="{userslug}"> <div class="account-username-box" data-userslug="{userslug}">
<span class="account-username"> <span class="account-username">
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i> <a href="/user/{userslug}">{username}</a> <i class="fa fa-chevron-right"></i>
<a href="/user/{userslug}/settings">settings</a> <a href="/user/{userslug}/settings">[[user:settings]]</a>
</span> </span>
</div> </div>
@@ -13,7 +12,7 @@
<h4>privacy</h4> <h4>privacy</h4>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="showemailCheckBox" type="checkbox" {showemail}> Show my email <input id="showemailCheckBox" type="checkbox" {showemail}> [[user:show_email]]
</label> </label>
</div> </div>
</div> </div>
@@ -23,6 +22,6 @@
</div> </div>
</div> </div>
<div class="form-actions"> <div class="form-actions">
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a> <a id="submitBtn" href="#" class="btn btn-primary">[[global:save_changes]]</a>
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,6 @@
<!-- IF redis -->
<h1>Redis</h1> <h1>Redis</h1>
<hr /> <hr />
<div id="admin-redis-info"> <div id="admin-redis-info">
@@ -21,4 +24,29 @@
<span>Keyspace Hits</span> <span class="text-right">{keyspace_hits}</span><br/> <span>Keyspace Hits</span> <span class="text-right">{keyspace_hits}</span><br/>
<span>Keyspace Misses</span> <span class="text-right">{keyspace_misses}</span><br/> <span>Keyspace Misses</span> <span class="text-right">{keyspace_misses}</span><br/>
</div> </div>
<hr />
<h3>Raw Info </h3>
<div class="highlight">
<pre>{raw}</pre>
</div>
<!-- ENDIF redis -->
<!-- IF mongo -->
<h1>Mongo</h1>
<hr />
<div id="admin-redis-info">
<span>Collections</span> <span class="text-right">{collections}</span><br/>
<span>Objects</span> <span class="text-right">{objects}</span><br/>
<span>Avg. Object Size</span> <span class="text-right">{avgObjSize} kb</span><br/>
<hr/>
<span>Data Size</span> <span class="text-right">{dataSize} kb</span><br/>
<span>Storage Size</span> <span class="text-right">{storageSize} kb</span><br/>
<span>File Size</span> <span class="text-right">{fileSize} kb</span><br/>
</div>
<hr />
<h3>Raw Info </h3>
<div class="highlight">
<pre>{raw}</pre>
</div>
<!-- ENDIF mongo -->

View File

@@ -0,0 +1,5 @@
<h1>Events</h1>
<pre>
{eventdata}
</pre>

View File

@@ -17,6 +17,7 @@
<p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p> <p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p>
</div> </div>
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" /> <input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
<input type="hidden" id="params" name="params">
</form> </form>
<div id="upload-progress-box" class="progress progress-striped"> <div id="upload-progress-box" class="progress progress-striped">

View File

@@ -54,13 +54,13 @@
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li> <li>
<a href="/admin/index"><i class="icon-home"></i> Home</a> <a href="/admin/index"><i class="fa fa-home" title="Home"></i></a>
</li> </li>
<li> <li>
<a href="/admin/settings"><i class="icon-cogs"></i> Settings</a> <a href="/admin/settings"><i class="fa fa-cogs" title="Settings"></i></a>
</li> </li>
<li> <li>
<a href="/" target="_top"><i class="icon-book"></i> Forum</a> <a href="/" target="_top"><i class="fa fa-book" title="Forum"></i></a>
</li> </li>
<li> <li>
<a href="#" id="reconnect"></a> <a href="#" id="reconnect"></a>
@@ -91,31 +91,35 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-sm-3">
<div class="well sidebar-nav"> <div class="well sidebar-nav">
<ul class="nav nav-list"> <ul class="nav nav-list">
<li class="nav-header">NodeBB</li> <li class="nav-header">NodeBB</li>
<li class='active'> <li class="active"><a href="{relative_path}/admin/index"><i class="fa fa-fw fa-home"></i> Home</a></li>
<a href='{relative_path}/admin/index'><i class='icon-home'></i> Home</a> <li><a href="{relative_path}/admin/categories/active"><i class="fa fa-fw fa-folder"></i> Categories</a></li>
</li> <li><a href="{relative_path}/admin/users/latest"><i class="fa fa-fw fa-user"></i> Users</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/groups"><i class="fa fa-fw fa-group"></i> Groups</a></li>
<li><a href='{relative_path}/admin/users/latest'><i class='icon-user'></i> Users</a></li> <li><a href="{relative_path}/admin/topics"><i class="fa fa-fw fa-book"></i> Topics</a></li>
<li><a href="{relative_path}/admin/groups"><i class="icon-group"></i> Groups</a></li> <li><a href="{relative_path}/admin/themes"><i class="fa fa-fw fa-th"></i> Themes</a></li>
<li><a href='{relative_path}/admin/topics'><i class='icon-book'></i> Topics</a></li> <li><a href="{relative_path}/admin/plugins"><i class="fa fa-fw fa-code-fork"></i> Plugins</a></li>
<li><a href='{relative_path}/admin/themes'><i class='icon-th'></i> Themes</a></li> <li><a href="{relative_path}/admin/settings"><i class="fa fa-fw fa-cogs"></i> Settings</a></li>
<li><a href='{relative_path}/admin/plugins'><i class='icon-code-fork'></i> Plugins</a></li> <li><a href="{relative_path}/admin/database"><i class="fa fa-fw fa-hdd-o"></i> Database</a></li>
<li><a href='{relative_path}/admin/settings'><i class='icon-cogs'></i> Settings</a></li> <li><a href="{relative_path}/admin/logger"><i class="fa fa-fw fa-th"></i> Logger</a></li>
<li><a href='{relative_path}/admin/redis'><i class='icon-hdd'></i> Redis</a></li> <li><a href="{relative_path}/admin/motd"><i class="fa fa-fw fa-comment"></i> MOTD</a></li>
<li><a href='{relative_path}/admin/logger'><i class='icon-th'></i> Logger</a></li> <li><a href="{relative_path}/admin/events"><i class="fa fa-fw fa-calendar-o"></i> Events</a></li>
<li><a href="{relative_path}/admin/motd"><i class="icon-comment"></i> MOTD</a></li>
</ul> </ul>
</div> </div>
<div class="well sidebar-nav"> <div class="well sidebar-nav">
<ul class="nav nav-list"> <ul class="nav nav-list">
<li class="nav-header">Social Authentication</li> <li class="nav-header">Social Authentication</li>
<li><a href='{relative_path}/admin/twitter'><i class='icon-twitter-sign'></i> Twitter</a></li> <li><a href="{relative_path}/admin/twitter"><i class="fa fa-fw fa-twitter-square"></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/facebook"><i class="fa fa-fw fa-facebook-square"></i> Facebook</a></li>
<li><a href='{relative_path}/admin/gplus'><i class='icon-google-plus-sign'></i> Google+</a></li> <li><a href="{relative_path}/admin/gplus"><i class="fa fa-fw fa-google-plus-square"></i> Google+</a></li>
<!-- BEGIN authentication -->
<li>
<a href="{relative_path}/admin{authentication.route}"><i class="fa fa-fw {authentication.icon}"></i> {authentication.name}</a>
</li>
<!-- END authentication -->
</ul> </ul>
</div> </div>
<div class="well sidebar-nav"> <div class="well sidebar-nav">
@@ -123,7 +127,7 @@
<li class="nav-header">Plugins</li> <li class="nav-header">Plugins</li>
<!-- BEGIN plugins --> <!-- BEGIN plugins -->
<li> <li>
<a href='{relative_path}/admin{plugins.route}'><i class="{plugins.icon}"></i> {plugins.name}</a> <a href="{relative_path}/admin{plugins.route}"><i class="fa fa-fw {plugins.icon}"></i> {plugins.name}</a>
</li> </li>
<!-- END plugins --> <!-- END plugins -->
</ul> </ul>
@@ -132,18 +136,18 @@
<ul class="nav nav-list"> <ul class="nav nav-list">
<li class="nav-header">Unit Tests</li> <li class="nav-header">Unit Tests</li>
<ul class="nav nav-list"> <ul class="nav nav-list">
<li><a href='{relative_path}/admin/testing/categories'>Categories</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/topics">Topics</a></li>
<li><a href='{relative_path}/admin/testing/posts'>Posts</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/accounts">Accounts</a></li>
<li><a href='{relative_path}/admin/testing/chat'>Chat</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/notifications">Notifications</a></li>
<li><a href='{relative_path}/admin/testing/friends'>Friends</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/feed">RSS Feed</a></li>
<li><a href='{relative_path}/admin/testing/emails'>Emails</a></li>--> <li><a href="{relative_path}/admin/testing/emails">Emails</a></li>-->
</ul> </ul>
</ul> </ul>
</div><!--/.well --> </div><!--/.well -->
</div><!--/span--> </div><!--/span-->
<div class="col-md-9" id="content"> <div class="col-sm-9" id="content">

View File

@@ -3,10 +3,10 @@
<h1>Welcome to NodeBB</h1> <h1>Welcome to NodeBB</h1>
<br /> <br />
<p> <p>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-comment"></i> NodeBB Forum</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="fa fa-comment"></i> NodeBB Forum</a>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-github-alt"></i> Get Plugins</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="fa fa-github-alt"></i> Get Plugins</a>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-github-alt"></i> Get Themes</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="fa fa-github-alt"></i> Get Themes</a>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-twitter"></i> dcplabs</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="fa fa-twitter"></i> dcplabs</a>
</p> </p>
<p><small>You are running <strong>NodeBB v{version}</strong>. Always make sure that your <strong>NodeBB</strong> is up to date for the latest security patches and bug fixes.</small></p> <p><small>You are running <strong>NodeBB v{version}</strong>. Always make sure that your <strong>NodeBB</strong> is up to date for the latest security patches and bug fixes.</small></p>
</div> </div>

View File

@@ -12,11 +12,14 @@
<input type="text" class="form-control" placeholder="Keywords describing your community, comma-seperated" data-field="keywords" /><br /> <input type="text" class="form-control" placeholder="Keywords describing your community, comma-seperated" data-field="keywords" /><br />
<label>Site Logo</label> <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="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 /> <input id="uploadLogoBtn" type="button" class="btn btn-default" value="Upload Logo"></input> <br />
<label>Imgur Client ID</label> <label>Imgur Client ID</label>
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br /> <input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
<label>Maximum User Image Size</label> <label>Maximum User Image Size</label>
<input type="text" class="form-control" placeholder="Maximum size of uploaded user images in kilobytes" data-field="maximumProfileImageSize" /> <input type="text" class="form-control" placeholder="Maximum size of uploaded user images in kilobytes" data-field="maximumProfileImageSize" /><br />
<label>Favicon</label><br />
<input id="faviconUrl" type="text" class="form-control" placeholder="favicon.ico" data-field="brand:favicon" /><br />
<input id="uploadFaviconBtn" type="button" class="btn btn-default" value="Upload Favicon"></input> <br />
</form> </form>
</div> </div>
@@ -26,6 +29,11 @@
<p>Use <strong>privilege thresholds</strong> to manage how much reputation a user must gain to receive moderator access.</p><br /> <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" data-field="privileges:manage_topic"><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 /> <strong>Manage Content</strong><br /> <input type="text" class="form-control" value="1000" data-field="privileges:manage_content"><br />
<div class="checkbox">
<label>
<input type="checkbox" data-field="privileges:disabled"> <strong>Disable Privilege Threshold System</strong>
</label>
</div>
</div> </div>
</form> </form>
@@ -55,15 +63,38 @@
</div> </div>
</form> </form>
<form>
<h3>Profile Settings</h3>
<div class="alert alert-warning">
<div class="checkbox">
<label>
<input type="checkbox" data-field="profile:convertProfileImageToPNG"> <strong>Convert profile image uploads to PNG</strong>
</label>
</div>
</div>
</form>
<form> <form>
<h3>User Settings</h3> <h3>User Settings</h3>
<div class="alert alert-warning"> <div class="alert alert-warning">
<div class="checkbox">
<label>
<input type="checkbox" data-field="allowRegistration" checked> <strong>Allow registration</strong>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" data-field="disableSignatures"> <strong>Disable signatures</strong>
</label>
</div>
<strong>Minimum Username Length</strong><br /> <strong>Minimum Username Length</strong><br />
<input type="text" class="form-control" value="2" data-field="minimumUsernameLength"><br /> <input type="text" class="form-control" value="2" data-field="minimumUsernameLength"><br />
<strong>Maximum Username Length</strong><br /> <strong>Maximum Username Length</strong><br />
<input type="text" class="form-control" value="16" data-field="maximumUsernameLength"><br /> <input type="text" class="form-control" value="16" data-field="maximumUsernameLength"><br />
<strong>Minimum Password Length</strong><br /> <strong>Minimum Password Length</strong><br />
<input type="text" class="form-control" value="6" data-field="minimumPasswordLength"><br /> <input type="text" class="form-control" value="6" data-field="minimumPasswordLength"><br />
<strong>Maximum Signature Length</strong><br />
<input type="text" class="form-control" value="255" data-field="maximumSignatureLength"><br />
</div> </div>
</form> </form>
@@ -72,17 +103,35 @@
<div class="alert alert-warning"> <div class="alert alert-warning">
<strong>Post Delay</strong><br /> <input type="text" class="form-control" value="10000" data-field="postDelay"><br /> <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 Title Length</strong><br /> <input type="text" class="form-control" value="3" data-field="minimumTitleLength"><br />
<strong>Maximum Title Length</strong><br /> <input type="text" class="form-control" value="255" data-field="maximumTitleLength"><br />
<strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br /> <strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br />
<strong>Chat Messages To Display</strong><br /> <input type="text" class="form-control" value="50" data-field="chatMessagesToDisplay"><br />
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" data-field="allowGuestPosting"> <strong>Allow guests to post without logging in</strong> <input type="checkbox" data-field="allowGuestPosting"> <strong>Allow guests to post without logging in</strong>
</label> </label>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" data-field="allowGuestSearching"> <strong>Allow guests to search without logging in</strong>
</label>
</div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" data-field="useOutgoingLinksPage"> <strong>Use Outgoing Links Warning Page</strong> <input type="checkbox" data-field="useOutgoingLinksPage"> <strong>Use Outgoing Links Warning Page</strong>
</label> </label>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" data-field="disableSocialButtons"> <strong>Disable social buttons</strong>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" data-field="allowFileUploads"> <strong>Allow users to upload regular files</strong>
</label>
</div>
<strong>Maximum File Size</strong><br /> <input type="text" class="form-control" value="2048" data-field="maximumFileSize"><br />
</div> </div>
</form> </form>

View File

@@ -6,7 +6,7 @@
The following themes are currently installed in this NodeBB instance. The following themes are currently installed in this NodeBB instance.
</p> </p>
<ul class="themes" id="installed_themes"> <ul class="themes" id="installed_themes">
<li><i class="icon-refresh icon-spin"></i> Checking for installed themes...</li> <li><i class="fa fa-refresh fa-spin"></i> Checking for installed themes...</li>
</ul> </ul>
<h3>Bootswatch Themes</h3> <h3>Bootswatch Themes</h3>
@@ -15,7 +15,7 @@
with Bootstrap as a base theme. with Bootstrap as a base theme.
</p> </p>
<ul class="themes" id="bootstrap_themes"> <ul class="themes" id="bootstrap_themes">
<li><i class="icon-refresh icon-spin"></i> Loading Themes</li> <li><i class="fa fa-refresh fa-spin"></i> Loading Themes</li>
</ul> </ul>
<h3>Revert to Default</h3> <h3>Revert to Default</h3>

View File

@@ -6,14 +6,14 @@
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li data-tid="{topics.tid}" data-locked="{topics.locked}" data-pinned="{topics.pinned}" data-deleted="{topics.deleted}"> <li data-tid="{topics.tid}" data-locked="{topics.locked}" data-pinned="{topics.pinned}" data-deleted="{topics.deleted}">
<div class="btn-group pull-right"> <div class="btn-group pull-right">
<button data-action="pin" class="btn"><i class="icon-pushpin"></i></button> <button data-action="pin" class="btn"><i class="fa fa-thumb-tack"></i></button>
<button data-action="lock" class="btn"><i class="icon-lock"></i></button> <button data-action="lock" class="btn"><i class="fa fa-lock"></i></button>
<button data-action="delete" class="btn"><i class="icon-trash"></i></button> <button data-action="delete" class="btn"><i class="fa fa-trash-o"></i></button>
</div> </div>
<a target="_blank" href="{relative_path}/topic/{topics.slug}">{topics.title}</a> <a target="_blank" href="{relative_path}/topic/{topics.slug}">{topics.title}</a>
<ul> <ul>
<li><i class="icon-time"></i> Posted <span class="timeago" title="{topics.relativeTime}"></span> by {topics.username}</li> <li><i class="fa fa-clock-o"></i> Posted <span class="timeago" title="{topics.relativeTime}"></span> by {topics.username}</li>
<li><i class="icon-comments"></i> {topics.postcount} post(s)</li> <li><i class="fa fa-comments"></i> {topics.postcount} post(s)</li>
</ul> </ul>
<div class="clear"></div> <div class="clear"></div>
</li> </li>

View File

@@ -1,4 +1,6 @@
<h1>Users</h1> <h1>Users</h1>
<button id="createUser" class="btn btn-primary">Create User</button>
<hr /> <hr />
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li class='active'><a href='/admin/users/latest'>Latest Users</a></li> <li class='active'><a href='/admin/users/latest'>Latest Users</a></li>
@@ -10,7 +12,7 @@
<br /> <br />
<div class="search {search_display} well"> <div class="search {search_display} well">
<input class="form-control" id="search-user" type="text" placeholder="Enter a username to search"/><br /> <input class="form-control" id="search-user" type="text" placeholder="Enter a username to search"/><br />
<i class="icon-spinner icon-spin none"></i> <i class="fa fa-spinner fa-spin none"></i>
<span id="user-notfound-notify" class="label label-danger hide">User not found!</span><br/> <span id="user-notfound-notify" class="label label-danger hide">User not found!</span><br/>
</div> </div>
@@ -24,13 +26,16 @@
<a href="/user/{users.userslug}">{users.username}</a> <a href="/user/{users.userslug}">{users.username}</a>
<br/> <br/>
<div title="reputation"> <div title="reputation">
<i class='icon-star'></i> <i class='fa fa-star'></i>
<span id='reputation'>{users.reputation}</span> <span id='reputation'>{users.reputation}</span>
</div> </div>
<div title="post count"> <div title="post count">
<i class='icon-pencil'></i> <i class='fa fa-pencil'></i>
<span id='postcount'>{users.postcount}</span> <span id='postcount'>{users.postcount}</span>
</div> </div>
<div>
<a href="#" class="btn btn-default admin-btn">Make Admin</a>
</div>
<div> <div>
<a href="#" class="btn btn-default ban-btn">Ban</a> <a href="#" class="btn btn-default ban-btn">Ban</a>
</div> </div>
@@ -38,6 +43,47 @@
<!-- END users --> <!-- END users -->
</ul> </ul>
<div class="modal fade" id="create-modal">
<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>
<h4 class="modal-title">Create User</h4>
</div>
<div class="modal-body">
<div class="alert alert-danger hide" id="create-modal-error"></div>
<form>
<div class="form-group">
<label for="group-name">User Name</label>
<input type="text" class="form-control" id="create-user-name" placeholder="User Name" />
</div>
<div class="form-group">
<label for="group-name">Email</label>
<input type="text" class="form-control" id="create-user-email" placeholder="Email of this user" />
</div>
<div class="form-group">
<label for="group-name">Password</label>
<input type="password" class="form-control" id="create-user-password" placeholder="Password" />
</div>
<div class="form-group">
<label for="group-name">Password Confirm</label>
<input type="password" class="form-control" id="create-user-password-again" placeholder="Password" />
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="create-modal-go">Create</button>
</div>
</div>
</div>
</div>
<div class="text-center {loadmore_display}"> <div class="text-center {loadmore_display}">
<button id="load-more-users-btn" class="btn btn-primary">Load More</button> <button id="load-more-users-btn" class="btn btn-primary">Load More</button>
</div> </div>

View File

@@ -3,19 +3,20 @@
<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a> <a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
</li> </li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">{category_name} <a target="_blank" href="../{category_id}.rss"><i class="icon-rss-sign"></i></a></span> <span itemprop="title">{category_name} <a target="_blank" href="../{category_id}.rss"><i class="fa fa-rss-square"></i></a></span>
</li> </li>
<div id="category_active_users"></div> <div id="category_active_users"></div>
</ol> </ol>
<div> <div>
<button id="new_post" class="btn btn-primary btn-lg {show_topic_button}">[[category:new_topic_button]]</button> <button id="new_post" class="btn btn-primary {show_topic_button}">[[category:new_topic_button]]</button>
<!-- IF !disableSocialButtons -->
<div class="inline-block pull-right"> <div class="inline-block pull-right">
<a href="#" id="facebook-share"><i class="icon-facebook-sign icon-2x"></i></a>&nbsp; <a href="#" id="facebook-share"><i class="fa fa-facebook-square fa-2x"></i></a>&nbsp;
<a href="#" id="twitter-intent"><i class="icon-twitter-sign icon-2x"></i></a>&nbsp; <a href="#" id="twitter-intent"><i class="fa fa-twitter-square fa-2x"></i></a>&nbsp;
<a href="#" id="google-share"><i class="icon-google-plus-sign icon-2x"></i></a>&nbsp; <a href="#" id="google-share"><i class="fa fa-google-plus-square fa-2x"></i></a>&nbsp;
</div> </div>
<!-- ENDIF !disableSocialButtons -->
</div> </div>
<hr/> <hr/>
@@ -38,38 +39,38 @@
<meta itemprop="name" content="{topics.title}"> <meta itemprop="name" content="{topics.title}">
<span class="topic-title"> <span class="topic-title">
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong> <strong><i class="fa {topics.pin-icon}"></i> <i class="fa {topics.lock-icon}"></i></strong>
{topics.title} {topics.title}
</span> </span>
</h3> </h3>
</a> </a>
<small> <small>
<span class="topic-stats"> <span class="topic-stats">
posts [[category:posts]]
<strong>{topics.postcount}</strong> <strong class="human-readable-number" title="{topics.postcount}">{topics.postcount}</strong>
</span> </span>
| |
<span class="topic-stats"> <span class="topic-stats">
views [[category:views]]
<strong>{topics.viewcount}</strong> <strong class="human-readable-number" title="{topics.viewcount}">{topics.viewcount}</strong>
</span> </span>
| |
<span> <span>
<a href="/user/{topics.userslug}"> <a href="/user/{topics.userslug}">
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/> <img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
</a> </a>
posted <span class="timeago" title="{topics.relativeTime}"></span> [[category:posted]] <span class="timeago" title="{topics.relativeTime}"></span>
</span> </span>
<span class="pull-right hidden-xs"> <span class="pull-right hidden-xs">
<!-- IF topics.unreplied --> <!-- IF topics.unreplied -->
No one has replied [[category:no_replies]]
<!-- ELSE --> <!-- ELSE -->
<a href="/user/{topics.teaser_userslug}"> <a href="/user/{topics.teaser_userslug}">
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/> <img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
</a> </a>
<a href="../../topic/{topics.slug}#{topics.teaser_pid}"> <a href="../../topic/{topics.slug}#{topics.teaser_pid}">
replied [[category:replied]]
</a> </a>
<span class="timeago" title="{topics.teaser_timestamp}"></span> <span class="timeago" title="{topics.teaser_timestamp}"></span>
<!-- ENDIF topics.unreplied --> <!-- ENDIF topics.unreplied -->
@@ -83,43 +84,35 @@
</ul> </ul>
</div> </div>
<div class="col-md-3 {show_sidebar} category-sidebar"> <div class="col-md-3 {show_sidebar} category-sidebar">
<div class="panel panel-default">
<div class="sidebar-block img-thumbnail"> <div class="panel-heading">[[category:sidebar.recent_replies]]</div>
<div class="block-header"> <div class="panel-body recent-replies">
[[category:sidebar.recent_replies]]
</div>
<div class="block-content recent-replies">
<ul id="category_recent_replies"></ul> <ul id="category_recent_replies"></ul>
</div> </div>
</div> </div>
<div class="sidebar-block img-thumbnail">
<div class="block-header"> <div class="panel panel-default">
[[category:sidebar.active_participants]] <div class="panel-heading">[[category:sidebar.active_participants]]</div>
</div> <div class="panel-body active-users">
<div class="block-content active-users">
<!-- BEGIN active_users --> <!-- BEGIN active_users -->
<a data-uid="{active_users.uid}" href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded user-img" /></a> <a data-uid="{active_users.uid}" href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded user-img" /></a>
<!-- END active_users --> <!-- END active_users -->
</div> </div>
</div> </div>
<div class="sidebar-block img-thumbnail {moderator_block_class}">
<div class="block-header"> <div class="panel panel-default {moderator_block_class}">
[[category:sidebar.moderators]] <div class="panel-heading">[[category:sidebar.moderators]]</div>
</div> <div class="panel-body moderators">
<div class="block-content">
<!-- BEGIN moderators --> <!-- BEGIN moderators -->
<a href="/user/{moderators.userslug}"><img title="{moderators.username}" src="{moderators.picture}" class="img-rounded" /></a> <a href="/user/{moderators.userslug}"><img title="{moderators.username}" src="{moderators.picture}" class="img-rounded" /></a>
<!-- END moderators --> <!-- END moderators -->
</div> </div>
</div> </div>
<!-- BEGIN sidebars --> <!-- BEGIN sidebars -->
<div class="sidebar-block img-thumbnail {sidebars.block_class}"> <div class="panel panel-default">
<div class="block-header"> <div class="panel panel-default {sidebars.block_class}">{sidebars.header}</div>
{sidebars.header} <div class="panel-body">{sidebars.content}</div>
</div>
<div class="block-content">
{sidebars.content}
</div>
</div> </div>
<!-- END sidebars --> <!-- END sidebars -->
</div> </div>
@@ -128,4 +121,4 @@
<input type="hidden" template-variable="category_id" value="{category_id}" /> <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="twitter-intent-url" value="{twitter-intent-url}" />
<input type="hidden" template-variable="facebook-share-url" value="{facebook-share-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}" /> <input type="hidden" template-variable="google-share-url" value="{google-share-url}" />

View File

@@ -0,0 +1,36 @@
<div class="composer">
<div class="composer-container">
<input class="title" type="text" tabIndex="1" placeholder="Enter your topic title here..." />
<div class="btn-toolbar formatting-bar">
<div class="btn-group">
<span class="btn btn-link" tabindex="-1"><i class="fa fa-bold"></i></span>
<span class="btn btn-link" tabindex="-1"><i class="fa fa-italic"></i></span>
<span class="btn btn-link" tabindex="-1"><i class="fa fa-list"></i></span>
<span class="btn btn-link" tabindex="-1"><i class="fa fa-link"></i></span>
<span class="btn btn-link img-upload-btn hide" tabindex="-1">
<i class="fa fa-picture-o"></i>
</span>
<span class="btn btn-link file-upload-btn hide" tabindex="-1">
<i class="fa fa-upload"></i>
</span>
<form id="fileForm">
<input type="file" id="files" name="files[]" multiple class="hide"/>
</form>
</div>
<!-- <div class="btn btn-link pull-right">Preview</div> -->
</div>
<textarea tabIndex="2"></textarea>
<div class="preview"></div>
<div class="imagedrop"><div>Drag and Drop Images Here</div></div>
<div class="text-center upload-instructions hide">
<small>Upload images by dragging & dropping them</small>
</div>
<div class="btn-toolbar action-bar">
<div class="btn-group pull-right">
<button class="btn btn-default" data-action="discard" tabIndex="5"><i class="fa fa-times"></i> Discard</button>
<button data-action="post" class="btn btn-default" tabIndex="3"><i class="fa fa-check"></i> Submit</button>
</div>
</div>
<div class="resizer"><div class="trigger"><i class="fa fa-chevron-left"></i></div></div>
</div>
</div>

View File

@@ -4,7 +4,8 @@
"^admin/topics.*": "admin/topics", "^admin/topics.*": "admin/topics",
"^admin/categories.*": "admin/categories", "^admin/categories.*": "admin/categories",
"^admin/users.*": "admin/users", "^admin/users.*": "admin/users",
"^admin/redis.*": "admin/redis", "^admin/database.*": "admin/database",
"^admin/events.*": "admin/events",
"^admin/index.*": "admin/index", "^admin/index.*": "admin/index",
"^admin/themes.*": "admin/themes", "^admin/themes.*": "admin/themes",
"^admin/plugins/?$": "admin/plugins", "^admin/plugins/?$": "admin/plugins",

View File

@@ -2,6 +2,6 @@
<strong>{title}</strong> <strong>{title}</strong>
<p>{text}</p> <p>{text}</p>
<p> <p>
<a href="/">NodeBB Home</a> <a href="/">[[notification:back_to_home]]</a>
</p> </p>
</div> </div>

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