Compare commits

..
940 Commits
Author SHA1 Message Date
Wyatt Johnson 2c98aa2097 Merge pull request #368 from coralproject/asset-name-fixes
Asset Name + Profile Tab Bugs
2017-03-01 16:46:41 -07:00
Wyatt Johnson 23540cb2c2 Merge branch 'master' into asset-name-fixes 2017-03-01 16:43:25 -07:00
Wyatt Johnson 5114b64935 Merge pull request #367 from coralproject/move-config-around
removing dead code
2017-03-01 16:43:16 -07:00
Wyatt Johnson e08d737a69 Merge branch 'master' into asset-name-fixes 2017-03-01 16:42:37 -07:00
Wyatt Johnson 57fc4b8a3f Fixed test 2017-03-01 16:40:47 -07:00
Kim Gardner ca635a34cc Merge branch 'master' into move-config-around 2017-03-01 18:39:09 -05:00
Kim Gardner 4cb796e54f Merge pull request #366 from coralproject/reply-no-reply
make sure reply box works
2017-03-01 18:38:49 -05:00
Wyatt Johnson cefe872bf0 Fixed queries 2017-03-01 16:38:27 -07:00
Riley Davis 5aabf2b177 Merge branch 'master' into move-config-around 2017-03-01 16:36:31 -07:00
Riley Davis a839596c20 Merge branch 'master' into reply-no-reply 2017-03-01 16:34:42 -07:00
Riley Davis ed0df4b738 make sure reply box works 2017-03-01 16:32:01 -07:00
Kim Gardner 36b707f213 Merge pull request #365 from coralproject/config-wordz
add some missing translations
2017-03-01 18:08:55 -05:00
Riley Davis fa5a94c4be add some missing translations 2017-03-01 16:00:42 -07:00
Wyatt Johnson 472b087a6c Merge pull request #364 from coralproject/embed-stream-fixes
Touched up embed + stream query
2017-03-01 15:54:46 -07:00
Wyatt Johnson 3d13380fcc Touched up embed + stream query 2017-03-01 15:49:39 -07:00
Kim Gardner eeb82ff7bb Merge pull request #363 from coralproject/move-config-around
re-organize settings so they make a bit more sense
2017-03-01 17:30:56 -05:00
riley c4edbd4db4 removing dead code 2017-03-01 15:28:44 -07:00
Riley Davis 0c89dd6d98 sam's feedback 2017-03-01 15:23:17 -07:00
Riley Davis 97496f8b3d Merge branch 'master' into move-config-around 2017-03-01 14:44:27 -07:00
Riley Davis 62793f5947 keepin' it DRY 2017-03-01 14:40:10 -07:00
Kim Gardner 9b9715f39f Merge pull request #360 from coralproject/bugfix-suspect-reporting
Comments with suspect words should not show up as "Reported" by the user who posted them.
2017-03-01 16:10:18 -05:00
Kim Gardner fb68bc3d48 Merge branch 'master' into bugfix-suspect-reporting 2017-03-01 16:06:08 -05:00
Wyatt Johnson 4461f47428 Merge pull request #361 from coralproject/yarn-docker
Yarn fixes for Docker
2017-03-01 14:03:20 -07:00
Wyatt Johnson 9fb0f2f241 Merge branch 'master' into yarn-docker 2017-03-01 13:55:04 -07:00
Wyatt Johnson fb96d432ee Moved action creation upstream 2017-03-01 13:51:32 -07:00
Wyatt Johnson 49477ead6a Fixed temp issue 2017-03-01 13:47:08 -07:00
Kim Gardner 09eb2edb32 Merge branch 'master' into bugfix-suspect-reporting 2017-03-01 15:44:51 -05:00
Kim Gardner 516a05eca0 Merge pull request #359 from coralproject/bugfix-premod-optimistic
Preventing premod comments from being posted optimistically.
2017-03-01 15:44:34 -05:00
Kim Gardner 58e803223f Merge branch 'master' into bugfix-suspect-reporting 2017-03-01 15:41:22 -05:00
Kim Gardner 285430e1ce Merge branch 'master' into bugfix-premod-optimistic 2017-03-01 15:40:52 -05:00
Kim Gardner fdb051c810 Merge pull request #354 from coralproject/admin-login-upgrade
Admin login upgrade - RIP Communicorn part deux
2017-03-01 15:39:03 -05:00
Kim Gardner 43d2b29921 Merge branch 'master' into admin-login-upgrade 2017-03-01 15:35:01 -05:00
David Jay 9cb835b08b Setting action user_id to null on suspect words. 2017-03-01 14:49:42 -05:00
David Jay 0085d2bb04 Preventing premod comments from being posted optimistically. 2017-03-01 14:21:03 -05:00
Wyatt Johnson 2f05cec380 Removed yarn install (already has yarn!) 2017-03-01 11:50:32 -07:00
Wyatt Johnson f87525d0a4 Merge pull request #358 from coralproject/version-pin
Pinning to 7.6 until upstream issues resolved
2017-03-01 11:43:30 -07:00
Kim Gardner e56a04abf1 Merge branch 'master' into admin-login-upgrade 2017-03-01 13:42:56 -05:00
Riley Davis 4e5bcbc27f even more design updates 2017-03-01 11:42:03 -07:00
Wyatt Johnson af8c926296 Pinning to 7.6 until upstream issues resolved, see #357 2017-03-01 11:37:29 -07:00
Kim Gardner 4b39587837 Merge pull request #356 from coralproject/make-links-less-linky
Make links less linky
2017-03-01 13:31:44 -05:00
Kim Gardner 70c69e3752 Merge branch 'master' into make-links-less-linky 2017-03-01 13:27:13 -05:00
riley b346083d11 Merged master into admin-login-upgrade 2017-03-01 11:19:48 -07:00
Kim Gardner 23917944b2 Merge pull request #353 from coralproject/bugfix-user-state
Refetching on login and logout.
2017-03-01 13:14:26 -05:00
Riley Davis 00b2bf37ee fix height of the table 2017-03-01 11:14:13 -07:00
David Jay 3b04dab0d5 Merge branch 'master' into bugfix-user-state 2017-03-01 12:57:40 -05:00
Wyatt Johnson 45b14d026d Added more conditional checks to exclusion 2017-03-01 10:49:47 -07:00
Riley Davis bbf70aedab put it back to 5 minutes 2017-03-01 10:47:31 -07:00
Riley Davis 045fa3e2a9 also ignore this 2017-03-01 10:42:01 -07:00
Riley Davis 58870e1630 Merged branch master into admin-login-upgrade 2017-03-01 10:40:51 -07:00
Riley Davis 04ab00d053 ignore this 2017-03-01 10:40:40 -07:00
Riley Davis 1f70a2d3eb Merged branch master into make-links-less-linky 2017-03-01 10:37:50 -07:00
Riley Davis 674585adeb design updates 2017-03-01 10:37:39 -07:00
Wyatt Johnson 74c457a0b0 Merge pull request #349 from coralproject/production-mode
Routes Production mode
2017-03-01 09:58:53 -07:00
Wyatt Johnson 79296999c0 Merge branch 'master' into production-mode 2017-03-01 09:52:46 -07:00
Kim Gardner a984fc9beb Merge pull request #355 from coralproject/comment-component
UX Comment Component
2017-03-01 11:03:54 -05:00
Belen Curcio 6cd0c12ba4 tweaks 2017-03-01 12:35:59 -03:00
Belen Curcio 545410b27e renaming duplicate statement 2017-03-01 11:50:05 -03:00
Belen Curcio 453894be80 Merge 2017-03-01 11:46:28 -03:00
Belen Curcio ea0602b7a5 Design Comment Component 2017-03-01 11:42:08 -03:00
Belen Curcio c33ae1f7f8 Merge branch 'master' of github.com:coralproject/talk into comment-component 2017-03-01 11:08:46 -03:00
Kim Gardner 87dddaf5b3 Merge branch 'master' into admin-login-upgrade 2017-03-01 09:01:15 -05:00
Kim Gardner 0af08fcaea Merge pull request #352 from coralproject/fix-permalink
Fix permalink
2017-03-01 09:00:59 -05:00
Kim Gardner 945e576ce7 Merge branch 'master' into fix-permalink 2017-03-01 08:57:17 -05:00
Belen Curcio 42b380b942 Flag box 2017-02-28 22:35:37 -03:00
Belen Curcio 339aefa72a Merge branch 'master' of github.com:coralproject/talk into comment-component 2017-02-28 21:26:57 -03:00
riley 7ccf77b1ad add reset password link 2017-02-28 17:10:51 -07:00
Belén Curcio cc9de0b02c Merge pull request #348 from coralproject/whizard-whitelist
Permitted Domains for Talk Install
2017-02-28 21:06:16 -03:00
Belen Curcio dc81ec1e80 Merge branch 'whizard-whitelist' of github.com:coralproject/talk into whizard-whitelist 2017-02-28 20:44:55 -03:00
Belen Curcio 1db0a75010 Merge 2017-02-28 20:42:03 -03:00
Riley Davis 1f3eb5f516 Merged branch master into admin-login-upgrade 2017-02-28 16:31:49 -07:00
riley 8f23ca678d add Alert component to coral-ui 2017-02-28 16:31:30 -07:00
David Jay 230488117f Merge branch 'master' into bugfix-user-state 2017-02-28 18:20:00 -05:00
David Jay d8117494d1 Cleaning up after merge conflict. 2017-02-28 18:18:17 -05:00
Wyatt Johnson e6b7098a6c Merge branch 'master' into production-mode 2017-02-28 16:10:27 -07:00
David Jay e68358a211 Merge branch 'master' into fix-permalink 2017-02-28 18:07:31 -05:00
Kim Gardner 7bd9411e73 Merge pull request #332 from coralproject/assets-by-like
Dashboard: Articles with most likes, articles with most flags
2017-02-28 16:57:47 -05:00
Riley Davis b369fb9101 re-rename 2017-02-28 14:38:19 -07:00
Riley Davis bb1aea63ba add a couple of routes 2017-02-28 14:33:57 -07:00
Riley Davis e2273f5810 change PermissionRequired to LoginView and create a login form. delete old login page 2017-02-28 14:32:01 -07:00
David Jay fed28b3eec Refetching on login and logout. 2017-02-28 16:13:51 -05:00
David Jay 037f5be377 Merge branch 'master' of github.com:coralproject/talk into fix-permalink 2017-02-28 14:28:32 -05:00
David Jay 62fe63d323 Scrolling to permalinked comment or reply. 2017-02-28 14:27:53 -05:00
David Jay 158f31d603 Highlighting permalinked comment. 2017-02-28 13:55:28 -05:00
Riley Davis c6885c8f1c Merge branch 'master' into assets-by-like 2017-02-28 11:16:26 -07:00
Riley Davis 86a6b545cc create a fragment for asset metrics. add some comments 2017-02-28 11:15:44 -07:00
Gabriela Rodríguez Berón 1db68a12a8 Merge branch 'master' into whizard-whitelist 2017-02-28 09:54:39 -08:00
Riley Davis 65e8e42ae6 remove query from Dashboard component per belen's advice 2017-02-28 10:51:14 -07:00
Kim Gardner b3b10efbba Use tú to match other translations 2017-02-28 12:35:20 -05:00
Kim Gardner 3bfde67d58 Copy updates with translations 2017-02-28 12:32:51 -05:00
Kim Gardner cdbc065c90 Merge pull request #351 from coralproject/mod-queue-sort
Fixing order bug with mod queue sorting.
2017-02-28 12:22:41 -05:00
David Jay b9297b6079 Merge branch 'master' into mod-queue-sort 2017-02-28 12:11:05 -05:00
David Jay 95a9259c06 Fixing bug with mod queue sorting. 2017-02-28 12:06:27 -05:00
David Jay bf23fc2974 Moving comment id into query. 2017-02-28 12:02:51 -05:00
Riley Davis ac3e6a12bd Merge branch 'master' into assets-by-like 2017-02-28 09:27:46 -07:00
Belen Curcio 061efd4734 Comment Component Design 2017-02-28 09:03:19 -03:00
Wyatt Johnson 87d64e575b Merge branch 'master' into whizard-whitelist 2017-02-27 15:52:16 -07:00
Wyatt Johnson aa88eb2374 Adjusted build process 2017-02-27 15:33:52 -07:00
Wyatt Johnson 6cd3c84853 Moved route definitions into env wrapped check, webpackified embed 2017-02-27 14:21:48 -07:00
David Jay 303148e5ba Merge pull request #345 from coralproject/bugfix-second-new-comments
Prevents "0" from appearing on empty comment streams
2017-02-27 15:34:40 -05:00
Belen Curcio 95876fa0e8 Merge branch 'master' of github.com:coralproject/talk into whizard-whitelist 2017-02-27 17:19:54 -03:00
Belen Curcio 555faa02f5 new translations for configure 2017-02-27 17:18:37 -03:00
Belen Curcio 1234a99a0d copy for install permitted domains updated 2017-02-27 17:16:19 -03:00
Belen Curcio b9e9d3e7fa status 2017-02-27 17:06:22 -03:00
Belen Curcio 628e8844f3 final translations 2017-02-27 17:04:14 -03:00
Belen Curcio b47fd35437 add user translations 2017-02-27 16:58:16 -03:00
David Jay 1b495b9b09 Merge branch 'master' into bugfix-second-new-comments 2017-02-27 14:57:24 -05:00
David Jay 373d0de576 Merge pull request #346 from coralproject/mod-queue-sort
Adding sort to moderation queue.
2017-02-27 14:57:09 -05:00
Belen Curcio 7341fdff4e add Organization translations 2017-02-27 16:54:03 -03:00
Belen Curcio 1e52c579cc Ínitial translations 2017-02-27 16:50:04 -03:00
Belen Curcio 013625df27 no validation for domains 2017-02-27 16:39:52 -03:00
David Jay ab16e5bfe7 Retrieving comment y id from backend 2017-02-27 14:26:20 -05:00
David Jay dfd211deda Merge branch 'master' into mod-queue-sort 2017-02-27 14:05:04 -05:00
Belen Curcio 9305853502 Renamed to PermittedDomains 2017-02-27 15:50:07 -03:00
Belen Curcio b1ec512721 Adding whitelist step, sending whitelist 2017-02-27 14:42:57 -03:00
David Jay 83e713d889 Merge branch 'master' into bugfix-second-new-comments 2017-02-27 12:34:11 -05:00
David Jay a47e2378e9 Merge pull request #331 from coralproject/dont-agree
Don't Agree flags
2017-02-27 12:32:56 -05:00
David Jay 491f254206 Merge branch 'mod-queue-sort' of github.com:coralproject/talk into mod-queue-sort 2017-02-27 12:30:04 -05:00
David Jay 2b22ce076c Merge branch 'master' into mod-queue-sort 2017-02-27 12:29:42 -05:00
David Jay 22793c02e2 Merge branch 'master' into dont-agree 2017-02-27 12:29:19 -05:00
David Jay 37b08fd4e4 Displaying oldest first by default. 2017-02-27 12:28:13 -05:00
Kim Gardner 7914296997 Merge pull request #347 from coralproject/header
[Tablet] - Header and Drawer
2017-02-27 11:50:30 -05:00
Belen Curcio e8a92b768c logout and version in drawer 2017-02-27 11:52:21 -03:00
Belen Curcio f85a6d4da1 Drawer 2017-02-27 11:49:53 -03:00
Kim Gardner 3a6a8ff199 Merge pull request #334 from coralproject/story-140080313-modal-dn
Prompt for username when signing up with Facebook
2017-02-27 08:08:19 -05:00
David Jay 847df91cbc Merge branch 'master' into bugfix-second-new-comments 2017-02-24 20:03:42 -05:00
gaba a429af3c0e Removes varialbe not used. 2017-02-24 16:10:39 -08:00
gaba d650779c1a Removes ; 2017-02-24 16:10:38 -08:00
David Jay 66a890458c Adding sort to moderation queue. 2017-02-24 17:42:29 -05:00
David Jay 6a9d8c0436 Removing refetch and prevent 0 from displaying on empty comment streams. 2017-02-24 15:51:43 -05:00
David Erwin f1c1214229 Merge branch 'master' into story-140080313-modal-dn 2017-02-24 15:40:11 -05:00
David Erwin 2739a01cd3 Merge pull request #341 from coralproject/notifications-alerts
copy updates and remove notification
2017-02-24 15:37:34 -05:00
Wyatt Johnson 212037891a Merge branch 'master' into assets-by-like 2017-02-24 13:36:54 -07:00
Riley Davis 11a664d446 Merge branch 'master'
Conflicts:
	client/coral-plugin-commentbox/CommentBox.js
2017-02-24 13:33:25 -07:00
David Erwin b981bea97f Merge pull request #343 from coralproject/updateCountCache-bug
fix Reply bug
2017-02-24 15:31:16 -05:00
Riley Davis d20fa3f497 Merge branch 'master' into notifications-alerts 2017-02-24 13:29:44 -07:00
Riley Davis 5c441e90e7 do not update countCache if the CommentBox is in a reply 2017-02-24 13:08:48 -07:00
Riley Davis 893fe97e66 Merged branch master into assets-by-like 2017-02-24 12:58:33 -07:00
Riley Davis aefcb29877 remove comment count from results 2017-02-24 12:58:06 -07:00
David Jay dcc16dc598 Removing extra semicolon. 2017-02-24 14:45:47 -05:00
David Jay 4015bea1db Merge branch 'master' into story-140080313-modal-dn 2017-02-24 14:39:51 -05:00
Riley Davis 1058bff8ad remove logs 2017-02-24 11:30:59 -07:00
Riley Davis 38e093aac3 remove most liked comments widget 2017-02-24 11:26:23 -07:00
Wyatt Johnson e14389c492 Merge pull request #340 from coralproject/heroku-setup-patch
Adjusted route for Heroku app.json
2017-02-24 11:20:01 -07:00
Wyatt Johnson 6566505fc1 Adjusted route 2017-02-24 11:15:46 -07:00
Riley Davis 886240a7bc translations. remove console.log on frontend. remove notification on POST moderation 2017-02-24 11:11:18 -07:00
Wyatt Johnson 799727264e Removed log 2017-02-24 09:57:57 -07:00
Wyatt Johnson f5277ce5e3 Patched silly bug 2017-02-24 09:57:24 -07:00
Riley Davis c283f193de remove console.log 2017-02-24 09:07:03 -07:00
riley c8c3c4acaf lint 2017-02-24 08:59:23 -07:00
Riley Davis dc5f95ec7f simplify moderationqueue 2017-02-24 08:50:52 -07:00
Riley Davis 5d1139dbdd Merge branch 'master'
Conflicts:
	client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
2017-02-24 08:49:30 -07:00
Kim Gardner a8d629d680 Merge pull request #339 from coralproject/comment-design-pass
Comment stream design pass
2017-02-23 17:48:48 -05:00
Riley Davis be25d6f56d "works" 2017-02-23 15:46:44 -07:00
Riley Davis 2a0f4906a7 Merged branch assets-by-like into assets-by-like 2017-02-23 15:23:15 -07:00
David Jay b150668d86 Merge branch 'comment-design-pass' of github.com:coralproject/talk into comment-design-pass 2017-02-23 17:11:21 -05:00
David Jay 39536046c9 Fixing permalink icon and adding padding to comment content. 2017-02-23 17:08:35 -05:00
David Jay e735fb800a Updating button and signin style. 2017-02-23 17:02:35 -05:00
gaba 92aa107eeb Merge branch 'master' into story-140080313-modal-dn 2017-02-23 13:58:10 -08:00
gaba 022667c9ae Brings some of the components into FakeComment 2017-02-23 13:55:51 -08:00
Wyatt Johnson 30b6259e87 Patched bug with asset metrics 2017-02-23 14:43:25 -07:00
Riley Davis 2f314c6937 omg, a bunch of UI madness 2017-02-23 14:29:51 -07:00
David Jay 66ef4e38a8 Merge branch 'master' into comment-design-pass 2017-02-23 16:28:20 -05:00
David Jay 423095360e Merge pull request #336 from coralproject/new-comments
View New Comments
2017-02-23 16:25:16 -05:00
David Jay 2456fe5145 Merge branch 'master' into comment-design-pass 2017-02-23 16:22:35 -05:00
David Jay 761d0aaa3a Merge branch 'master' into new-comments 2017-02-23 16:21:36 -05:00
David Jay c1e3aec181 Updating styles. 2017-02-23 16:20:20 -05:00
Kim Gardner 498eb63e1a Merge pull request #330 from coralproject/empty-states
Empty states for different lists
2017-02-23 16:18:24 -05:00
Kim Gardner 532f2d92ca Merge branch 'master' into empty-states 2017-02-23 16:15:05 -05:00
Wyatt Johnson 58af8ed003 Merge pull request #335 from coralproject/suspect-word-flag-reason
Fixed issue related to action create on suspect word match
2017-02-23 14:14:46 -07:00
Wyatt Johnson b50808d365 Merge branch 'master' into suspect-word-flag-reason 2017-02-23 14:11:21 -07:00
David Jay 3e2634be76 Merge branch 'master' into new-comments 2017-02-23 16:11:01 -05:00
David Jay 4cc3426af4 Adding bolding, adding translations to settings page. 2017-02-23 16:07:41 -05:00
David Jay 471607403c Switching font. 2017-02-23 15:26:02 -05:00
David Jay d73a6a87a4 Adding border radius to tag label. 2017-02-23 15:19:38 -05:00
David Jay da732ece14 Updating button padding and font size. 2017-02-23 15:03:09 -05:00
David Jay f795a96f46 Returning dontagree id on post. 2017-02-23 13:19:09 -05:00
gaba 5d0ea6a880 Rmove continue button, fix bug with facebook signup. 2017-02-23 09:48:59 -08:00
David Jay 3f5d751a80 Setting reason to null for dontAgree. 2017-02-23 12:24:32 -05:00
David Jay 8cb0f9cd90 Merge branch 'master' into dont-agree 2017-02-23 12:12:53 -05:00
David Jay 2dda81f257 Merge pull request #338 from coralproject/admin-login
show login page instead of communicorn :'(
2017-02-23 12:12:21 -05:00
Riley Davis e3b9799d4a nested ternaries hurt me 2017-02-23 09:58:31 -07:00
Riley Davis 7ae81fd172 fixing mixed types 2017-02-23 09:43:50 -07:00
Riley Davis 953c749bd2 show login page instead of communicorn :'( 2017-02-22 16:14:14 -07:00
Riley Davis 197569714b Merged branch assets-by-like into assets-by-like 2017-02-22 14:25:19 -07:00
Wyatt Johnson 9e759c7667 Adjusted BE beheviour 2017-02-22 13:55:48 -07:00
Riley Davis e6b78a3e63 change some copy 2017-02-22 13:33:50 -07:00
David Jay 9d13a95a7c Merge branch 'master' into new-comments 2017-02-22 15:20:39 -05:00
David Jay 27bd1404b3 Adding es translation. 2017-02-22 15:13:46 -05:00
David Jay bb0b3f461a Moving polling interval to constant. 2017-02-22 15:11:50 -05:00
Riley Davis 111b956522 change index route to Dashboard component 2017-02-22 13:10:50 -07:00
David Jay 6becb88b56 Adding style to new comments button. 2017-02-22 15:07:32 -05:00
gaba 32bc754bf7 It needs to send the translation key 2017-02-22 11:22:51 -08:00
David Jay 096bd12df6 Loading new comments. 2017-02-22 13:45:02 -05:00
Riley Davis 5ef5a01f59 Merged branch master into assets-by-like 2017-02-22 11:15:35 -07:00
Riley Davis 2acd099f3f add empty state for asset search 2017-02-22 11:15:13 -07:00
David Jay dd09ddf968 Adding NewCount component. 2017-02-22 13:13:25 -05:00
Riley Davis 691d001213 simplify a bit 2017-02-22 10:49:48 -07:00
Riley Davis bc48496327 add most liked comments widget 2017-02-22 10:47:23 -07:00
Wyatt Johnson 80706d2fdf fixed issue related to action create on suspect word match 2017-02-22 10:33:47 -07:00
Wyatt Johnson 6398e2faad Fixed issue with comment hydration 2017-02-22 09:32:51 -07:00
Gabriela Rodríguez Berón 224e58f3e2 Merge branch 'master' into story-140080313-modal-dn 2017-02-22 08:16:51 -08:00
Gabriela Rodríguez Berón b29614256b Merge branch 'master' into empty-states 2017-02-22 08:13:44 -08:00
Gabriela Rodríguez Berón 3e2b8b1a20 Merge branch 'master' into dont-agree 2017-02-22 08:09:55 -08:00
Wyatt Johnson 07a956e582 Merge pull request #333 from coralproject/rest-depreciation
Cleaned up some routes
2017-02-22 08:13:02 -07:00
gaba 2c60285981 The component needs the banned value. 2017-02-22 07:00:15 -08:00
gaba 1b79a10580 Change username when it changes. 2017-02-21 16:36:50 -08:00
Riley Davis e7c4acde4d small bugfix 2017-02-21 16:11:56 -07:00
gaba 5d0730b4d5 Adds fake comment component. 2017-02-21 14:52:19 -08:00
David Jay ca107afb9b Avoiding new comment button when a comment is posted. 2017-02-21 17:29:58 -05:00
Wyatt Johnson d7a6bf9e1f Cleaned up some routes 2017-02-21 15:24:44 -07:00
David Jay 9149b737f5 Retrieving counts and displaying new comments to user. 2017-02-21 17:23:09 -05:00
Wyatt Johnson 2360f280d2 Added commentMetrics edge. 2017-02-21 14:30:52 -07:00
Riley Davis 30fbcd30a9 Merge branch 'master' into assets-by-like 2017-02-21 14:18:22 -07:00
Riley Davis f32fe2629c css is the worst 2017-02-21 14:16:02 -07:00
Riley Davis 5d94a98305 Like and Flag summaries work again 2017-02-21 14:05:49 -07:00
gaba 02c05e8ae1 CSS, translations and code for new create username dialog. 2017-02-21 12:43:02 -08:00
David Jay 0aaa52e01f Merge branch 'master' into dont-agree 2017-02-21 15:02:33 -05:00
David Jay 3377aa3da2 Displaying DontAgree actions as flags. 2017-02-21 14:55:00 -05:00
Riley Davis f76befd6ac no comments in user list 2017-02-21 12:48:01 -07:00
David Jay 1d8ce1ae34 Posting DontAgree actions. 2017-02-21 14:41:33 -05:00
Riley Davis 9000778d8f empty result for users in Community 2017-02-21 12:39:29 -07:00
Riley Davis e24a0caa20 add EmptyCard component. fix typos. more PropTypes. 2017-02-21 12:08:02 -07:00
Riley Davis 364cf1808e assets by like? 2017-02-21 10:28:33 -07:00
David Erwin 17813e23a9 Merge pull request #327 from coralproject/error_translation_key
Error translation key
2017-02-17 20:13:11 -05:00
David Erwin 0563b93d39 Merge branch 'master' into error_translation_key 2017-02-17 20:07:49 -05:00
David Erwin df53b101ab Merge pull request #316 from coralproject/update-on-post-comment
Apollo update on post comment
2017-02-17 20:07:40 -05:00
David Erwin 6f17a84097 Merge branch 'master' into update-on-post-comment 2017-02-17 20:02:41 -05:00
Wyatt Johnson 09513c0e83 Merge pull request #329 from coralproject/docker-fixx
Added yarn.lock to Dockerfile
2017-02-17 18:02:26 -07:00
Wyatt Johnson 301c8e829f Added production flag back 2017-02-17 17:56:44 -07:00
Wyatt Johnson afcdacff0d Added yarn.lock to Dockerfile 2017-02-17 17:49:59 -07:00
Riley Davis 8866366d3c storing my place in case we want to come back to this 2017-02-17 16:07:34 -07:00
David Erwin a8396481f9 Merge pull request #328 from coralproject/graph-tests
Initial graph tests
2017-02-17 18:01:17 -05:00
Wyatt Johnson 4cccf09a87 spelling... 2017-02-17 15:53:23 -07:00
Wyatt Johnson 3e4dc9c8b2 Merge branch 'master' into graph-tests 2017-02-17 15:50:21 -07:00
Wyatt Johnson a00befafcb Initial graph tests 2017-02-17 15:48:46 -07:00
Gabriela Rodríguez Berón 31dda920c6 Merge branch 'master' into error_translation_key 2017-02-17 14:30:00 -08:00
gaba 9f168e627a It uses message and not translation key. 2017-02-17 14:19:30 -08:00
Riley Davis 2506442453 toISOString 2017-02-17 14:10:21 -07:00
Riley Davis 6eb4725dd9 remove ternary from return statement 2017-02-17 14:03:37 -07:00
David Erwin 5d1bc5fb5a Merge branch 'master' into update-on-post-comment 2017-02-17 13:52:25 -05:00
David Erwin 62ee81e5a9 Merge pull request #326 from coralproject/stream-settings
Stream Settings
2017-02-17 13:51:26 -05:00
gaba 5a09beb727 It has to be translation_key and not message. 2017-02-17 10:12:37 -08:00
Belen Curcio 2d374fcdab Úsing the db url 2017-02-17 15:08:55 -03:00
Belen Curcio 54f0705f0f Merge branch 'master' of github.com:coralproject/talk into stream-settings 2017-02-17 15:05:17 -03:00
David Erwin c92e336c7f Merge pull request #321 from coralproject/the-answer-is-42
Counts on Mod Queues
2017-02-17 13:00:24 -05:00
Belén Curcio 98a34892ff Merge branch 'master' into the-answer-is-42 2017-02-17 14:42:44 -03:00
David Jay f6a6f443a9 Merge branch 'master' into update-on-post-comment 2017-02-17 12:30:42 -05:00
Gabriela Rodríguez Berón bf387af586 Merge pull request #325 from coralproject/displayNameFORALL
From displayName to username everywhere
2017-02-17 09:24:38 -08:00
Belen Curcio 051978a1ee to the configure 2017-02-17 14:21:47 -03:00
Belen Curcio 761afeaf4f Stream cog, settings 2017-02-17 14:15:16 -03:00
Belen Curcio 25f366c5f1 Comment Count Component 2017-02-17 13:55:35 -03:00
Belen Curcio 7eca9db63c null assetIDs and old typo 2017-02-17 13:53:34 -03:00
gaba dd3435fffe Replace with translation_key to manage error translation. 2017-02-17 08:42:48 -08:00
David Jay a35af36ee1 Handling premod in optimistic comment updates. 2017-02-17 11:26:19 -05:00
Belen Curcio 41d0610985 PropTypes 2017-02-17 13:22:41 -03:00
gaba 3bfac591b3 An N was missing... 2017-02-17 08:20:27 -08:00
David Jay d10936749d Adding DontAgreeActionSummaries 2017-02-17 11:14:42 -05:00
gaba 5370a6f81a A few more changes from display to username. 2017-02-17 08:05:32 -08:00
David Erwin 8502b43974 Updating more displayname to username 2017-02-17 10:04:15 -05:00
Belén Curcio e8f711ff9c Merge branch 'master' into update-on-post-comment 2017-02-17 02:38:22 -03:00
gaba a903ceb643 Merge branch 'master' into displayNameFORALL 2017-02-16 16:13:36 -08:00
gaba 4765973a58 Other small changes and fix bug because displayname instead displayName... 2017-02-16 16:07:19 -08:00
Wyatt Johnson 83f2f7b86b Merge branch 'master' into the-answer-is-42 2017-02-16 16:49:46 -07:00
Riley Davis f925e6a8ea fix bug when likes or flags were 0 for an article 2017-02-16 16:49:14 -07:00
David Erwin a267397f06 Standardizing username 2017-02-16 16:49:14 -07:00
Wyatt Johnson 84dcf3b3b6 Added some comments 2017-02-16 16:49:14 -07:00
Wyatt Johnson 1e81ec9f4e Added new commentCount edge, added more docs 2017-02-16 16:49:14 -07:00
gaba 902cd8300d Adds back the timeout to close window. 2017-02-16 16:49:14 -07:00
Gabriela Rodríguez Berón e1383293d5 Merge pull request #324 from coralproject/summary-bugfix
fix bug when likes or flags were 0 for an article
2017-02-16 15:43:36 -08:00
Riley Davis 2b4e16edfa fix bug when likes or flags were 0 for an article 2017-02-16 16:39:02 -07:00
gaba 3166692d4e Rename everywhere to username. 2017-02-16 15:16:54 -08:00
David Erwin 465de98318 Merge pull request #323 from coralproject/more-lingering-displayname-bugs
Standardizing username
2017-02-16 18:02:04 -05:00
David Erwin e5512a0e54 Standardizing username 2017-02-16 17:56:46 -05:00
David Jay 1b2d84f38d Merge branch 'master' into update-on-post-comment 2017-02-16 17:28:40 -05:00
David Jay e3e85c5f3b Fixing error when posting comment. 2017-02-16 17:18:36 -05:00
David Jay e4e7ff27d2 Adding don't agree action to typedefs. 2017-02-16 16:54:05 -05:00
David Erwin fa597ca04d Merge pull request #322 from coralproject/bug-close-fb-win
Adds back the timeout to close window.
2017-02-16 15:42:21 -05:00
Riley Davis 1bda098045 Merge branch 'master' into the-answer-is-42 2017-02-16 13:41:01 -07:00
David Erwin 24f052024d Merge branch 'master' into update-on-post-comment 2017-02-16 15:40:51 -05:00
David Erwin 82811b8fae Merge branch 'master' into bug-close-fb-win 2017-02-16 15:39:06 -05:00
David Erwin 88214a008c Merge pull request #320 from coralproject/add-icon-qbox
Adds icon and style to the question box.
2017-02-16 15:38:44 -05:00
Riley Davis c771e72ac3 Merge branch 'master' into the-answer-is-42 2017-02-16 13:34:31 -07:00
David Erwin a59296db14 Merge branch 'master' into update-on-post-comment 2017-02-16 15:32:23 -05:00
Wyatt Johnson 7f11efb648 Merge branch 'master' into bug-close-fb-win 2017-02-16 13:29:08 -07:00
David Erwin 4b2cd1731b Merge branch 'master' into add-icon-qbox 2017-02-16 15:28:15 -05:00
David Erwin 29b134af87 Merge pull request #319 from coralproject/story-139830877-bug-local-user
It changes from displayname to username
2017-02-16 15:26:51 -05:00
gaba 06d426b72b Adds back the timeout to close window. 2017-02-16 12:17:20 -08:00
Riley Davis c53aa1d93b add a badge to the queues to show the count. proptypes 2017-02-16 12:48:36 -07:00
Riley Davis 1dcdd4a9c9 make Dashboard first tab per Sam. simplify tabs 2017-02-16 12:04:24 -07:00
gaba 8e14c1986d Adds icon and style to the question box. 2017-02-16 10:33:28 -08:00
David Jay b0e81d97ec Merge branch 'master' into update-on-post-comment 2017-02-16 12:16:31 -05:00
Gabriela Rodríguez Berón c8870f6097 Merge branch 'master' into story-139830877-bug-local-user 2017-02-16 08:54:58 -08:00
David Erwin a482d6c4bf Merge pull request #318 from coralproject/flags-button
Added count clearing for status changes
2017-02-16 11:41:28 -05:00
David Erwin 3c28557736 Merge branch 'master' into flags-button 2017-02-16 11:36:57 -05:00
Belén Curcio 501e65f729 Merge branch 'master' into update-on-post-comment 2017-02-16 13:36:39 -03:00
David Erwin d54f3103f9 Merge pull request #313 from coralproject/load-more-replies
Load more replies
2017-02-16 11:35:55 -05:00
David Erwin ca1d9a04f6 Merge branch 'master' into load-more-replies 2017-02-16 11:31:32 -05:00
David Erwin b6e9e3dcf5 Merge pull request #314 from coralproject/sory-139595043-qbox
Story 139595043 Question Box to Comment Stream
2017-02-16 11:31:17 -05:00
David Erwin 5467d980df Merge branch 'master' into sory-139595043-qbox 2017-02-16 11:26:22 -05:00
David Erwin 04c0d42278 Merge branch 'master' into update-on-post-comment 2017-02-16 11:26:11 -05:00
David Erwin 57d11c63d2 Merge pull request #317 from coralproject/flag-widget
most flagged assets widget
2017-02-16 11:23:08 -05:00
gaba ccb5228a9a Errors when saving the user. 2017-02-15 18:28:49 -08:00
Wyatt Johnson 3be8ae39c4 Removed old TODO's 2017-02-15 17:53:13 -07:00
Wyatt Johnson 9ab2653054 Merge branch 'master' into flags-button 2017-02-15 17:51:56 -07:00
Wyatt Johnson fa152bda3f Added count clearning for status changes 2017-02-15 17:48:40 -07:00
Wyatt Johnson 27819d0d6a Changed aggregate -> find 2017-02-15 17:42:54 -07:00
Wyatt Johnson b75aed8bd8 Another optimization pass 2017-02-15 17:28:47 -07:00
Wyatt Johnson 9eefc0299f Optimization pass 2017-02-15 17:12:17 -07:00
Riley Davis 6c141cbce0 now with likes 2017-02-15 16:52:06 -07:00
Wyatt Johnson 56fe8ef08e Merge branch 'master' into flag-widget 2017-02-15 16:17:30 -07:00
Wyatt Johnson 93636aabac Added like summaries 2017-02-15 16:15:43 -07:00
David Jay 80c60c560a Merge branch 'master' into update-on-post-comment 2017-02-15 17:56:44 -05:00
David Jay 45b419139e Merge branch 'master' of github.com:coralproject/talk into load-more-replies 2017-02-15 17:51:30 -05:00
Riley Davis f91607cff9 show the author and publication date 2017-02-15 15:36:46 -07:00
Riley Davis da1a931a40 remove a comma 2017-02-15 15:13:54 -07:00
Riley Davis b3b36a245a even more translations en Español 2017-02-15 14:23:54 -07:00
riley b26c170a52 add some translations and link to mod queue 2017-02-15 14:11:44 -07:00
David Erwin 8a3309da92 Merge branch 'master' into sory-139595043-qbox 2017-02-15 15:50:42 -05:00
David Erwin a37e103e87 Merge pull request #315 from coralproject/flags-button
Admin actions for Flagged comments
2017-02-15 15:48:32 -05:00
Belen Curcio 1796efd3ba Ádding NONE to the FE 2017-02-15 17:34:29 -03:00
Belen Curcio 0778b7832f Merge branch 'flags-button' of github.com:coralproject/talk into flags-button 2017-02-15 17:33:30 -03:00
Belen Curcio 675f823849 no comments endpoints in /admin 🎉 2017-02-15 17:32:54 -03:00
Wyatt Johnson 57d4f9d757 Changed new comment's status from null to NONE. 2017-02-15 13:21:15 -07:00
Riley Davis efb529d7bd Merge branch 'master'
Conflicts:
	client/coral-admin/src/AppRouter.js
2017-02-15 13:03:19 -07:00
Riley Davis 6eda495663 display most flagged assets in a table 2017-02-15 12:57:51 -07:00
Gabriela Rodríguez Berón a33daea5a3 Merge branch 'master' into sory-139595043-qbox 2017-02-15 11:57:39 -08:00
gaba 68c764fd43 Rename coral-ui/FormField with TextField. 2017-02-15 11:50:21 -08:00
gaba 1d1b1b3391 Move to use FormField the right way. 2017-02-15 11:37:39 -08:00
Belén Curcio 5b16df44ed Merge branch 'master' into flags-button 2017-02-15 16:33:53 -03:00
Belen Curcio ed105f876e not Accepted ones in the Flagged queue 2017-02-15 16:19:31 -03:00
David Erwin 0747219c63 Merge pull request #306 from coralproject/load-more-comments
Load more comments
2017-02-15 14:11:16 -05:00
Belen Curcio e59534bf46 merge confict 2017-02-15 16:09:04 -03:00
David Jay 1cad1d36d5 Updating asset_id to camelcase. 2017-02-15 13:26:56 -05:00
David Jay 88843486f1 Removing log statement. 2017-02-15 13:21:34 -05:00
David Jay 79dbe7dcda Updating commentCount on comment post. 2017-02-15 13:05:31 -05:00
David Jay 904bda86db Fixing error with replies. 2017-02-15 12:36:19 -05:00
David Jay a9104b749c Updating comments and replies using updatequeries w/ optimistic updating. 2017-02-15 12:19:36 -05:00
Belen Curcio 417edc81ca Admin actions for flagged comments 2017-02-15 13:40:01 -03:00
gaba d7469d648d Replace element with name instead of id. Add style to textfield from coral-ui. 2017-02-14 17:48:10 -08:00
gaba 0f074dc034 Changes styles for Emma and Sam's design. 2017-02-14 17:13:08 -08:00
gaba d3f81353a6 Fix TextField for coral-ui and setting configure stream, question box. 2017-02-14 16:51:57 -08:00
Wyatt Johnson e640bc2cc0 Added edge for metrics, fixed client linting errors 2017-02-14 17:02:50 -07:00
David Jay 6bc610d2c6 Merge branch 'load-more-replies' of github.com:coralproject/talk into load-more-replies 2017-02-14 18:59:00 -05:00
David Jay fbf543727b Moving load more limit to constant. 2017-02-14 18:54:51 -05:00
David Jay bab2acc557 Merge branch 'master' into load-more-replies 2017-02-14 18:36:15 -05:00
gaba 52a1ebe82a Merge branch 'master' into sory-139595043-qbox 2017-02-14 15:35:43 -08:00
gaba b1d90dfd87 Fix TextField for the Configure Stream. 2017-02-14 15:31:12 -08:00
David Jay 00c2305ff7 Adding view more button to replies. 2017-02-14 18:29:59 -05:00
Riley Davis 50e3e733c3 create FlagWidget 2017-02-14 16:12:38 -07:00
Riley Davis 63982aa241 add PropTypes to LoadMore component 2017-02-14 15:27:36 -07:00
Riley Davis 0bef413a3a saving my place 2017-02-14 15:20:28 -07:00
David Jay 714c251c77 Merge branch 'master' into load-more-comments 2017-02-14 17:02:32 -05:00
David Erwin d4651d4a2f Merge pull request #312 from coralproject/disable-e2e-ci
Comment out e2e in ci
2017-02-14 17:01:46 -05:00
David Erwin 06422d7074 Comment out e2e in ci 2017-02-14 16:56:55 -05:00
David Jay e95372373c Merge branch 'master' into load-more-comments 2017-02-14 16:35:44 -05:00
David Erwin b76f3bbf91 Merge branch 'all-flag'
t push origin master
2017-02-14 16:31:01 -05:00
David Jay a07b98dcc4 Updating logout path. 2017-02-14 16:16:40 -05:00
David Jay 85ea41725a Increasing logout button load time. 2017-02-14 16:03:38 -05:00
David Jay f524f539da Increasing logout time. 2017-02-14 15:53:40 -05:00
David Jay b497dc2286 Merge branch 'master' of github.com:coralproject/talk into load-more-comments 2017-02-14 15:42:32 -05:00
David Jay c3fe9fb43e Commenting out brittle e2e test. 2017-02-14 15:41:25 -05:00
Belén Curcio 023ba74c8f Merge branch 'master' into all-flag 2017-02-14 16:34:44 -03:00
Belen Curcio 41a1eeda26 Removing all code 2017-02-14 16:10:08 -03:00
Belen Curcio bbb82b6a77 New static routes <3 for global and single assets, also, deprecating the all view 2017-02-14 16:06:13 -03:00
Riley Davis 5e2c1a9d4f Merge branch 'master'
Conflicts:
	client/coral-admin/src/translations.json
2017-02-14 12:03:54 -07:00
David Erwin daec809d87 Merge pull request #307 from coralproject/external-css-file
Configure override css file
2017-02-14 13:58:05 -05:00
David Erwin ce2213549b Merge branch 'master' into external-css-file 2017-02-14 13:40:22 -05:00
David Erwin d2c9b4b7a7 Merge pull request #310 from coralproject/close-time-bug
fix Configure in admin
2017-02-14 13:40:07 -05:00
Riley Davis 53e3147290 properly save settings to server 2017-02-14 11:29:40 -07:00
Riley Davis fe1aaba9ed revert state shape to what it was before 2017-02-14 11:05:39 -07:00
David Jay 330c37ef86 Merge branch 'load-more-comments' of github.com:coralproject/talk into load-more-comments 2017-02-14 12:27:17 -05:00
David Jay 03a198a867 Adding View More translation. 2017-02-14 12:25:39 -05:00
David Jay 6b4ea14ce2 Merge branch 'master' into load-more-comments 2017-02-14 12:00:12 -05:00
gaba f51c857e8b Merge branch 'master' into sory-139595043-qbox 2017-02-14 08:51:58 -08:00
Wyatt Johnson d987b33d99 Merge branch 'master' into flag-widget 2017-02-14 09:00:12 -07:00
David Erwin d1749e0f35 Merge branch 'master' into external-css-file 2017-02-14 09:59:37 -05:00
David Erwin b85950140b Merge pull request #309 from coralproject/install-missing
Missing reducer in Talk Installer
2017-02-14 09:59:16 -05:00
Belen Curcio f07900b294 Missing reducer 2017-02-14 11:44:22 -03:00
gaba d4109c8fca Adds textfield to coral-UI. 2017-02-13 16:51:31 -08:00
David Jay edc3671839 Updating load-more-comments style. 2017-02-13 19:32:39 -05:00
David Jay 7d617ca611 Merge branch 'master' of github.com:coralproject/talk into load-more-comments 2017-02-13 18:26:05 -05:00
Riley Davis 86d0e59147 add translation to input label 2017-02-13 16:09:06 -07:00
Riley Davis 183d2e1ea0 Merged branch master into external-css-file 2017-02-13 16:04:00 -07:00
Riley Davis 0234112ad0 enable configuration for linking override stylesheets 2017-02-13 16:03:49 -07:00
David Erwin 586d3c4c8a Merge pull request #304 from coralproject/mod-queues-ql
Moderation Queue
2017-02-13 17:40:26 -05:00
Wyatt Johnson 51cbf79f30 Changed fragment 2017-02-13 15:33:10 -07:00
gaba 1d70ae0dec Adds question enable checkbox. 2017-02-13 14:31:56 -08:00
Belen Curcio 8953182329 merge 2017-02-13 19:14:27 -03:00
gaba f9f32b1ef6 Merge branch 'master' into sory-139595043-qbox 2017-02-13 14:13:28 -08:00
gaba 1b788b98c6 Adds questionBox to the gl query 2017-02-13 14:12:32 -08:00
Belen Curcio 51be47b62e added flag functionality 2017-02-13 19:09:26 -03:00
David Erwin b30ace74b4 Merge pull request #299 from coralproject/username-displayname
Inital rename of displayName -> username
2017-02-13 17:07:56 -05:00
gaba 5a2e4b708b Adds question box 2017-02-13 13:48:03 -08:00
David Jay 1af25bb2a7 Updating mocks. 2017-02-13 16:29:56 -05:00
Belen Curcio 37ba14f3b5 breaking big words 2017-02-13 18:27:26 -03:00
Belen Curcio 519298b980 Design Pass 2017-02-13 18:12:37 -03:00
gaba b16bdafc1a Adds plugin for question box. 2017-02-13 12:22:05 -08:00
David Jay c756b22302 Switching Load More to stateless component. 2017-02-13 15:15:27 -05:00
Belen Curcio 732ef9b8bd Adding moderationList for the tests 2017-02-13 16:24:12 -03:00
Belen Curcio 72156a01b8 Merge branch 'mod-queues-ql' of github.com:coralproject/talk into mod-queues-ql 2017-02-13 16:14:48 -03:00
Belen Curcio 0403c46c6c Adding errors response 2017-02-13 16:13:03 -03:00
Belen Curcio 594b87208a GraphQL Review and Responses 2017-02-13 16:00:55 -03:00
David Jay 03b73d8d6d Merge branch 'master' into load-more-comments 2017-02-13 12:45:25 -06:00
Belén Curcio 12acbeee45 Merge branch 'master' into mod-queues-ql 2017-02-13 15:23:43 -03:00
David Jay 5b2f52a283 Restyling Load More button. 2017-02-13 13:22:50 -05:00
Wyatt Johnson e494cbd7e8 Merge branch 'master' into username-displayname 2017-02-13 11:17:29 -07:00
Riley Davis e178b2e82a Merge pull request #305 from coralproject/date-spacing
add a non-breaking space in between username and pubdate
2017-02-13 11:08:58 -07:00
riley ba51b0742f remove non-breaking space and add css 2017-02-13 11:00:28 -07:00
Riley Davis 5a0af59c45 add a non-breaking space in between username and pubdate 2017-02-13 10:53:03 -07:00
Wyatt Johnson b30a5d2184 Removed 3.4 dep 2017-02-13 10:49:47 -07:00
Belen Curcio e2ebac7176 adds new mutations: setCommentStatus 2017-02-13 14:32:48 -03:00
Wyatt Johnson 4e73d7fc23 Switched from Circle 2.0 -> 1.0, added set -e 2017-02-13 10:23:11 -07:00
Wyatt Johnson a2855a7343 Removed unused shell 2017-02-13 10:03:55 -07:00
Belen Curcio 90ed1f1056 Ban user with graphql, 100 refactor 2017-02-13 11:50:16 -03:00
Wyatt Johnson af49b0f6fb Fixed checkout order 2017-02-10 16:29:29 -07:00
Wyatt Johnson 184e63e4e3 Updated circle based on feedback 2017-02-10 16:28:09 -07:00
Wyatt Johnson de54433c5c Merge branch 'master' into username-displayname 2017-02-10 16:19:27 -07:00
David Jay 4fedb7f31e Merge branch 'master' of github.com:coralproject/talk into load-more-comments 2017-02-10 14:34:19 -08:00
Riley Davis d4e68de349 setting up dashboard :) 2017-02-10 15:32:30 -07:00
Gabriela Rodríguez Berón b962d672f4 Merge pull request #300 from coralproject/story-137818425-tag-staff
Tag comments made by Staff with 'STAFF' tag automatically.
2017-02-10 13:48:41 -08:00
gaba e2e40ca2b3 Merge branch 'master' into story-137818425-tag-staff 2017-02-10 13:42:50 -08:00
David Erwin 4db2d63ee2 Merge pull request #302 from coralproject/like-bugfix
fix very annoying Like bug
2017-02-10 16:35:20 -05:00
gaba 11ef12c14c Adds padding and resolves when is not staff. 2017-02-10 13:33:57 -08:00
Belen Curcio 894fec9fb6 adding context for moderation 2017-02-10 17:55:51 -03:00
Riley Davis a5e66e61f3 Merge branch 'master' into like-bugfix 2017-02-10 13:26:55 -07:00
Riley Davis 81a408a0b1 fix very annoying Like bug 2017-02-10 13:21:58 -07:00
Belen Curcio bc14b908eb banUsers and moderate separate streams, not found assets etc 2017-02-10 17:15:26 -03:00
gaba 498eb5eb91 Fix the test. 2017-02-10 12:06:36 -08:00
David Erwin 5c4daa72cb Merge pull request #297 from coralproject/actions
Actions Metadata
2017-02-10 14:53:46 -05:00
gaba c80f406b21 Add tests 2017-02-10 11:52:02 -08:00
Wyatt Johnson c637be364a Moved tag creation into mutator 2017-02-10 12:00:50 -07:00
Gabriela Rodríguez Berón 7325525716 Merge branch 'master' into story-137818425-tag-staff 2017-02-10 10:32:43 -08:00
gaba 71fcf9cd3f FE for the Staff Tag. Modifed typedfs graphql. 2017-02-10 10:20:34 -08:00
Wyatt Johnson 2c2541e7be Added fix for not logged in user 2017-02-10 11:00:12 -07:00
Riley Davis 27ea409e31 html things 2017-02-10 10:52:55 -07:00
Belen Curcio 9320ece35b Merge branch 'master' of github.com:coralproject/talk into mod-queues-ql 2017-02-10 08:57:35 -03:00
David Jay 05fcc61c9c Resolving bug on comment load. 2017-02-09 17:56:28 -08:00
David Jay 52099efed9 Updating format of returned comments in CommentBox. 2017-02-09 17:27:16 -08:00
David Jay b4ee765437 Merge branch 'master' into actions 2017-02-09 17:14:53 -08:00
Wyatt Johnson fded4f0a91 Added new machine type 2017-02-09 18:05:47 -07:00
Wyatt Johnson c202042cda Revert "Revert "Adjusted circle""
This reverts commit 832d1f6763.
2017-02-09 18:04:31 -07:00
Wyatt Johnson 832d1f6763 Revert "Adjusted circle"
This reverts commit 229ddb11f2.
2017-02-09 18:02:25 -07:00
David Jay 1272dabb4b Updating frontend actions to new format. 2017-02-09 17:01:16 -08:00
Wyatt Johnson 229ddb11f2 Adjusted circle 2017-02-09 17:37:39 -07:00
Wyatt Johnson 7db3da5fdd Added fix for facebook 2017-02-09 17:03:41 -07:00
Wyatt Johnson 3cd2b28bc2 Inital rename of displayName -> username 2017-02-09 16:48:59 -07:00
gaba 10937cf717 Tag.name as string. Debuging comment.tag in Embed stream. 2017-02-09 15:28:09 -08:00
gaba 647ae2fca0 Assigned by will be a string for now. 2017-02-09 14:25:35 -08:00
Wyatt Johnson 646f6040ac Merge pull request #291 from coralproject/recent-items
Added new recentComments, recentReplies
2017-02-09 14:24:04 -07:00
Wyatt Johnson 91e94e8400 Merge branch 'master' into recent-items 2017-02-09 14:11:03 -07:00
gaba 3be4fea8ad Merge branch 'master' into story-137818425-tag-staff 2017-02-09 12:32:26 -08:00
gaba 1c26e64c71 Adds Tags to the graphql. 2017-02-09 12:23:40 -08:00
Gabriela Rodríguez Berón a825d29b60 Merge pull request #298 from coralproject/bugfix-9000
Bugfix 9000
2017-02-09 12:22:59 -08:00
David Erwin cd7d683fe2 Fixing for default url case 2017-02-09 15:16:02 -05:00
David Erwin 81e050214d fix js error 2017-02-09 15:03:29 -05:00
gaba f2341ca9d3 Adds tag label plugin. 2017-02-09 09:17:56 -08:00
Belen Curcio d3184d9afb Merge issues and Settings reducer updated 2017-02-09 13:58:55 -03:00
Belen Curcio e6087dfa25 Go to Moderate from Stream 2017-02-09 13:53:23 -03:00
Belen Curcio e234fa7e0b Reducers refactor, working Moderate Queue 2017-02-09 07:00:39 -03:00
Wyatt Johnson 25103630b5 Added graphql docs endpoint. 2017-02-08 17:41:28 -07:00
Wyatt Johnson fb9bedc57b Added new action rep, refactored mutations 2017-02-08 17:41:11 -07:00
Riley Davis ddeb28d729 have cursor be the date of the oldest comment 2017-02-08 16:27:09 -07:00
Riley Davis d4d1f088cd Merge branch 'master' into recent-items 2017-02-08 14:50:06 -07:00
Riley Davis ddc26f0cec Merge pull request #296 from coralproject/admin-apollo
Using GraphQL and Apollo in Talk Admin. [Don't Merge]
2017-02-08 14:17:09 -07:00
Riley Davis 0d141e7472 explicit return for clarity 2017-02-08 14:12:17 -07:00
gaba 85dabae355 Merge branch 'master' into story-137818425-tag-staff 2017-02-08 12:50:14 -08:00
Riley Davis a3f99f76e2 Merged branch master into admin-apollo 2017-02-08 13:42:37 -07:00
David Erwin 2352a21826 Merge pull request #295 from coralproject/story-138524853-domain-whitelist
Story 138524853 domain whitelist
2017-02-08 15:38:38 -05:00
gaba 075e62581e Tags should be an array 2017-02-08 09:03:21 -08:00
David Jay 0c777cbad4 Adding loadMore function. 2017-02-08 08:59:38 -08:00
Belen Curcio dc35425afd Moderate Queue by assetId 2017-02-08 13:31:41 -03:00
Belen Curcio fab0c7e74d Mod Queue Working 2017-02-08 12:39:35 -03:00
Belen Curcio 6a6574239f Mod Queue Working 2017-02-08 12:36:05 -03:00
gaba fc880de9bf Backend for the tags staff on staff users. 2017-02-07 16:31:39 -08:00
Gabriela Rodríguez Berón f014338083 Merge branch 'master' into story-138524853-domain-whitelist 2017-02-07 15:38:09 -08:00
gaba 1176a94bec Update tests 2017-02-07 15:36:10 -08:00
Belen Curcio a4d62f78f2 Apollo config ready 2017-02-07 20:10:18 -03:00
Belén Curcio 604a3a42b9 Merge pull request #293 from coralproject/install-admin
Install Talk
2017-02-07 19:43:37 -03:00
Gabriela Rodríguez Berón 5b609ceb86 Merge branch 'master' into story-138524853-domain-whitelist 2017-02-07 14:20:48 -08:00
gaba 3effd11ad2 Adding test. 2017-02-07 13:57:46 -08:00
Wyatt Johnson 4e1891c068 Fixed install state. 2017-02-07 14:30:25 -07:00
gaba d08a9fb7b2 Front End for Domain Whitelist. 2017-02-07 13:07:22 -08:00
Belen Curcio fd40135f1b Merge branch 'master' of github.com:coralproject/talk into install-admin 2017-02-07 17:46:45 -03:00
Belen Curcio a3e1bd972c Redirect if its initialized, funtionallity of finalStep 2017-02-07 17:44:27 -03:00
Belen Curcio f8d83ffbde Rest of the embed side config 2017-02-07 16:15:53 -03:00
Belen Curcio 3fc7fbd01e Ádmin running on Apollo 2017-02-07 16:06:32 -03:00
David Erwin f188df162c Merge pull request #294 from coralproject/wrong-error-proptype
fix a warning in the console when you're signed out
2017-02-07 13:02:18 -05:00
David Erwin fcd431dc21 Merge branch 'master' into wrong-error-proptype 2017-02-07 12:51:20 -05:00
Riley Davis ca2c55f643 Merge pull request #292 from coralproject/bugfix-tiny-mobile-fonts
Fixing mobile viewport width.
2017-02-07 10:50:30 -07:00
Riley Davis 9d3960fd7b fix a warning in the console when you're signed out 2017-02-07 10:37:38 -07:00
Wyatt Johnson 5cdfc7bd1b Merge branch 'master' into install-admin 2017-02-07 10:05:31 -07:00
Riley Davis cd3d08c6f9 Merge branch 'master' into bugfix-tiny-mobile-fonts 2017-02-07 10:05:20 -07:00
Wyatt Johnson cdc81c4fc5 Added new setup endpoint 2017-02-07 10:02:23 -07:00
Wyatt Johnson f47638ae07 Removed provisioning text. 2017-02-07 09:45:43 -07:00
Belen Curcio c837e89b38 wip 2017-02-07 13:43:17 -03:00
Belen Curcio 526f0145f1 if goTo is not defined 2017-02-07 02:06:32 -03:00
Kim Gardner ba450a0166 Merge pull request #283 from coralproject/username-ban
Username banning flow
2017-02-06 23:04:43 -05:00
Belen Curcio 0b9956a842 Merge branch 'master' of github.com:coralproject/talk into install-admin 2017-02-07 00:16:51 -03:00
Belen Curcio e239926866 Failure check 2017-02-07 00:07:53 -03:00
David Jay 586537084b Merge branch 'master' into bugfix-tiny-mobile-fonts 2017-02-06 18:29:34 -08:00
Belen Curcio 3a953c7efe First Step 2017-02-06 23:23:08 -03:00
Belen Curcio 77f090fd3c Multiple validations 2017-02-06 23:18:14 -03:00
David Jay 652170735b Merge branch 'master' into username-ban 2017-02-06 18:14:13 -08:00
Belen Curcio 16f93a0a19 Validaiton 2017-02-06 22:41:21 -03:00
Kim Gardner f19e0134db Merge pull request #286 from coralproject/more-email-verify
Cleanup on email verification
2017-02-06 20:26:07 -05:00
David Jay ecb72b036d Fixing mobile viewport width. 2017-02-06 16:49:31 -08:00
David Jay 80ea100fc5 Making username update services atomic. 2017-02-06 15:49:48 -08:00
Riley Davis 8c8a8eb3b8 define more propTypes. add a second button so tests work 2017-02-06 16:23:00 -07:00
gaba d93dfe55af Merge branch 'master' into story-138524853-domain-whitelist 2017-02-06 14:53:46 -08:00
David Jay b22895b908 Switching displayname update to account endpoint. 2017-02-06 14:38:14 -08:00
Riley Davis fe6d6f611d pause suddenly not a function? 2017-02-06 14:59:23 -07:00
Riley Davis 93ee5bfc4a just throwing stuff at the wall to see what sticks 2017-02-06 14:42:40 -07:00
Riley Davis 3a145e9927 add a pause to see if anything changes 2017-02-06 14:22:37 -07:00
gaba 8d4f87a95d Domain List. 2017-02-06 13:11:39 -08:00
Riley Davis 36ba9bd615 Merge branch 'master' into more-email-verify 2017-02-06 13:39:34 -07:00
Wyatt Johnson 7bef103217 Added new recentComments, recentReplies 2017-02-06 12:54:55 -07:00
David Jay 1901592780 Switching displayname edit after facebook auth to account endpoint. 2017-02-06 11:07:35 -08:00
David Jay cb78e8d563 Merge branch 'master' into username-ban 2017-02-06 10:23:14 -08:00
Kim Gardner adae7aadf3 Merge pull request #290 from coralproject/losowsky-patch-1
added product link, note about alpha
2017-02-04 15:03:32 -05:00
Andrew Losowsky 3815267919 added product link 2017-02-04 09:31:23 -05:00
Riley Davis ff3c49c7bd Merged branch master into more-email-verify 2017-02-03 14:21:40 -07:00
Riley Davis e91ff306e9 whoops, missed one 2017-02-03 14:21:22 -07:00
David Erwin c8bb59be5b Merge pull request #289 from coralproject/circle-release-fix
Circle release fix
2017-02-03 16:16:00 -05:00
David Erwin bc14a9a758 Merge branch 'master' into circle-release-fix 2017-02-03 16:11:23 -05:00
Riley Davis a0c03605b9 Merge branch 'master'
Conflicts:
	client/coral-embed-stream/src/Embed.js
	client/coral-framework/constants/auth.js
	client/coral-framework/reducers/auth.js
	client/coral-sign-in/components/SignUpContent.js
2017-02-03 13:12:32 -07:00
Riley Davis 3441ec36c6 re-direct to sign in when email verification is off. add proptypes 2017-02-03 12:50:01 -07:00
Benjamin Goering 5614b9781e Merge branch 'master' into 20170203_futureproof_embed_code 2017-02-04 03:31:33 +08:00
Benjamin Goering f9cfdd07a5 update e2e tests to find iframe using selector that works with coral-embed 2017-02-04 03:25:15 +08:00
David Jay c784de6e95 Updating e2e tests. 2017-02-03 11:15:10 -08:00
Belen Curcio 8664a16bfc modQueue Query 2017-02-03 16:05:27 -03:00
Riley Davis 5ae078ff6e Merge pull request #277 from coralproject/story-134624635
Adds a screen to create username after facebook signup
2017-02-03 11:55:00 -07:00
Wyatt Johnson 35919740fd Uncommented things that were 2017-02-03 11:39:31 -07:00
Wyatt Johnson 5caade2409 Adjusted circle scripts for current usage of flags 2017-02-03 11:36:49 -07:00
Benjamin Goering 838e2a66e3 trivial change to trigger CircleCI (wyatt asked me to) 2017-02-04 02:35:58 +08:00
David Jay 2dacb72341 Updating reload code. 2017-02-03 10:27:17 -08:00
Wyatt Johnson fb8450c7ff Fixed issues with default setup 2017-02-03 11:22:35 -07:00
David Jay d5abe78a3d Merge branch 'username-ban' of github.com:coralproject/talk into username-ban 2017-02-03 10:20:29 -08:00
David Jay 6e92f8c3d6 Merge branch 'master' of github.com:coralproject/talk into username-ban 2017-02-03 10:19:21 -08:00
Benjamin Goering 1e8a363205 remove use of template strings in coral-embed script as some browsers will throw exceptions on them 2017-02-04 02:18:54 +08:00
David Jay d92c2d9744 Updating user queue routes. 2017-02-03 10:18:29 -08:00
Benjamin Goering bbdd9f22c1 re-copypaste pym.js into coral-embed, as eslint-fix had changed it to use template strings 2017-02-04 02:11:59 +08:00
Wyatt Johnson f488b0e02d Added BE for setup
- Adjusted docs
- Fixed issues with process terminating wrong
2017-02-03 11:05:46 -07:00
Benjamin Goering 160fb6e4d4 Add async attribute to coral-embed scripts 2017-02-04 02:03:31 +08:00
Riley Davis 8f374852ac hide social stuff after sign up 2017-02-03 10:49:52 -07:00
Benjamin Goering 841d6add45 Rename /Coral.js to /embed.js 2017-02-04 01:48:57 +08:00
Benjamin Goering b9c918eb88 Add pym.js LICENSE to coral-embed 2017-02-04 01:29:24 +08:00
Riley Davis 5c365b1a79 YOU. SHALL. NOT. PASS 2017-02-03 10:19:56 -07:00
Benjamin Goering f1010462ec Dont' use const/let in coral-embed so it works in all browsers 2017-02-04 01:19:47 +08:00
Riley Davis c4b934a542 whoops. commited to the wrong branch 2017-02-03 10:19:18 -07:00
Riley Davis a6f7cf258b attempt to make e2e tests pass 2017-02-03 10:15:51 -07:00
Benjamin Goering fb60b088b3 Rename coral-embd-api to coral-embed 2017-02-04 01:15:35 +08:00
Benjamin Goering e376e19c2c remove console.log from coral-embed-api 2017-02-04 01:00:46 +08:00
Benjamin Goering f7cddad659 Futureproof Embed Code
* Add client/coral-embed-api module. It's meant to be included in an HTML page and define window.Coral
* Serve coral-embed-api at /Coral.js
* Update / handler (article view) to use new embed format
* Update Talk Admin embed code section to use new embed format
2017-02-04 00:47:32 +08:00
Belen Curcio 80241025e5 linitin 2017-02-03 13:43:17 -03:00
gaba b5c9c1c731 Returns the real displayname and not the one transformed. 2017-02-02 16:03:14 -08:00
gaba 7da141ed29 Dispatch other action to only trigger change when sign up. 2017-02-02 15:44:45 -08:00
gaba d74d2e9e87 Dispatchs the user. 2017-02-02 15:25:45 -08:00
gaba c272b88340 It returns user now. 2017-02-02 15:24:55 -08:00
Riley Davis 5a5c04f557 break the line as per the designs 2017-02-02 15:57:54 -07:00
Riley Davis 98c3d11815 add a translation 2017-02-02 15:46:51 -07:00
Riley Davis a53db77c21 redirect back to the proper page 2017-02-02 15:44:12 -07:00
Riley Davis a0cf467f1b remove confirm and replace with verify 2017-02-02 14:59:45 -07:00
David Jay b8b66bc50c Merge branch 'master' into username-ban 2017-02-02 12:51:11 -05:00
Wyatt Johnson 2df980b560 Merge branch 'master' into install-admin 2017-02-02 10:34:17 -07:00
David Jay feb0860ba1 Merge branch 'master' of github.com:coralproject/talk into username-ban 2017-02-02 12:29:29 -05:00
Riley Davis bb798b8b02 Merge branch 'master' into story-134624635 2017-02-02 10:05:17 -07:00
David Erwin 03c240054a Merge pull request #284 from coralproject/stream-admin-bugfix
Fix admin user stream bug
2017-02-02 12:02:06 -05:00
David Erwin 79a2a2144c Fix admin user stream bug 2017-02-02 11:51:09 -05:00
gaba d53dc09aed Merge branch 'master' into story-134624635 2017-02-02 08:16:50 -08:00
David Erwin ce002e366d Merge pull request #282 from coralproject/hide-pending-sometimes
Hide Pre-Mod tab sometimes
2017-02-02 10:33:00 -05:00
Belén Curcio 7fe6002219 Merge branch 'master' into hide-pending-sometimes 2017-02-02 12:28:17 -03:00
Belén Curcio 3273749fbb Merge pull request #269 from coralproject/design-pass-tweaks
Design pass tweaks
2017-02-02 12:21:47 -03:00
David Erwin 22e32b1869 Merge branch 'master' into hide-pending-sometimes 2017-02-02 10:16:53 -05:00
Belén Curcio 04669350a1 Merge branch 'master' into design-pass-tweaks 2017-02-02 12:15:23 -03:00
David Erwin b0045fd8a6 Merge pull request #281 from coralproject/add-yarn
Add yarn + Upgrade Webpack
2017-02-02 10:14:07 -05:00
Wyatt Johnson bd86eef441 Merge branch 'master' into add-yarn 2017-02-02 08:02:06 -07:00
Belen Curcio 65aa5e7e92 Ádding Select, Options to Coral Ui and steps with formField 2017-02-02 11:31:43 -03:00
Belén Curcio b4a03a5133 Merge branch 'master' into design-pass-tweaks 2017-02-02 07:55:41 -03:00
Belen Curcio ad8d92a8ea Merge branch 'master' of github.com:coralproject/talk into install-admin 2017-02-01 23:08:22 -03:00
David Jay 97c3ebc3e0 Whitelisting usernames once they have been approved. 2017-02-01 20:55:45 -05:00
David Jay 5831a6f927 Merge branch 'master' of github.com:coralproject/talk into username-ban 2017-02-01 20:00:54 -05:00
David Jay d703cc2168 Prompting user to update name when banned. 2017-02-01 19:53:37 -05:00
Gabriela Rodríguez Berón d8ab3b8fba Merge branch 'master' into story-134624635 2017-02-01 14:03:56 -08:00
gaba 445eb97d76 Moves create displayname dialog to sign-in plugin. 2017-02-01 14:00:09 -08:00
David Jay e3da45daeb Displaying appropriate suspension message. 2017-02-01 16:38:01 -05:00
Riley Davis 13114e7530 Merge pull request #276 from coralproject/bugfix-asset-search
Addressing console errors in asset search.
2017-02-01 14:35:42 -07:00
gaba 4df09ffbd2 Validation for the field underneath. 2017-02-01 13:29:47 -08:00
gaba 0c360e62a6 Display errors that come from the backend. 2017-02-01 13:20:17 -08:00
David Jay 7e907e18f2 Enabling canEditName when suspending user. 2017-02-01 16:19:42 -05:00
David Jay 5dca1611bc Moving account endpoint tests to account. 2017-02-01 15:58:07 -05:00
David Jay b2430eaf60 Adding displayname editing and appropriate toggle. 2017-02-01 15:55:33 -05:00
gaba ee0bb50dd5 Adds errors on field when not validated. 2017-02-01 12:34:02 -08:00
Riley Davis 6e118c93aa Merged branch master into hide-pending-sometimes 2017-02-01 13:33:42 -07:00
Riley Davis 22c0ed686f rename everything to Pre-Mod from Pending. hide the pre-mod tab sometimes. 2017-02-01 13:33:29 -07:00
David Jay 265860bccb Merge branch 'master' into bugfix-asset-search 2017-02-01 15:16:36 -05:00
Wyatt Johnson 064ccfbbb3 Added build step for static assets 2017-02-01 13:12:40 -07:00
gaba 990685f17a Merge branch 'master' into story-134624635 2017-02-01 12:11:01 -08:00
gaba a8e4ce466d Adds validation to the dialog. 2017-02-01 12:09:27 -08:00
Wyatt Johnson e4722249b6 Added node env to environment 2017-02-01 13:07:14 -07:00
Wyatt Johnson 3a87bdc282 Merge branch 'master' into add-yarn 2017-02-01 12:56:52 -07:00
Wyatt Johnson 7beb28e077 Updated documentation 2017-02-01 12:54:15 -07:00
Wyatt Johnson 6b582fdb2c Updated docker + build 2017-02-01 12:54:04 -07:00
Gabriela Rodríguez Berón f45429f510 Merge pull request #280 from coralproject/display-name-fix
Allowing uppercase letters in the display name.
2017-02-01 11:46:38 -08:00
riley 4a9c996cba enforce uniqueness case-independent 2017-02-01 12:39:16 -07:00
Wyatt Johnson f4177f318f Upgraded webpack 2017-02-01 12:34:17 -07:00
Wyatt Johnson 723dee3394 Added yarn.lock back 2017-02-01 12:33:50 -07:00
gaba 3d301b7e70 Allowing uppercase letters in the display name. 2017-02-01 10:51:33 -08:00
gaba c9b12fc5d6 Thanks git-godesses for the log on finding a bad remove when merging. 2017-02-01 10:46:32 -08:00
gaba f4d672ddcf Merge branch 'master' into story-134624635 2017-02-01 10:42:00 -08:00
Riley Davis ec956165a3 Merge branch 'master' into bugfix-asset-search 2017-02-01 11:35:59 -07:00
Gabriela Rodríguez Berón 81ae482524 Merge pull request #279 from coralproject/graphql-hotfix
fix a bug per wyatt advice
2017-02-01 10:28:36 -08:00
riley 00297d1a1a fix a bug per wyatt advice 2017-02-01 11:22:42 -07:00
Belen Curcio 7866d1f8cd Done Wizard mEnu 2017-02-01 15:17:15 -03:00
Gabriela Rodríguez Berón f7c43a7853 Merge pull request #270 from coralproject/timeago-fix
Fixing bug where timeAgo cannot interpret dates from mongoose properly.
2017-02-01 09:57:30 -08:00
Gabriela Rodríguez Berón e1650aa721 Merge branch 'master' into timeago-fix 2017-02-01 09:52:40 -08:00
Wyatt Johnson d1cf6fd19b Applied patch 2017-02-01 10:45:01 -07:00
David Jay 6a4c168882 Merge pull request #275 from coralproject/mobile-width-fix
Updating mock article template to support mobile widths.
2017-02-01 12:37:44 -05:00
David Jay 37975574ea Switching rejected usernames from suspended to banned. 2017-02-01 12:26:01 -05:00
David Jay 17eefeea15 Merge branch 'master' into mobile-width-fix 2017-02-01 12:16:57 -05:00
David Jay 26ce3c635d Merge pull request #274 from coralproject/emailus-veritas
Verify Email upon signup
2017-02-01 12:13:28 -05:00
David Jay 37d688f99d Merge branch 'mobile-width-fix' of github.com:coralproject/talk into mobile-width-fix 2017-02-01 12:12:14 -05:00
David Jay bc6c77176e Fixing error in styling. 2017-02-01 12:11:12 -05:00
David Jay e24b953f51 Merge branch 'master' into emailus-veritas 2017-02-01 12:06:57 -05:00
David Erwin 653e67c9c4 Merge pull request #271 from coralproject/e2e-port-fix
Switching e2e to port 3011.
2017-02-01 11:59:13 -05:00
David Jay 6c01a37f6a Removing user bio. 2017-02-01 11:48:16 -05:00
Belen Curcio caa04de09d linting and proptypes 2017-02-01 12:12:28 -03:00
Belen Curcio 12070309d6 Wizard Nav 2017-02-01 12:11:15 -03:00
Belen Curcio 6a4315d608 Working Install Wizard 2017-02-01 10:49:16 -03:00
Belen Curcio 6c318b0502 Merge branch 'master' of github.com:coralproject/talk into install-admin 2017-02-01 09:57:02 -03:00
gaba 097934dee5 Merge branch 'master' into story-134624635 2017-01-31 15:00:14 -08:00
gaba 282c7dc4f5 Removes duplicate func and adds tests. 2017-01-31 14:04:31 -08:00
gaba 37eae51641 Adds component to coral-framework to deal with the change of displayname. 2017-01-31 13:52:40 -08:00
gaba a377f61bdd Adds service and endpoint to change displayname. 2017-01-31 13:51:36 -08:00
gaba 80122dfdce Adds formfield to components. 2017-01-31 13:48:45 -08:00
Riley Davis f5d873ba57 use referer from headers 2017-01-31 13:45:23 -07:00
David Jay 20bdf648d7 Merge branch 'e2e-port-fix' of github.com:coralproject/talk into e2e-port-fix 2017-01-31 15:40:09 -05:00
Riley Davis 55dc229b32 Merged branch emailus-veritas into emailus-veritas 2017-01-31 13:29:34 -07:00
Riley Davis e6e8ad5ca6 close the resend email box after use. remove dead code 2017-01-31 13:29:23 -07:00
David Jay 5d185e7aca Updating port in globals. 2017-01-31 15:21:21 -05:00
Wyatt Johnson a13b1c95d6 Adjusted form 2017-01-31 12:57:09 -07:00
David Jay f44c1d6f0a Merge branch 'master' into timeago-fix 2017-01-31 14:41:58 -05:00
David Jay 4a6b09ac96 Merge branch 'master' into e2e-port-fix 2017-01-31 14:41:43 -05:00
David Jay 64d2aa7f42 Merge branch 'master' into mobile-width-fix 2017-01-31 14:41:30 -05:00
David Jay ae3b8a6474 Merge branch 'master' into bugfix-asset-search 2017-01-31 14:41:22 -05:00
David Jay 7babb14bab Addressing bug in asset search. 2017-01-31 14:37:52 -05:00
Belen Curcio 890da0400f Steps 2017-01-31 16:31:29 -03:00
David Jay 3ca263bcfb Updating mock article template to support mobile widths. 2017-01-31 14:23:15 -05:00
Riley Davis ec1dfefc5d lint 2017-01-31 12:15:33 -07:00
Riley Davis 3d83c246f7 simplify email text 2017-01-31 12:08:59 -07:00
Riley Davis e63e1f3428 Merge branch 'master'
Conflicts:
	errors.js
	test/routes/api/auth/index.js
2017-01-31 12:05:16 -07:00
Riley Davis 56ddc49386 resend verify email 2017-01-31 12:04:19 -07:00
Wyatt Johnson d72b661c93 Merge pull request #272 from coralproject/fix-252
CLI + Init fixes
2017-01-31 10:47:02 -07:00
Wyatt Johnson 0af41a5b6b Updated circle test 2017-01-30 18:41:23 -07:00
Wyatt Johnson 864413b5d6 Fixed issues with e2e 2017-01-30 18:38:06 -07:00
Wyatt Johnson 91cd2e9e86 Fixes #266 2017-01-30 18:21:36 -07:00
Wyatt Johnson e6ef4b615f Changed prompt lib, updated cli's
- fixes #252
2017-01-30 17:46:09 -07:00
Belen Curcio 6fd2a927f1 Linted 2017-01-30 20:51:28 -03:00
Belen Curcio 76f55b4524 Cursor on user name, and Reply Bio box 2017-01-30 20:50:25 -03:00
Belen Curcio 5b926b9b85 merge 2017-01-30 20:42:15 -03:00
David Jay e8d8c7af5b Switching e2e to port 3011. 2017-01-30 18:04:17 -05:00
Riley Davis 976c821715 move FormField to coral-ui 2017-01-30 16:00:24 -07:00
Belen Curcio 0881201465 Notification 2017-01-30 20:00:01 -03:00
David Jay 235214642e Fixing bug where timeAgo cannot interpret dates from mongoose properly. 2017-01-30 17:55:06 -05:00
Belen Curcio 5d2ed3f585 Notifications 2017-01-30 19:48:07 -03:00
Belen Curcio f83def83bd tab 2017-01-30 19:38:58 -03:00
Wyatt Johnson b75557912f Merge pull request #268 from coralproject/graph-pagination
Graph Pagination
2017-01-30 14:43:44 -07:00
Belen Curcio f9363e86d0 Merge 2017-01-30 17:24:57 -03:00
Belén Curcio 887fe08819 Merge branch 'master' into graph-pagination 2017-01-30 17:12:23 -03:00
Belen Curcio 2e1dd35de3 Permalink box 2017-01-30 16:47:26 -03:00
Belen Curcio 019e2a4fa9 Flag order 2017-01-30 16:37:00 -03:00
gaba e803593fc7 Move stuff around. 2017-01-30 11:34:04 -08:00
Belen Curcio f5dc5ed8b7 Test updated 2017-01-30 15:53:29 -03:00
Belen Curcio 744f2085da Lint 🎉 2017-01-30 15:38:26 -03:00
Belen Curcio e4839468e3 Merge branch 'master' of github.com:coralproject/talk into design-pass-tweaks 2017-01-30 15:34:23 -03:00
Belen Curcio b316d626f5 Bio and Embed Stream refresh 2017-01-30 15:33:08 -03:00
Belen Curcio f7725f4143 Notifications working everywhere :D 2017-01-30 15:27:09 -03:00
Belen Curcio 7a227dff91 Notification in the CommentStream Working 2017-01-30 15:11:16 -03:00
David Erwin 1070dfd5ae Merge pull request #249 from coralproject/e2e-env-fix
E2E environment fix
2017-01-30 12:14:50 -05:00
Belen Curcio 805b5cb441 Lintin 2017-01-30 09:04:00 -03:00
Belen Curcio c42fea5889 SnackBar 2017-01-30 08:19:18 -03:00
Belen Curcio 8fe8f6c690 Working permalink 2017-01-30 07:56:08 -03:00
Belen Curcio 177a4d8f2e Permalinks and Embed 2017-01-30 07:32:19 -03:00
Belen Curcio 0105b1d4ac Permalink 2017-01-30 06:57:12 -03:00
Belen Curcio 61d24bad60 User bio, onClick and onMouseOver - Styles and Tooltip 2017-01-30 06:17:39 -03:00
Belen Curcio e5976576c1 merge 2017-01-30 05:37:25 -03:00
Wyatt Johnson 04cd7312a6 Added support for new commentCount 2017-01-27 15:52:05 -07:00
David Jay b674ff45c0 Addressing errors with replies in e2e. 2017-01-27 17:29:23 -05:00
Riley Davis 5e521b832c add a confirm-email landing page 2017-01-27 15:04:42 -07:00
David Jay 1da61784b4 Fixing bugs around settings in e2e. 2017-01-27 16:10:47 -05:00
David Jay f0bb9db892 Merge branch 'e2e-env-fix' of github.com:coralproject/talk into e2e-env-fix 2017-01-27 15:21:47 -05:00
David Jay 9d15a0448a Adding settings init to pree2e script. 2017-01-27 15:21:18 -05:00
David Jay f09d9d0a8b Merge branch 'master' into e2e-env-fix 2017-01-27 14:10:59 -05:00
David Jay 801d18639b Merge branch 'e2e-env-fix' of github.com:coralproject/talk into e2e-env-fix 2017-01-27 14:09:29 -05:00
David Jay fcf22ef4f9 Adding selenium settings and fixing bug in moderation approval. 2017-01-27 14:07:34 -05:00
Wyatt Johnson 2e6c7f7b09 First pass at pagination 2017-01-27 12:05:19 -07:00
Wyatt Johnson d829d2541f Added new env var for e2e reports 2017-01-27 11:20:45 -07:00
Wyatt Johnson ebc928c6f2 Merge pull request #267 from coralproject/lodash-dep
Lodash Import
2017-01-27 11:03:59 -07:00
Belen Curcio 55a8b4984a only importing isEqual 2017-01-27 14:58:36 -03:00
Belen Curcio 1778c19f6a çhanges 2017-01-27 14:55:55 -03:00
David Erwin 087b1a7bd7 Merge pull request #264 from coralproject/postmod-default
Update default setting to POST moderation
2017-01-27 11:16:36 -05:00
Wyatt Johnson 0096de6e79 Merge branch 'master' into postmod-default 2017-01-27 09:11:51 -07:00
Kim Gardner 88d718e7c9 Merge pull request #265 from coralproject/remove-avatar
Remove avatar from user cards
2017-01-27 10:32:12 -05:00
David Erwin c8c160a551 Remove avatar from user cards 2017-01-27 10:16:09 -05:00
Wyatt Johnson 5d1fd61425 Merge branch 'master' into e2e-env-fix 2017-01-27 08:04:34 -07:00
Wyatt Johnson 9fbde9c023 Update init.js 2017-01-27 08:04:00 -07:00
David Erwin 744fce4563 Set settings model as well 2017-01-27 10:01:58 -05:00
David Erwin c071484357 Removing test artifiacts 2017-01-27 09:48:41 -05:00
David Erwin 0a80205095 Update default setting to POST moderation 2017-01-27 09:45:35 -05:00
David Erwin fbc5a6660a Merge pull request #263 from coralproject/moderate-bio
Marking correct user as "Pending" on user flags.
2017-01-27 09:25:02 -05:00
Wyatt Johnson 71647d5233 Merge branch 'master' into e2e-env-fix 2017-01-26 16:47:52 -07:00
David Jay e7587d9a41 Merge branch 'master' into moderate-bio 2017-01-26 17:46:20 -05:00
David Jay 02ece80227 Marking correct user as Pending on user flags. 2017-01-26 17:42:45 -05:00
David Erwin 74d78b76a5 Merge pull request #219 from coralproject/moderate-bio
Moderate bio or username
2017-01-26 17:09:44 -05:00
David Jay d4c9bdb6fd Merge branch 'e2e-env-fix' of github.com:coralproject/talk into e2e-env-fix 2017-01-26 16:52:12 -05:00
David Jay b8ff2b6763 Resetting chrome version to 2.25 in nightwatch. 2017-01-26 16:51:03 -05:00
Wyatt Johnson fa985acd14 Update circle.yml 2017-01-26 14:45:02 -07:00
David Jay f06edec6a2 Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-26 16:38:43 -05:00
David Jay b6d8a90084 Updating style of UserActions and moving ActionButton to its own component. 2017-01-26 16:36:43 -05:00
David Erwin 3d08d1f54f Merge branch 'master' into e2e-env-fix 2017-01-26 16:30:14 -05:00
David Erwin b35336d1ed Merge pull request #260 from coralproject/hide-premod-links
Hiding premod links
2017-01-26 16:28:48 -05:00
David Erwin f42b8f324a Merge branch 'master' into hide-premod-links 2017-01-26 16:24:06 -05:00
David Erwin 30583cd93f Merge pull request #261 from coralproject/collapse-replies
Collapse replies
2017-01-26 16:21:07 -05:00
David Erwin 2086bd7b72 Merge branch 'master' into collapse-replies 2017-01-26 16:17:23 -05:00
David Erwin fe62c1a739 Merge pull request #257 from coralproject/refresh-data-fix
Cleanup and more GraphQL features
2017-01-26 16:15:55 -05:00
David Jay 06331d3c07 Updating bio moderation to graphQL architecture. 2017-01-26 15:31:15 -05:00
Riley Davis 2ad27ef6f9 add a cancel button to the reply box 2017-01-26 13:21:23 -07:00
Riley Davis 3cbe8bb7ac Stream now tracks what reply box is open 2017-01-26 12:45:14 -07:00
David Jay 963fc1f091 Merge branch 'e2e-env-fix' of github.com:coralproject/talk into e2e-env-fix 2017-01-26 14:41:41 -05:00
David Jay 2e34f726eb Updating tests to reflect graphql changes. 2017-01-26 14:41:03 -05:00
Belen Curcio 58c4d5ceb5 Comment button order 2017-01-26 16:09:56 -03:00
Belen Curcio dc89b22abb Clicking user goes to Settings Tab 2017-01-26 15:39:16 -03:00
David Erwin afb246b173 Hiding premod links 2017-01-26 10:46:01 -05:00
Wyatt Johnson 48cc82b039 Merge branch 'master' into e2e-env-fix 2017-01-26 07:59:01 -07:00
Belen Curcio 88169526c3 Missing semicolon 2017-01-26 10:50:53 -03:00
Belen Curcio 4a54c571ed Open Stream and Closed Stream status 2017-01-26 10:45:50 -03:00
Belen Curcio 853981e21f Working configure tab and refresh data. Adding uncontrolled components, deleting state 2017-01-26 10:19:49 -03:00
Belen Curcio 74bef18098 user checker 2017-01-26 09:34:47 -03:00
Belen Curcio 64ed10845b Merge 2017-01-25 21:59:42 -03:00
David Erwin 0b73f18eb3 Merge pull request #258 from coralproject/streams-by-asset-id
Streams by asset
2017-01-25 17:48:41 -05:00
David Erwin 1a065ffa51 Merge branch 'streams-by-asset-id' of https://github.com/coralproject/talk into streams-by-asset-id 2017-01-25 17:45:09 -05:00
David Erwin c4256fd27b Ditch console logs 2017-01-25 17:44:21 -05:00
David Jay aa83aea849 Merge branch 'master' of github.com:coralproject/talk into e2e-env-fix 2017-01-25 17:17:04 -05:00
David Jay 31abe57cb2 Merge branch 'master' of github.com:coralproject/talk into moderate-bio
Integrating graphql, updating tests, and preserving transition to ModerationList from CommentList.
2017-01-25 17:15:12 -05:00
Wyatt Johnson 000f2c64bb Merge branch 'master' into streams-by-asset-id 2017-01-25 14:50:51 -07:00
Wyatt Johnson 34ca36822f Merge pull request #259 from coralproject/graphql-abstraction
Moved the graphql file out of the const string
2017-01-25 14:50:42 -07:00
Wyatt Johnson 0035037583 Merge branch 'master' into graphql-abstraction 2017-01-25 14:32:52 -07:00
Wyatt Johnson 693e92a627 Moved the graphql file out of the string 2017-01-25 14:31:11 -07:00
David Erwin b4abb9724e Fix homepage bug 2017-01-25 15:59:08 -05:00
David Erwin 9764631a37 Fix asset search skip limit bug 2017-01-25 15:37:18 -05:00
David Erwin e320a9dd20 Merge branch 'master' into streams-by-asset-id 2017-01-25 15:30:44 -05:00
David Erwin 1f1b1d43ae Add useful links 2017-01-25 15:27:38 -05:00
David Erwin 29e69a66bb Add basic index page for assets 2017-01-25 15:23:02 -05:00
Wyatt Johnson 9598c55466 Merge pull request #256 from coralproject/graoh-fixes
Comment ordering fixed, banned users can't do anything
2017-01-25 13:16:05 -07:00
Belen Curcio ec0dd34d40 Configure tab while you are loggedOff 2017-01-25 16:33:55 -03:00
David Erwin ad9684acd9 Create load stream by id route 2017-01-25 14:08:19 -05:00
Wyatt Johnson 91402a3e8b Moved PID into util.js 2017-01-25 11:57:40 -07:00
Wyatt Johnson edf9ced454 Added PID support 2017-01-25 11:11:33 -07:00
Belen Curcio 7c132600f0 Console log and typo 2017-01-25 11:47:35 -03:00
Belen Curcio b3eb3e3c43 Updated tests for Comment History 2017-01-25 11:44:51 -03:00
Belen Curcio d19d2ebe48 My Comment History Working - linted 2017-01-25 11:19:34 -03:00
Belen Curcio 30b6f8d112 my Comment History working 🎉 : TADA : 2017-01-25 11:04:23 -03:00
Belen Curcio 14483a39a0 Tests passing, removing legacy items 2017-01-25 08:38:03 -03:00
Belen Curcio c1d3cdce77 deprecating items 2017-01-25 08:11:52 -03:00
Belen Curcio cebc5f2f45 Adding fragment views 2017-01-25 07:09:11 -03:00
Belen Curcio b1848c28e4 Adding Missing Translations 2017-01-25 07:08:55 -03:00
Belen Curcio e993330467 Adding fragments 🎉 2017-01-25 05:20:34 -03:00
Belen Curcio e3b183c565 Items cleanup 2017-01-25 05:15:04 -03:00
Wyatt Johnson 373f119ac2 Added more comment sorting 2017-01-24 17:22:29 -07:00
Wyatt Johnson eb469df573 Removed debugging tool 2017-01-24 17:08:12 -07:00
Wyatt Johnson 26b7d48f1f Comment ordering fixed, banned users can't do anything 2017-01-24 17:06:40 -07:00
David Erwin 79b366f257 Merge pull request #255 from coralproject/translation-fix
fixed translation key, enforced moderation setting schema
2017-01-24 18:34:48 -05:00
Wyatt Johnson b6d917a22b fixed translation key, enforced moderation setting schema 2017-01-24 16:08:13 -07:00
David Erwin 622afcc80f Merge pull request #254 from coralproject/graphql
Graphql redux (as in 'the return of')
2017-01-24 18:02:18 -05:00
David Erwin a09c208f58 Merge branch 'master' into graphql 2017-01-24 17:56:04 -05:00
Wyatt Johnson 2698045e82 Fixed capital 2017-01-24 15:50:37 -07:00
Wyatt Johnson 5a2d8c5201 Fixed template tag 2017-01-24 15:40:32 -07:00
David Erwin 9616c50d49 Merge pull request #251 from coralproject/graphql
Graphql for the comment feed
2017-01-24 17:24:11 -05:00
David Erwin 66b0babdde Merge branch 'graphql' of https://github.com/coralproject/talk into graphql 2017-01-24 17:17:38 -05:00
David Erwin 9435b627b1 Uppercase more enums in coral-admin 2017-01-24 17:17:03 -05:00
Riley Davis da7349343e Merged branch graphql into graphql 2017-01-24 15:09:09 -07:00
Riley Davis 62ae297887 make sure the app does not break if you don't have react devtools installed 2017-01-24 15:08:38 -07:00
Belen Curcio d2acffc19c linting! 2017-01-24 19:07:53 -03:00
Belen Curcio 054baa0530 çonfig actions not defined 2017-01-24 19:01:45 -03:00
Belen Curcio fb791a9df9 merge 2017-01-24 18:52:37 -03:00
Belen Curcio ecfb365c24 log 2017-01-24 18:51:06 -03:00
Wyatt Johnson fa300b1c24 Fixes to circle 2017-01-24 14:38:44 -07:00
David Jay b0acdff34d Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-24 16:23:27 -05:00
Belen Curcio 6f44b9cba0 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-24 18:23:11 -03:00
David Jay 7c3f566202 Handling links inside of iframe in via Pym. 2017-01-24 16:21:49 -05:00
Belen Curcio 9edf99730d Status 2017-01-24 18:21:49 -03:00
Wyatt Johnson 050e98f157 Fixed report location 2017-01-24 14:12:52 -07:00
David Erwin 5566856542 Merge branch 'graphql' of https://github.com/coralproject/talk into graphql 2017-01-24 16:12:13 -05:00
David Erwin 53dd34f406 Updating admin screen for all caps enums 2017-01-24 16:11:17 -05:00
Belen Curcio 9d7b727f08 Settings container 2017-01-24 18:08:58 -03:00
Wyatt Johnson 603854c0be Adjusted tests for circle 2017-01-24 14:06:39 -07:00
David Jay ce73118f53 Loading history links in parent page. 2017-01-24 16:04:03 -05:00
Wyatt Johnson 2ffecbeeff Removed build step, is already done via postinstall hook 2017-01-24 13:46:53 -07:00
Wyatt Johnson 0a5a23c1f3 Added fixes for tests on CI 2017-01-24 13:45:06 -07:00
Belen Curcio ee9674cd23 Settings stream tab 2017-01-24 17:27:30 -03:00
Wyatt Johnson 9dc57c82d1 Fixes to consts + services 2017-01-24 13:27:22 -07:00
Belen Curcio 5750758cf1 Error with asset resolver 2017-01-24 17:27:05 -03:00
David Jay 88ed6ffe76 Updating logic for returning users to frontend. 2017-01-24 15:20:27 -05:00
Wyatt Johnson aa1f06d1ee Added explicit joinBy to dataLoader reponse 2017-01-24 13:12:51 -07:00
Wyatt Johnson cda768d694 Fixer pre/post 2017-01-24 13:10:35 -07:00
Belen Curcio 33ec0798e9 Uppercase model 2017-01-24 17:07:23 -03:00
Wyatt Johnson eeaefd9656 fixes for cli 2017-01-24 13:01:56 -07:00
Wyatt Johnson 3a59a785f8 Uppercased status's 2017-01-24 12:58:20 -07:00
Wyatt Johnson 5e9f8eb624 Fixed statuses 2017-01-24 12:43:22 -07:00
David Jay d921a59858 Hiding bio tooltip when the user has no bio. 2017-01-24 14:39:46 -05:00
David Jay 74ced3b2fd Removing display of user ID in settings. 2017-01-24 14:35:14 -05:00
David Jay 6e9757ae91 Updating tests to use capitalized admin. 2017-01-24 14:27:50 -05:00
David Jay 8b8bef4ea7 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-24 14:19:58 -05:00
David Jay 4b33199f2f Adding signin button to settings page. 2017-01-24 14:19:20 -05:00
Wyatt Johnson 368606befe Fixed yet, another, import 2017-01-24 12:18:32 -07:00
Wyatt Johnson 71f219c0c4 Fixed another import 2017-01-24 12:17:06 -07:00
Wyatt Johnson 0e27984518 Fixed import 2017-01-24 12:15:31 -07:00
Riley Davis 98b3d299e5 Merged branch graphql into graphql 2017-01-24 12:14:06 -07:00
Riley Davis d977c33cf5 refetch when you post a comment 2017-01-24 12:13:56 -07:00
Wyatt Johnson a7e9c0c776 Const, service, and model updates
- Updated enum values to be uppercase
- Updated services to expose service models
- Updated models to only export the mongoose model
- Moved all mongoose static methods over to new services
- Updated tests to refelct new setup

BREAKING

- Status that were uppercased (caps) have caused issues with the
  admin pages
2017-01-24 12:10:32 -07:00
Riley Davis 0994023864 Merged branch graphql into graphql 2017-01-24 12:09:16 -07:00
Riley Davis 8cd929bb1b npm chokes on .js package names 2017-01-24 12:09:04 -07:00
David Jay 840e0d0ef6 Re-adding buttons when the user is not logged in and showing login if a non-logged-in user clicks 'Reply.' 2017-01-24 14:00:16 -05:00
David Jay 90c58885aa Fixing lint errors and renaming Pym file. 2017-01-24 13:23:42 -05:00
David Jay 0d7b482dc1 Re-adding pym import. 2017-01-24 13:21:13 -05:00
Belen Curcio c1fea3151c Merge 2017-01-24 15:15:17 -03:00
Belen Curcio a409fcbab7 Asset reducers 2017-01-24 15:10:09 -03:00
Riley Davis 4e8306bcc9 close reply box when comment is posted 2017-01-24 10:55:51 -07:00
David Jay c4e1c1ae44 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-24 12:53:30 -05:00
David Jay 58e50b4242 Fixing permalinks and moving Pym to a seperate file. 2017-01-24 12:51:52 -05:00
Riley Davis 7fd075a7a6 posting a reply refetches comments 2017-01-24 10:43:29 -07:00
David Jay 75bcf29154 Replies to replies are children of the root comment. 2017-01-24 12:37:50 -05:00
David Jay 33d274386a Re-adding display of comment date. 2017-01-24 12:24:30 -05:00
Belen Curcio 365f60c428 Reducer and constants 2017-01-24 14:22:58 -03:00
Belen Curcio f375ff48c9 dispatching 2017-01-24 14:13:43 -03:00
Belen Curcio 765edb48b9 Émbed 2017-01-24 14:07:46 -03:00
David Jay 9fbd1d3a77 Displaying author names and bios. 2017-01-24 11:49:59 -05:00
Belen Curcio 9fd184f638 Store normalized 💪 2017-01-24 10:06:42 -03:00
Belen Curcio c3f238b0b2 Connected to the Apollo Store 🎉 2017-01-24 09:56:26 -03:00
Riley Davis fa4c6e8ec5 lint! 2017-01-23 16:56:44 -07:00
Riley Davis 6282c39f3a merge remote. can reply to a comment 2017-01-23 16:52:16 -07:00
Wyatt Johnson 9e22c100f0 Added admin login form (naive approach) 2017-01-23 16:31:37 -07:00
Belen Curcio 01f905b79d Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-23 19:46:34 -03:00
Belen Curcio adf4729d89 Resolver settings fix, info box and func working! 2017-01-23 19:45:34 -03:00
David Jay 44058b1718 Adding flag action buttons. 2017-01-23 17:34:24 -05:00
David Jay da761468b6 Enabling like buttons. 2017-01-23 16:18:51 -05:00
David Jay a8227bd4ad Activating post and delete actions on likebutton. 2017-01-23 14:22:25 -05:00
Wyatt Johnson d1621ad424 Fixed context 2017-01-23 11:57:35 -07:00
David Jay b2d9688c89 Adding postAction mutation and fixing lint errors. 2017-01-23 13:25:24 -05:00
David Jay f023d1de08 Merge branch 'master' into e2e-env-fix 2017-01-23 12:07:12 -05:00
Wyatt Johnson 6563f29ccf Moved tests -> test because mocha likes that 2017-01-20 17:28:08 -07:00
Wyatt Johnson fbd0bf52fe Fixed methods 2017-01-20 16:53:43 -07:00
Wyatt Johnson 0ec2c11cf7 modularized mutators, loaders 2017-01-20 16:49:19 -07:00
Riley Davis 32d23214f1 replies 2017-01-20 16:23:34 -07:00
Riley Davis 0dcc912fd0 whoops, committed broken code 2017-01-20 15:58:36 -07:00
Riley Davis dfc073ab10 Merge remote-tracking branch 'refs/remotes/origin/graphql'
Conflicts:
	client/coral-embed-stream/src/Embed.js
2017-01-20 15:56:03 -07:00
Riley Davis d724351fb6 merge my changes 2017-01-20 15:54:18 -07:00
Belen Curcio 9c40ee1245 Login sign In back on! 🎉 2017-01-20 19:35:31 -03:00
Belen Curcio 450d74848e Notifications are back 2017-01-20 19:20:43 -03:00
Belen Curcio 39bf092ebb Correct Status 2017-01-20 18:56:42 -03:00
Belen Curcio b98b71189f atempt to solution 2017-01-20 18:53:11 -03:00
Belen Curcio f813a77194 queries and mutations 2017-01-20 18:48:35 -03:00
Belen Curcio 715faf321f Wordlist is undefined 2017-01-20 18:04:30 -03:00
Belen Curcio 2615db8575 mutations and queries, lint errors 2017-01-20 17:59:28 -03:00
Belen Curcio ae29d65c75 mutations and queries in separate folders 🎉 2017-01-20 17:40:33 -03:00
Belen Curcio d89f41a041 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-20 16:47:28 -03:00
Belen Curcio 3c540b9daa createComment and suspect list 2017-01-20 16:47:02 -03:00
Riley Davis 8f95e29315 Stream is a separate component that renders comments 2017-01-20 12:41:33 -07:00
Belen Curcio f0f692fc26 ádding webpack loader and post comment 2017-01-20 16:39:23 -03:00
Riley Davis d14fa33077 rename files to make more sense 2017-01-20 12:36:33 -07:00
Riley Davis c16f3b8fbe change scalar URL type for a bit 2017-01-20 11:54:18 -07:00
Belen Curcio 7a5aa0a80c Changes 2017-01-20 15:25:18 -03:00
Belen Curcio cc740a4a5e Adding closedAt status commentStream 2017-01-20 12:52:35 -03:00
Belen Curcio 0f15c21cc9 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-20 12:34:14 -03:00
Belen Curcio 89c1fd19b7 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-20 12:32:46 -03:00
Wyatt Johnson 07b2f9c2e5 Update typeDefs.js 2017-01-20 08:13:45 -07:00
Wyatt Johnson f286263f1f Adjusted placement of server side graph code. 2017-01-19 18:26:33 -07:00
Wyatt Johnson fec18b50d1 Merge branch 'master' into graphql 2017-01-19 18:08:38 -07:00
Wyatt Johnson 1db1908778 Introduced some changes to the resolvers + mutators 2017-01-19 18:06:06 -07:00
David Jay 8bad6f7a73 Enabling signup. 2017-01-19 15:51:38 -05:00
David Jay e9846c654c Enabling stream login with graphql. 2017-01-19 15:37:05 -05:00
David Jay 672d1ee19e Updating store to include auth and apollo. 2017-01-19 13:39:46 -05:00
Wyatt Johnson 840c91274d Added comments to typeDefs 2017-01-19 11:17:47 -07:00
Wyatt Johnson 03a5acc736 Modularized reolvers 2017-01-19 11:01:24 -07:00
Wyatt Johnson d683b09b1d Added more mutators 2017-01-19 10:39:29 -07:00
Belen Curcio b172e30e5b By doing this we can refactor one by one and still having a working app 2017-01-19 11:30:30 -03:00
Belen Curcio 2d414a5ab7 Transport, Client, from coral-framework 2017-01-19 10:01:43 -03:00
David Jay d34d511ce6 Merge branch 'graphql' of github.com:coralproject/talk into graphql 2017-01-18 23:13:50 -05:00
David Jay a40971e376 Adding thunk to apollog redux store. 2017-01-18 19:23:47 -05:00
David Jay 44f3f99b47 Adding ability to query the currently logged in user. 2017-01-18 19:20:36 -05:00
David Jay d3c215a18d Switching bare-bones commentstream to graphql. 2017-01-18 18:23:32 -05:00
Riley Davis bbfd017998 adding changes 2017-01-18 15:27:48 -07:00
Riley Davis 58e7abbe0c sdfasdf 2017-01-18 15:25:48 -07:00
Riley Davis 89a188255f super amazing code. 2017-01-18 15:23:07 -07:00
Riley Davis 5f63af8140 Merged branch master into graphql 2017-01-18 14:24:45 -07:00
David Erwin 347fe8bf2b Showing graphql data in console 2017-01-18 16:15:40 -05:00
David Erwin e36ecdfb33 Implement stream with data fetch 2017-01-18 15:58:36 -05:00
Wyatt Johnson 5b30fd89c8 More comments 2017-01-18 11:28:39 -07:00
Wyatt Johnson 52ed18a9b8 Added comments 2017-01-18 11:23:47 -07:00
David Jay f8e55ddc8b Merge branch 'master' of github.com:coralproject/talk into e2e-env-fix 2017-01-18 12:44:44 -05:00
David Jay 074630e628 Updating usernames and passwords to new standards. 2017-01-18 12:43:09 -05:00
David Jay b59512d2b8 Allowing e2e to run with only node_env=test. 2017-01-18 12:12:49 -05:00
Wyatt Johnson 17849898ae First pass 2017-01-18 00:09:26 -07:00
David Jay dc63cc6bce Merge branch 'moderate-bio' of github.com:coralproject/talk into moderate-bio 2017-01-13 16:39:17 -05:00
David Jay 0131dd6b13 Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-13 16:37:31 -05:00
David Jay 1432cfba4a Updating signing dialog style to render as intended in Firefox. 2017-01-12 15:49:23 -05:00
Belén Curcio b957e75d68 Merge branch 'master' into moderate-bio 2017-01-12 09:21:02 -03:00
David Jay 61bc87bafc Adding ALL moderation list. 2017-01-11 17:35:09 -05:00
David Jay 30c7539cde Merge branch 'moderate-bio' of github.com:coralproject/talk into moderate-bio 2017-01-11 13:14:44 -05:00
David Jay e8dc319a9a Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-11 13:12:07 -05:00
David Jay 98a6ba5f1f Merge branch 'master' into moderate-bio 2017-01-11 12:32:25 -05:00
David Jay 9d7810f48c Merge branch 'master' into moderate-bio 2017-01-10 19:48:09 -05:00
David Jay f957e3c8ce Closing suspend user modal once user is suspended and e-mail is sent. 2017-01-09 18:33:12 -05:00
David Jay 8fa7561e20 Updating item_type for consitency. 2017-01-09 18:27:54 -05:00
David Jay 01d9fc313e Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-09 18:20:44 -05:00
David Jay 8601562418 Improving extraction of user e-mail address. 2017-01-09 18:18:19 -05:00
David Jay a2704e3099 Updating user email address. 2017-01-09 18:15:25 -05:00
David Jay 9809403701 Pluralizing user item_type for consistency. 2017-01-09 17:43:39 -05:00
David Jay b8fc2c9cc7 Addressing CSRF and user status issues. 2017-01-09 15:39:08 -05:00
David Jay 0f24ab6b28 Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-06 15:56:03 -05:00
David Jay ec020df488 Adding account flags tab. 2017-01-06 15:42:19 -05:00
David Jay d5a82f04c2 Fixing issues with e-mail and status update. 2017-01-06 15:35:32 -05:00
David Jay 9e651a9292 Adding actions to suspend users and send e-mail. 2017-01-06 15:16:23 -05:00
David Jay 565e34e09d Adding endpoint for admins to send transactional e-mail. 2017-01-06 14:29:50 -05:00
David Jay e510f99754 Adding style to suspension modal. 2017-01-05 20:51:40 -05:00
David Jay a468fe4c28 Adding language and e-mail input to suspend user modal. 2017-01-05 16:51:37 -05:00
David Jay 890ab810f3 Activating SuspendUserModal and updating language for clarity. 2017-01-05 14:50:21 -05:00
David Jay 9ba0176b3c Seperating username and bio flags 2017-01-04 16:11:31 -05:00
David Jay 7a239c02ab Limiting to user actions. 2017-01-04 15:56:52 -05:00
David Jay 216b21251d Styling UserActions Component. 2017-01-04 15:33:41 -05:00
David Jay c1e3580ef7 Resolving error in moderation list. 2017-01-04 14:46:06 -05:00
David Jay 0a064c0e11 Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-04 14:33:27 -05:00
David Jay 3fa0460f34 Sending actions to ModerationList. 2017-01-04 14:20:00 -05:00
David Jay f792aeccd2 Adding user display. 2017-01-04 13:39:10 -05:00
David Jay b517adab56 Moving commentList to ModerationList 2017-01-03 16:57:53 -05:00
David Jay 5c5a7f5ba5 Switching to central call for mod queue to support current reducer structure. 2017-01-03 15:13:09 -05:00
David Jay dd7bdb8a26 Adding actions for fetching user moderation queue. 2017-01-03 15:05:45 -05:00
David Jay 800382132d Merge branch 'master' of github.com:coralproject/talk into moderate-bio 2017-01-03 14:29:05 -05:00
David Jay dff8413404 Adding pending users and actions to moderation queue call. 2016-12-19 20:34:27 -05:00
David Jay 20505acfbe Updating tests for pending users endpoint. 2016-12-19 20:27:58 -05:00
David Jay 845d274b9b Creatind endpoint to return pending users and action summaries. 2016-12-19 19:54:20 -05:00
414 changed files with 22529 additions and 7054 deletions
+2
View File
@@ -1 +1,3 @@
node_modules
test
.git
+1 -1
View File
@@ -17,7 +17,7 @@
"no-template-curly-in-string": [1],
"no-unsafe-negation": [1],
"array-callback-return": [1],
"eqeqeq": [2],
"eqeqeq": [2, "smart"],
"no-eval": [2],
"no-global-assign": [2],
"no-implied-eval": [2],
+2 -3
View File
@@ -3,13 +3,12 @@ npm-debug.log*
dist
!dist/coral-admin
dist/coral-admin/bundle.js
tests/e2e/reports
test/e2e/reports
.DS_Store
*.iml
*.swp
dump.rdb
.env
gaba.cfg
*.cfg
.idea/
coverage/
yarn.lock
+2 -1
View File
@@ -1,4 +1,5 @@
{
"verbose": true,
"ignore": ["tests/*", "client/*", "dist/*"]
"ignore": ["test/*", "client/*", "dist/*"],
"ext": "js,json,graphql"
}
-3
View File
@@ -1,3 +0,0 @@
language: node_js
node_js:
- 4
+7 -9
View File
@@ -1,9 +1,10 @@
# Contributor's Guide
Welcome! We are very excited that you are interested in contributing to Talk.
Welcome! We are very excited that you are interested in contributing to Talk.
This document is a companion to help you approach contributing. If it does not do so, please [let us know how we can improve it](https://github.com/coralproject/talk/issues)!
By contributing to this project you agree to the [Code of Conduct](https://coralproject.net/code-of-conduct.html).
## Product Roadmap
@@ -13,7 +14,7 @@ You can view product ideas and our longer term roadmap here https://trello.com/b
## Contribute to the documentation
Clear docs are a prerequisite for a successful open source project. We value non-code and code contributions equally.
Clear docs are a prerequisite for a successful open source project. We value non-code and code contributions equally.
We are looking for _documentarians_ to:
@@ -40,7 +41,7 @@ Talk is designed to integrate into existing environments in a variety of ways:
If you're considering deploying Talk, [please let us know](https://github.com/coralproject/talk/wiki/Contact-Us)! We are quite literally doing this for you and want to help you succeed any way we can.
If you are writing custom integration code in your fork of Talk, please consider keeping it generic and filing a Pull Request to contribute it back to the project! See our [forking and merging guidelines](https://github.com/coralproject/talk/wiki/Forking,-Branching-and-Merging) for more info.
If you are writing custom integration code in your fork of Talk, please consider keeping it generic and filing a Pull Request to contribute it back to the project! See our [forking and merging guidelines](https://github.com/coralproject/talk/wiki/Forking,-Branching-and-Merging) for more info.
## Write some code
@@ -48,9 +49,9 @@ First, [set up a dev environment](https://github.com/coralproject/talk/blob/mast
### Build a New Feature / Plugin
Talk is beginning life as a Commenting Platform, but is architected to support many varieties of community engagement.
Talk is beginning life as a Commenting Platform, but is architected to support many varieties of community engagement.
Please [contact us](https://github.com/coralproject/talk/wiki/Contact-Us) early and often if you'd like to help. We would love to hear your ideas for features and plugins and help you find a way to productively engage the project.
Please [contact us](https://github.com/coralproject/talk/wiki/Contact-Us) early and often if you'd like to help. We would love to hear your ideas for features and plugins and help you find a way to productively engage the project.
To get an idea of where the Coral Team is going, see:
@@ -66,13 +67,10 @@ Examples:
### Work on the Core
There is always more work to be done to make an application more stable, scaleable and secure.
There is always more work to be done to make an application more stable, scaleable and secure.
If you see issues in the code or have ideas on how we may improve Talk, please consider:
* [contributing a fix](https://github.com/coralproject/talk/wiki/Forking,-Branching-and-Merging),
* [filing an issue](https://github.com/coralproject/talk/issues), or
* or otherwise [letting us know](https://github.com/coralproject/talk/wiki/Contact-Us).
+7 -4
View File
@@ -1,4 +1,7 @@
FROM node:7
FROM node:7.6
# Add node-gyp for bcrypt build support
RUN yarn global add node-gyp
# Create app directory
RUN mkdir -p /usr/src/app
@@ -11,10 +14,10 @@ ENV TALK_PORT 5000
EXPOSE 5000
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install --production
COPY package.json yarn.lock /usr/src/app/
RUN yarn install --production
# Bundle app source
COPY . /usr/src/app
CMD [ "npm", "start" ]
CMD [ "yarn", "start" ]
+64 -65
View File
@@ -1,95 +1,94 @@
# Installing a dev environment
By contributing to this project you agree to the [Code of Conduct](https://coralproject.net/code-of-conduct.html).
# Installation
## Requirements
### System
- Any flavor of Linux, OSX or Windows
- Any flavour of Linux, OSX or Windows
- 1GB memory (minimum)
- 5GB storage (minimum)
### Software
## Installation From Source
* [Node](https://nodejs.org/es/download/package-manager) v7 or later
* Mongo v3.2 or later
* Redis v3.2 or later
### Requirements
There are some runtime requirements for running Talk from source:
- [Node](https://nodejs.org/) v7 or later
- [MongoDB](https://www.mongodb.com/) v3.4 or later
- [Redis](https://redis.io/) v3.2 or later
- [Yarn](https://yarnpkg.com/) v0.19.1 or later
_Please be sure to check the versions of these requirements. Insufficient versions of these may lead to unexpected errors!_
## First time setup
### Installing
### Installation
```bash
# Download the tarball containing the repository
curl -L https://github.com/coralproject/talk/tarball/master -o coralproject-talk.tar.gz
Navigate to a directory.
# Untar that file and change to that directory
tar xpf coralproject-talk.tar.gz
mv coralproject-talk-* coralproject-talk
cd coralproject-talk
```
git clone https://github.com/coralproject/talk
cd talk
npm install
# Install package dependancies
yarn
# Build static files
yarn build
```
### Environmental Variables
### Running
Talk uses environmental variables for configuration. You can learn about them in the [README file](README.md).
Refer to the `README.md` file for required configuration variables to add to the
environment.
You can start the server after configuring the server using the command:
## Workflows
### The server
Starting the server:
```
npm start
```bash
yarn start
```
Browse to `http://localhost:3000` (or your custom port.)
You can see other scripts we've made available by consulting the `package.json`
file under the `scripts` key including:
### Building the front end
- `yarn test` run unit tests
- `yarn e2e` run end to end tests
- `yarn build-watch` watch for changes to client files and build static assets
- `yarn dev-start` watch for changes to server files and reload the server
Our build process will build all front end components registered [here](https://github.com/coralproject/talk/blob/6052cac1d3494f8060325a88bb2ce03c88c2f94c/webpack.config.dev.js#L9-L15).
## Installation From Docker Hub
One time build:
### Requirements
```
npm build
There are some runtime requirements for running Talk for Docker:
- [MongoDB](https://www.mongodb.com/) v3.2 or later
- [Redis](https://redis.io/) v3.2 or later
- [Docker](https://www.docker.com/) v1.13.0 or later
- [Docker Compose](https://docs.docker.com/compose/) v1.10.0 or later
_Please be sure to check the versions of these requirements. Insufficient versions of these may lead to unexpected errors!_
### Installing
```bash
# Create a directory for talk
mkdir coralproject-talk
cd coralproject-talk
# Download the docker-compose.yml file from the repository
curl -LO https://raw.githubusercontent.com/coralproject/talk/master/docker-compose.yml
```
Build, then rebuild when a file is updated (development build):
At this stage, you should refer to the `README.md` file for required
configuration variables to add to the environment key for the `talk` service
listed in the `docker-compose.yml` file.
### Running
```bash
# Start the services using compose
docker-compose up -d
```
npm build-watch
```
### Testing
Run all tests once:
`
npm test
`
Run our end to end tests (will install Selenium and nightwatch):
`
npm run e2e
`
_Please ensure all tests are passing before submitting a PR!_
## Troubleshooting
##### Can't ping the redis server!
- Check that Redis Server is running.
- Check that TALK_REDIS_URL is set.
##### Authenticaiton doesn't work!
- Make sure Redis is the correct version.
+6 -2
View File
@@ -1,6 +1,9 @@
# Talk [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk)
A commenting platform from [The Coral Project](https://coralproject.net).
A commenting platform from [The Coral Project](https://coralproject.net). Talk enters a closed beta in March 2017, but you can download the code for our alpha here. [Read more about Talk here.](https://coralproject.net/products/talk.html)
Third party licenses are available via the `/client/3rdpartylicenses.txt`
endpoint when the server is running with built assets.
## Contributing to Talk
@@ -12,7 +15,7 @@ See our [Contribution Guide](https://github.com/coralproject/talk/blob/master/CO
To set up a development environment or build from source, see [INSTALL.md](https://github.com/coralproject/talk/blob/master/INSTALL.md).
To launch a Talk server of your own from your browser without any need to muck about in a terminal or think about engineering concepts, stay tuned. We will launch [our installer](https://github.com/coralproject/talk-install) shortly!!
To launch a Talk server of your own from your browser without any need to muck about in a terminal or think about engineering concepts, stay tuned. We will launch [our installer](https://github.com/coralproject/talk-install) shortly!
### Configuration
@@ -37,6 +40,7 @@ Facebook Login enabled app.
- `TALK_SMTP_PASSWORD` (*required for email*) - password for the SMTP provider you are using.
- `TALK_SMTP_HOST` (*required for email*) - SMTP host url with format `smtp.domain.com`.
- `TALK_SMTP_PORT` (*required for email*) - SMTP port.
- `TALK_INSTALL_LOCK` (_optional for dynamic setup_) - Defaults to `FALSE`. When `TRUE`, disables the dynamic setup endpoint.
Refer to the wiki page on [Configuration Loading](https://github.com/coralproject/talk/wiki/Configuration-Loading) for
alternative methods of loading configuration during development.
+25 -1
View File
@@ -10,6 +10,8 @@ const RedisStore = require('connect-redis')(session);
const redis = require('./services/redis');
const csrf = require('csurf');
const errors = require('./errors');
const graph = require('./graph');
const apollo = require('graphql-server-express');
const app = express();
@@ -49,7 +51,7 @@ const session_opts = {
name: 'talk.sid',
cookie: {
secure: false,
maxAge: 18000000, // 30 minutes for expiry.
maxAge: 36000000, // 1 hour for expiry.
},
store: new RedisStore({
ttl: 1800,
@@ -77,6 +79,28 @@ app.use(session(session_opts));
app.use(passport.initialize());
app.use(passport.session());
//==============================================================================
// GraphQL Router
//==============================================================================
// GraphQL endpoint.
app.use('/api/v1/graph/ql', apollo.graphqlExpress(graph.createGraphOptions));
// Only include the graphiql tool if we aren't in production mode.
if (app.get('env') !== 'production') {
// Interactive graphiql interface.
app.use('/api/v1/graph/iql', apollo.graphiqlExpress({
endpointURL: '/api/v1/graph/ql'
}));
// GraphQL documention.
app.get('/admin/docs', (req, res) => {
res.render('admin/docs');
});
}
//==============================================================================
// CSRF MIDDLEWARE
//==============================================================================
+1 -1
View File
@@ -29,5 +29,5 @@
"as": "POSTMARK"
}],
"image": "heroku/nodejs",
"success_url": "/admin/setup"
"success_url": "/admin/install"
}
+14 -32
View File
@@ -4,43 +4,25 @@
* Module dependencies.
*/
const program = require('commander');
const pkg = require('../package.json');
const dotenv = require('dotenv');
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
program
.version(pkg.version)
.option('-c, --config [path]', 'Specify the configuration file to load')
.parse(process.argv);
if (program.config) {
let r = dotenv.config({
path: program.config
});
if (r.error) {
throw r.error;
}
}
// Perform rewrites to the runtime environment variables based on the contents
// of the process.env.REWRITE_ENV if it exists. This is done here as it is the
// entrypoint for the entire application.
require('env-rewrite').rewrite();
// const util = require('./util');
const program = require('./commander');
program
.command('serve', 'serve the application')
.command('assets', 'interact with assets')
.command('settings', 'work with the application settings')
.command('setup', 'setup the application')
.command('jobs', 'work with the job queues')
.command('users', 'work with the application auth')
.parse(process.argv);
// If there is no command listed, output help.
if (!process.argv.slice(2).length) {
program.outputHelp();
}
/**
* When this provess exists, check to see if we have a running command, if we do
* check to see if it is still running. If it is, then kill it with a SIGINT
* signal. This is for the use case where we want to kill the process that is
* labled with the PID written out by the parent process.
*/
process.once('exit', () => {
if ((program.runningCommand.killed === false) && (program.runningCommand.exitCode === null)) {
program.runningCommand.kill('SIGINT');
}
});
+5 -9
View File
@@ -4,14 +4,13 @@
* Module dependencies.
*/
const program = require('commander');
const pkg = require('../package.json');
const program = require('./commander');
const parseDuration = require('parse-duration');
const Table = require('cli-table');
const Asset = require('../models/asset');
const AssetModel = require('../models/asset');
const mongoose = require('../services/mongoose');
const scraper = require('../services/scraper');
const util = require('../util');
const util = require('./util');
// Register the shutdown criteria.
util.onshutdown([
@@ -22,7 +21,7 @@ util.onshutdown([
* Lists all the assets registered in the database.
*/
function listAssets() {
Asset
AssetModel
.find({})
.sort({'created_at': 1})
.then((asset) => {
@@ -56,7 +55,7 @@ function refreshAssets(ageString) {
const ageMs = parseDuration(ageString);
const age = new Date(now - ageMs);
Asset.find({
AssetModel.find({
$or: [
{
scraped: {
@@ -86,9 +85,6 @@ function refreshAssets(ageString) {
// Setting up the program command line arguments.
//==============================================================================
program
.version(pkg.version);
program
.command('list')
.description('list all the assets in the database')
+2 -2
View File
@@ -4,10 +4,10 @@
* Module dependencies.
*/
const program = require('commander');
const program = require('./commander');
const scraper = require('../services/scraper');
const mailer = require('../services/mailer');
const util = require('../util');
const util = require('./util');
const mongoose = require('../services/mongoose');
const kue = require('../services/kue');
+9 -22
View File
@@ -1,14 +1,13 @@
#!/usr/bin/env node
const app = require('../app');
const debug = require('debug')('talk:server');
const program = require('./commander');
const http = require('http');
const init = require('../init');
const scraper = require('../services/scraper');
const mailer = require('../services/mailer');
const kue = require('../services/kue');
const mongoose = require('../services/mongoose');
const util = require('../util');
const util = require('./util');
/**
* Get port from environment and store in Express.
@@ -79,34 +78,22 @@ function onListening() {
let bind = typeof addr === 'string'
? `pipe ${ addr}`
: `port ${ addr.port}`;
debug(`Listening on ${ bind}`);
console.log(`Listening on ${ bind}`);
}
/**
* Start the app.
*/
function startApp() {
init().then(() => {
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
})
.catch((err) => {
console.error(err);
util.shutdown(1);
});
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
}
/**
* Module dependencies.
*/
const program = require('commander');
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
-45
View File
@@ -1,45 +0,0 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const program = require('commander');
const mongoose = require('../services/mongoose');
const Setting = require('../models/setting');
const util = require('../util');
// Register the shutdown criteria.
util.onshutdown([
() => mongoose.disconnect()
]);
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
program
.command('init')
.description('initilizes the talk settings')
.action(() => {
const defaults = {id: '1', moderation: 'pre'};
Setting
.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true})
.then(() => {
console.log('Created settings object.');
util.shutdown();
})
.catch((err) => {
console.error(`failed to create the settings object ${JSON.stringify(err)}`);
util.shutdown(1);
});
});
program.parse(process.argv);
// If there is no command listed, output help.
if (!process.argv.slice(2).length) {
program.outputHelp();
util.shutdown();
}
Executable
+197
View File
@@ -0,0 +1,197 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const program = require('./commander');
const inquirer = require('inquirer');
const mongoose = require('../services/mongoose');
const SettingModel = require('../models/setting');
const SettingsService = require('../services/settings');
const SetupService = require('../services/setup');
const UsersService = require('../services/users');
const util = require('./util');
const errors = require('../errors');
// Register the shutdown criteria.
util.onshutdown([
() => mongoose.disconnect()
]);
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
program
.description('runs the setup wizard to setup the application')
.option('--defaults', 'apply defaults for config instead of prompting')
.parse(process.argv);
//==============================================================================
// Setup the application
//==============================================================================
const performSetup = () => {
if (program.defaults) {
return SettingsService
.init()
.then(() => {
console.log('Settings created.');
console.log('\nTalk is now installed!');
util.shutdown();
})
.catch((err) => {
console.error(err);
util.shutdown(1);
});
}
// Get the current settings, we are expecing an error here.
return SettingsService
.retrieve()
.then(() => {
// We should NOT have gotten a settings object, this means that the
// application is already setup. Error out here.
throw errors.ErrSettingsInit;
})
.catch((err) => {
// If the error is `not init`, then we're good, otherwise, it's something
// else.
if (err !== errors.ErrSettingsNotInit) {
throw err;
}
})
.then(() => {
// Create the base settings model.
let settings = new SettingModel();
console.log('We\'ll ask you some questions in order to setup your installation of Talk.\n');
return inquirer.prompt([
{
type: 'input',
name: 'organizationName',
message: 'Organization Name',
default: settings.organizationName,
validate: (input) => {
if (input && input.length > 0) {
return true;
}
return 'Organization Name is required.';
}
},
{
type: 'list',
choices: SettingModel.MODERATION_OPTIONS,
name: 'moderation',
default: settings.moderation,
message: 'Select a moderation mode'
},
{
type: 'confirm',
name: 'requireEmailConfirmation',
default: settings.requireEmailConfirmation,
message: 'Should emails always be confirmed'
}
])
.then((answers) => {
// Update the settings that were changed.
Object.keys(answers).forEach((key) => {
if (answers[key] !== undefined) {
settings[key] = answers[key];
}
});
console.log('\nWe\'ll ask you some questions about your first admin user.\n');
return inquirer.prompt([
{
type: 'input',
name: 'username',
message: 'Username',
filter: (username) => {
return UsersService
.isValidUsername(username, false)
.catch((err) => {
throw err.message;
});
}
},
{
name: 'email',
message: 'Email',
format: 'email',
validate: (value) => {
if (value && value.length >= 3) {
return true;
}
return 'Email is required';
}
},
{
name: 'password',
message: 'Password',
type: 'password',
filter: (password) => {
return UsersService
.isValidPassword(password)
.catch((err) => {
throw err.message;
});
}
},
{
name: 'confirmPassword',
message: 'Confirm Password',
type: 'password',
filter: (confirmPassword) => {
return UsersService
.isValidPassword(confirmPassword)
.catch((err) => {
throw err.message;
});
}
},
]);
})
.then((user) => {
if (user.password !== user.confirmPassword) {
return Promise.reject(new Error('Passwords do not match'));
}
return SetupService.setup({
settings: settings.toObject(),
user: {
email: user.email,
username: user.username,
password: user.password
}
});
});
})
.then(({user}) => {
console.log('Settings created.');
console.log(`User ${user.id} created.`);
console.log('\nTalk is now installed!');
console.log('\nWe recommend adding TALK_INSTALL_LOCK=TRUE to your environment to turn off the dynamic setup.');
util.shutdown();
})
.catch((err) => {
console.error(err);
util.shutdown(1);
});
};
// Start tthe setup process.
performSetup();
+156 -121
View File
@@ -4,105 +4,139 @@
* Module dependencies.
*/
const program = require('commander');
const pkg = require('../package.json');
const prompt = require('prompt');
const User = require('../models/user');
const program = require('./commander');
const inquirer = require('inquirer');
const UsersService = require('../services/users');
const UserModel = require('../models/user');
const mongoose = require('../services/mongoose');
const util = require('../util');
const util = require('./util');
const Table = require('cli-table');
const validateRequired = (msg = 'Field is required', len = 1) => (input) => {
if (input && input.length >= len) {
return true;
}
return msg;
};
// Regeister the shutdown criteria.
util.onshutdown([
() => mongoose.disconnect()
]);
function getUserCreateAnswers(options) {
if (options.flag_mode) {
let user = {
email: options.email,
password: options.password,
confirmPassword: options.password,
username: options.name,
roles: []
};
if (options.role && UserModel.USER_ROLES.indexOf(options.role) > -1) {
user.roles = [options.role];
}
return Promise.resolve(user);
}
return inquirer.prompt([
{
name: 'email',
message: 'Email',
format: 'email',
validate: validateRequired('Email is required')
},
{
name: 'password',
message: 'Password',
type: 'password',
filter: (password) => {
return UsersService
.isValidPassword(password)
.catch((err) => {
throw err.message;
});
}
},
{
name: 'confirmPassword',
message: 'Confirm Password',
type: 'password',
filter: (confirmPassword) => {
return UsersService
.isValidPassword(confirmPassword)
.catch((err) => {
throw err.message;
});
}
},
{
name: 'username',
message: 'Username',
filter: (username) => {
return UsersService
.isValidUsername(username)
.catch((err) => {
throw err.message;
});
}
},
{
name: 'roles',
message: 'User Role',
type: 'checkbox',
choices: UserModel.USER_ROLES
}
]);
}
/**
* Prompts for input and registers a user based on those.
*/
function createUser(options) {
return new Promise((resolve, reject) => {
if (options.flag_mode) {
return resolve({
email: options.email,
password: options.password,
displayName: options.name,
role: options.role
});
}
prompt.start();
prompt.get([
{
name: 'email',
description: 'Email',
format: 'email',
required: true
},
{
name: 'password',
description: 'Password',
hidden: true,
required: true
},
{
name: 'confirmPassword',
description: 'Confirm Password',
hidden: true,
required: true
},
{
name: 'displayName',
description: 'Display Name',
required: true
},
{
name: 'role',
description: 'User Role',
required: false
}
], (err, result) => {
if (err) {
return reject(err);
getUserCreateAnswers(options)
.then((answers) => {
if (answers.password !== answers.confirmPassword) {
return Promise.reject(new Error('Passwords do not match'));
}
if (result.password !== result.confirmPassword) {
return reject(new Error('Passwords do not match'));
}
return answers;
})
.then((answers) => {
return UsersService
.createLocalUser(answers.email.trim(), answers.password.trim(), answers.username.trim())
.then((user) => {
console.log(`Created user ${user.id}.`);
resolve(result);
});
})
.then((result) => {
return User.createLocalUser(result.email.trim(), result.password.trim(), result.displayName.trim())
.then((user) => {
console.log(`Created user ${user.id}.`);
if (result.role && result.role.length > 0) {
return User
.addRoleToUser(user.id, result.role.trim())
.then(() => {
console.log(`Added the admin ${result.role.trim()} to User ${user.id}.`);
util.shutdown();
});
} else {
util.shutdown();
}
if (answers.roles.length > 0) {
return Promise.all(answers.roles.map((role) => {
return UsersService
.addRoleToUser(user.id, role)
.then(() => {
console.log(`Added the role ${role} to User ${user.id}.`);
});
}));
}
});
})
.then(() => {
util.shutdown();
})
.catch((err) => {
console.error(err);
util.shutdown();
});
});
}
/**
* Deletes a user.
*/
function deleteUser(userID) {
User
UserModel
.findOneAndRemove({
id: userID
})
@@ -120,44 +154,34 @@ function deleteUser(userID) {
* Changes the password for a user.
*/
function passwd(userID) {
prompt.start();
prompt.get([
inquirer.prompt([
{
name: 'password',
description: 'Password',
hidden: true,
required: true
message: 'Password',
type: 'password',
validate: validateRequired('Password is required')
},
{
name: 'confirmPassword',
description: 'Confirm Password',
hidden: true,
required: true
message: 'Confirm Password',
type: 'password',
validate: validateRequired('Confirm Password is required')
}
], (err, result) => {
if (err) {
console.error(err);
util.shutdown();
return;
])
.then((answers) => {
if (answers.password !== answers.confirmPassword) {
return Promise.reject(new Error('Password mismatch'));
}
if (result.password !== result.confirmPassword) {
console.error(new Error('Password mismatch'));
util.shutdown(1);
return;
}
User
.changePassword(userID, result.password)
.then(() => {
console.log('Password changed.');
util.shutdown();
})
.catch((err) => {
console.error(err);
util.shutdown(1);
});
return UsersService.changePassword(userID, answers.password);
})
.then(() => {
console.log('Password changed.');
util.shutdown();
})
.catch((err) => {
console.error(err);
util.shutdown(1);
});
}
@@ -168,7 +192,7 @@ function updateUser(userID, options) {
const updates = [];
if (options.email && typeof options.email === 'string' && options.email.length > 0) {
let q = User.update({
let q = UserModel.update({
'id': userID,
'profiles.provider': 'local'
}, {
@@ -181,11 +205,11 @@ function updateUser(userID, options) {
}
if (options.name && typeof options.name === 'string' && options.name.length > 0) {
let q = User.update({
let q = UserModel.update({
'id': userID
}, {
$set: {
displayName: options.name
username: options.name
}
});
@@ -208,13 +232,13 @@ function updateUser(userID, options) {
* Lists all the users registered in the database.
*/
function listUsers() {
User
UsersService
.all()
.then((users) => {
let table = new Table({
head: [
'ID',
'Display Name',
'Username',
'Profiles',
'Roles',
'Status',
@@ -225,7 +249,7 @@ function listUsers() {
users.forEach((user) => {
table.push([
user.id,
user.displayName,
user.username,
user.profiles.map((p) => p.provider).join(', '),
user.roles.join(', '),
user.status,
@@ -248,7 +272,7 @@ function listUsers() {
* @param {String} srcUserID id of the user to which is the source of the merge
*/
function mergeUsers(dstUserID, srcUserID) {
User
UsersService
.mergeUsers(dstUserID, srcUserID)
.then(() => {
console.log(`User ${srcUserID} was merged into user ${dstUserID}.`);
@@ -266,7 +290,14 @@ function mergeUsers(dstUserID, srcUserID) {
* @param {String} role the role to add
*/
function addRole(userID, role) {
User
if (UserModel.USER_ROLES.indexOf(role) === -1) {
console.error(`Role '${role}' is not supported. Supported roles are ${UserModel.USER_ROLES.join(', ')}.`);
util.shutdown(1);
return;
}
UsersService
.addRoleToUser(userID, role)
.then(() => {
console.log(`Added the ${role} role to User ${userID}.`);
@@ -284,7 +315,14 @@ function addRole(userID, role) {
* @param {String} role the role to remove
*/
function removeRole(userID, role) {
User
if (UserModel.USER_ROLES.indexOf(role) === -1) {
console.error(`Role '${role}' is not supported. Supported roles are ${UserModel.USER_ROLES.join(', ')}.`);
util.shutdown(1);
return;
}
UsersService
.removeRoleFromUser(userID, role)
.then(() => {
console.log(`Removed the ${role} role from User ${userID}.`);
@@ -301,8 +339,8 @@ function removeRole(userID, role) {
* @param {String} userID id of the user to ban
*/
function ban(userID) {
User
.setStatus(userID, 'banned', '')
UsersService
.setStatus(userID, 'BANNED')
.then(() => {
console.log(`Banned the User ${userID}.`);
util.shutdown();
@@ -318,8 +356,8 @@ function ban(userID) {
* @param {String} userUD id of the user to remove the role from
*/
function unban(userID) {
User
.setStatus(userID, 'active', '')
UsersService
.setStatus(userID, 'ACTIVE')
.then(() => {
console.log(`Unban the User ${userID}.`);
util.shutdown();
@@ -335,7 +373,7 @@ function unban(userID) {
* @param {String} userID the ID of a user to disable
*/
function disableUser(userID) {
User
UsersService
.disableUser(userID)
.then(() => {
console.log(`User ${userID} was disabled.`);
@@ -352,7 +390,7 @@ function disableUser(userID) {
* @param {String} userID the ID of a user to enable
*/
function enableUser(userID) {
User
UsersService
.enableUser(userID)
.then(() => {
console.log(`User ${userID} was enabled.`);
@@ -368,9 +406,6 @@ function enableUser(userID) {
// Setting up the program command line arguments.
//==============================================================================
program
.version(pkg.version);
program
.command('create')
.option('--email [email]', 'Email to use')
+56
View File
@@ -0,0 +1,56 @@
const pkg = require('../package.json');
const dotenv = require('dotenv');
const fs = require('fs');
const program = require('commander');
// Perform rewrites to the runtime environment variables based on the contents
// of the process.env.REWRITE_ENV if it exists. This is done here as it is the
// entrypoint for the entire application.
require('env-rewrite').rewrite();
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
const parseArgs = require('minimist')(process.argv.slice(2), {
alias: {
'c': 'config'
},
string: [
'config',
'pid'
],
default: {
'config': null,
'pid': null
}
});
/**
* If the config flag is present, then we have to load the configuration from
* the file specified. We will then load those values into the environment.
*/
if (parseArgs.config) {
let envConfig = dotenv.parse(fs.readFileSync(parseArgs.config, {encoding: 'utf8'}));
Object.keys(envConfig).forEach((k) => {
process.env[k] = envConfig[k];
});
}
/**
* If the pid flag is present, then we have to create a pid file at the location
* specified.
*/
if (parseArgs.pid) {
const util = require('./util');
console.log('Wrote PID');
util.pid(parseArgs.pid);
}
module.exports = program
.version(pkg.version)
.option('-c, --config [path]', 'Specify the configuration file to load')
.option('--pid [path]', 'Specify a path to output the current PID to');
+94
View File
@@ -0,0 +1,94 @@
const debug = require('debug')('talk:util');
const fs = require('fs');
const util = module.exports = {};
/**
* Stores an array of functions that should be executed in the event that the
* application needs to shutdown.
* @type {Array}
*/
util.toshutdown = [];
/**
* Calls all the shutdown functions and then ends the process.
* @param {Number} [defaultCode=0] default return code upon sucesfull shutdown.
*/
util.shutdown = (defaultCode = 0, signal = null) => {
if (signal) {
debug(`Reached ${signal} signal`);
}
debug(`${util.toshutdown.length} jobs now being called`);
Promise
.all(util.toshutdown.map((func) => func ? func(signal) : null).filter((func) => func))
.then(() => {
debug('Shutdown complete, now exiting');
process.exit(defaultCode);
})
.catch((err) => {
console.error(err);
process.exit(1);
});
};
/**
* Waits until an event is triggered by the node runtime and elevates a series
* of jobs to be ran in the event we need to shutdown.
* @param {Array} jobs Array of promise capable shutdown functions that are
* executed.
*/
util.onshutdown = (jobs) => {
debug(`${jobs.length} jobs registered to be called during shutdown`);
// Add the new jobs to shutdown to the object reference.
util.toshutdown = util.toshutdown.concat(jobs);
};
/**
* Register a PID file to be maintained for the lifespan of the process.
* @param {String} path path to the PID file to create
*/
util.pid = (path) => {
if (!/\//.test(path)) {
if (!/\.pid/.test(path)) {
path += '.pid';
}
path = `/tmp/${path}`;
}
const pid = `${process.pid.toString()}\n`;
fs.writeFile(path, pid, (err) => {
if (err) {
console.error(`Can't write PID file: ${err}`);
throw err;
}
// Add the cleanup for the fs onto the shutdown.
util.onshutdown([
() => new Promise((resolve, reject) => {
// Remove the pid file.
fs.unlink(path, (err) => {
if (err) {
return reject(err);
}
return resolve();
});
})
]);
});
};
// Attach to the SIGTERM + SIGINT handles to ensure a clean shutdown in the
// event that we have an external event. SIGUSR2 is called when the app is asked
// to be 'killed', same procedure here.
process.on('SIGTERM', () => util.shutdown(0, 'SIGTERM'));
process.on('SIGINT', () => util.shutdown(0, 'SIGINT'));
process.once('SIGUSR2', () => util.shutdown(0, 'SIGUSR2'));
+38 -5
View File
@@ -1,18 +1,51 @@
machine:
node:
version: 7
version: 7.6
services:
- docker
- redis
environment:
PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin"
NODE_ENV: "test"
dependencies:
override:
# TODO: use the following to add in support for MongoDB 3.4.
# # Upgrade the database version to 3.4.
# - sudo apt-get purge mongodb-org*
# - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
# - echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
# - sudo apt-get update
# - sudo apt-get install -y mongodb-org
# - sudo service mongod restart
# Install node dependencies.
- yarn
cache_directories:
- ~/.cache/yarn
post:
# Build the static assets.
- yarn build
# Lint the project here, before tests are ran.
- yarn lint
database:
post:
# Initialize the settings in the database, this will create indicies for the
# database.
- ./bin/cli setup --defaults
- sleep 2
test:
override:
- MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml ./node_modules/.bin/mocha tests --reporter mocha-junit-reporter
- npm run lint
- npm run build
# Run the tests using the junit reporter.
- MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test
# Run the e2e test suite.
# - E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e yarn e2e
deployment:
release:
tag: /[0-9]+(\.[0-9]+)*/
tag: /v[0-9]+(\.[0-9]+)*/
commands:
- bash ./scripts/deploy.sh
+2 -1
View File
@@ -18,6 +18,7 @@
],
"rules": {
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error"
"react/jsx-uses-vars": "error",
"no-console": ["warn", { "allow": ["warn", "error"] }]
}
}
+3 -3
View File
@@ -5,19 +5,19 @@ This app handles moderation for Talk (and maybe more later on)
## Installation
$ npm install
$ yarn install
$ cp config.sample.json config.json
Then change `config.json` to adjust it to your project
## Building for production
$ npm run build
$ yarn build
The public folder has everything you need for deployment. You can just copy that folder to your favorite static web server
## Development
$ npm start
$ yarn start
A development server will be running at http://localhost:4132
+34 -12
View File
@@ -1,21 +1,43 @@
import React from 'react';
import {Router, Route, IndexRoute, browserHistory} from 'react-router';
import {Router, Route, IndexRoute, IndexRedirect, browserHistory} from 'react-router';
import ModerationContainer from 'containers/ModerationQueue/ModerationContainer';
import CommentStream from 'containers/CommentStream/CommentStream';
import Configure from 'containers/Configure/Configure';
import Streams from 'containers/Streams/Streams';
import CommunityContainer from 'containers/Community/CommunityContainer';
import Configure from 'containers/Configure/Configure';
import LayoutContainer from 'containers/LayoutContainer';
import InstallContainer from 'containers/Install/InstallContainer';
import CommunityContainer from 'containers/Community/CommunityContainer';
import ModerationLayout from 'containers/ModerationQueue/ModerationLayout';
import ModerationContainer from 'containers/ModerationQueue/ModerationContainer';
import Dashboard from 'containers/Dashboard/Dashboard';
const routes = (
<Route path='/admin' component={LayoutContainer}>
<IndexRoute component={ModerationContainer} />
<Route path='embed' component={CommentStream} />
<Route path='community' component={CommunityContainer} />
<Route path='configure' component={Configure} />
<Route path='streams' component={Streams} />
</Route>
<div>
<Route exact path="/admin/install" component={InstallContainer}/>
<Route path='/admin' component={LayoutContainer}>
<IndexRoute component={Dashboard} />
<Route path='community' component={CommunityContainer} />
<Route path='configure' component={Configure} />
<Route path='streams' component={Streams} />
<Route path='dashboard' component={Dashboard} />
{/* Moderation Routes */}
<Route path='moderate' component={ModerationLayout}>
<Route path='premod' components={ModerationContainer}>
<Route path=':id' components={ModerationContainer} />
</Route>
<Route path='rejected' components={ModerationContainer}>
<Route path=':id' components={ModerationContainer} />
</Route>
<Route path='flagged' components={ModerationContainer}>
<Route path=':id' components={ModerationContainer} />
</Route>
<Route path=':id' components={ModerationContainer} />
<IndexRedirect to='premod' />
</Route>
</Route>
</div>
);
const AppRouter = () => <Router history={browserHistory} routes={routes} />;
+7 -1
View File
@@ -4,8 +4,10 @@ import {
FETCH_ASSETS_FAILURE,
UPDATE_ASSET_STATE_REQUEST,
UPDATE_ASSET_STATE_SUCCESS,
UPDATE_ASSET_STATE_FAILURE
UPDATE_ASSET_STATE_FAILURE,
UPDATE_ASSETS
} from '../constants/assets';
import coralApi from '../../../coral-framework/helpers/response';
/**
@@ -34,3 +36,7 @@ export const updateAssetState = (id, closedAt) => (dispatch) => {
dispatch({type: UPDATE_ASSET_STATE_SUCCESS}))
.catch(error => dispatch({type: UPDATE_ASSET_STATE_FAILURE, error}));
};
export const updateAssets = assets => dispatch => {
dispatch({type: UPDATE_ASSETS, assets});
};
+32 -5
View File
@@ -1,5 +1,29 @@
import * as actions from '../constants/auth';
import coralApi from '../../../coral-framework/helpers/response';
import coralApi from 'coral-framework/helpers/response';
// Log In.
export const handleLogin = (email, password) => dispatch => {
dispatch({type: actions.LOGIN_REQUEST});
return coralApi('/auth/local', {method: 'POST', body: {email, password}})
.then(result => {
const isAdmin = !!result.user.roles.filter(i => i === 'ADMIN').length;
dispatch(checkLoginSuccess(result.user, isAdmin));
})
.catch(error => {
dispatch({type: actions.LOGIN_FAILURE, message: error.translation_key});
});
};
const forgotPassowordRequest = () => ({type: actions.FETCH_FORGOT_PASSWORD_REQUEST});
const forgotPassowordSuccess = () => ({type: actions.FETCH_FORGOT_PASSWORD_SUCCESS});
const forgotPassowordFailure = () => ({type: actions.FETCH_FORGOT_PASSWORD_FAILURE});
export const requestPasswordReset = email => dispatch => {
dispatch(forgotPassowordRequest(email));
return coralApi('/account/password/reset', {method: 'POST', body: {email}})
.then(() => dispatch(forgotPassowordSuccess()))
.catch(error => dispatch(forgotPassowordFailure(error)));
};
// Check Login
@@ -9,12 +33,15 @@ const checkLoginFailure = error => ({type: actions.CHECK_LOGIN_FAILURE, error});
export const checkLogin = () => dispatch => {
dispatch(checkLoginRequest());
coralApi('/auth')
return coralApi('/auth')
.then(result => {
const isAdmin = !!result.user.roles.filter(i => i === 'admin').length;
const isAdmin = !!result.user.roles.filter(i => i === 'ADMIN').length;
dispatch(checkLoginSuccess(result.user, isAdmin));
})
.catch(error => dispatch(checkLoginFailure(error)));
.catch(error => {
console.error(error);
dispatch(checkLoginFailure(`${error.translation_key}`));
});
};
// LogOut Actions
@@ -25,7 +52,7 @@ const logOutFailure = () => ({type: actions.LOGOUT_FAILURE});
export const logout = () => dispatch => {
dispatch(logOutRequest());
coralApi('/auth', {method: 'DELETE'})
return coralApi('/auth', {method: 'DELETE'})
.then(() => dispatch(logOutSuccess()))
.catch(error => dispatch(logOutFailure(error)));
};
-102
View File
@@ -1,102 +0,0 @@
import coralApi from '../../../coral-framework/helpers/response';
import * as commentTypes from '../constants/comments';
import * as actionTypes from '../constants/actions';
function addUsersCommentsActions (dispatch, {comments, users, actions}) {
dispatch({type: commentTypes.USERS_MODERATION_QUEUE_FETCH_SUCCESS, users});
dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS, comments});
dispatch({type: actionTypes.ACTIONS_MODERATION_QUEUE_FETCH_SUCCESS, actions});
}
// Get comments to fill each of the three lists on the mod queue
export const fetchModerationQueueComments = () => {
return dispatch => {
dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
return Promise.all([
coralApi('/queue/comments/pending'),
coralApi('/queue/comments/rejected'),
coralApi('/queue/comments/flagged')
])
.then(([pending, rejected, flagged]) => {
/* Combine seperate calls into a single object */
flagged.comments.forEach(comment => comment.flagged = true);
return {
comments: [...pending.comments, ...rejected.comments, ...flagged.comments],
users: [...pending.users, ...rejected.users, ...flagged.users],
actions: [...pending.actions, ...rejected.actions, ...flagged.actions]
};
})
.then(addUsersCommentsActions.bind(this, dispatch));
};
};
export const fetchPendingQueue = () => {
return dispatch => {
dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
return coralApi('/queue/comments/pending')
.then(addUsersCommentsActions.bind(this, dispatch));
};
};
export const fetchRejectedQueue = () => {
return dispatch => {
dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
return coralApi('/queue/comments/rejected')
.then(addUsersCommentsActions.bind(this, dispatch));
};
};
export const fetchFlaggedQueue = () => {
return dispatch => {
dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
return coralApi('/queue/comments/flagged')
.then(results => {
results.comments.forEach(comment => comment.flagged = true);
return results;
})
.then(addUsersCommentsActions.bind(this, dispatch));
};
};
// Create a new comment
export const createComment = (name, body) => {
return (dispatch) => {
const formData = {body, name};
return coralApi('/comments', {method: 'POST', body: formData})
.then(res => dispatch({type: commentTypes.COMMENT_CREATE_SUCCESS, comment: res}))
.catch(error => dispatch({type: commentTypes.COMMENT_CREATE_FAILED, error}));
};
};
/**
* Action disptacher related to comments
*/
// Update a comment. Now to update a comment we need to send back the whole object
export const updateStatus = (status, comment) => {
return dispatch => {
dispatch({type: commentTypes.COMMENT_STATUS_UPDATE_REQUEST, id: comment.id, status});
return coralApi(`/comments/${comment.id}/status`, {method: 'PUT', body: {status}})
.then(res => dispatch({type: commentTypes.COMMENT_STATUS_UPDATE_SUCCESS, res}))
.catch(error => dispatch({type: commentTypes.COMMENT_STATUS_UPDATE_FAILURE, error}));
};
};
export const flagComment = id => (dispatch, getState) => {
dispatch({type: commentTypes.COMMENT_FLAG, id});
dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)});
};
// Dialog Actions
export const showBanUserDialog = (userId, userName, commentId) => {
return {type: commentTypes.SHOW_BANUSER_DIALOG, userId, userName, commentId};
};
export const hideBanUserDialog = (showDialog) => {
return {type: commentTypes.HIDE_BANUSER_DIALOG, showDialog};
};
+118
View File
@@ -0,0 +1,118 @@
import coralApi from 'coral-framework/helpers/response';
import * as actions from '../constants/install';
import validate from 'coral-framework/helpers/validate';
import errorMsj from 'coral-framework/helpers/error';
export const nextStep = () => ({type: actions.NEXT_STEP});
export const previousStep = () => ({type: actions.PREVIOUS_STEP});
export const goToStep = step => ({type: actions.GO_TO_STEP, step});
const installRequest = () => ({type: actions.INSTALL_REQUEST});
const installSuccess = () => ({type: actions.INSTALL_SUCCESS});
const installFailure = error => ({type: actions.INSTALL_FAILURE, error});
const addError = (name, error) => ({type: actions.ADD_ERROR, name, error});
const hasError = error => ({type: actions.HAS_ERROR, error});
const clearErrors = () => ({type: actions.CLEAR_ERRORS});
const validation = (formData, dispatch, next) => {
if (!(formData != null)) {
return dispatch(hasError());
}
const validKeys = Object.keys(formData)
.filter(name => name !== 'domains');
// Required Validation
const empty = validKeys
.filter(name => {
const cond = !formData[name].length;
if (cond) {
// Adding Error
dispatch(addError(name, 'This field is required.'));
} else {
dispatch(addError(name, ''));
}
return cond;
});
if (empty.length) {
return dispatch(hasError());
}
// RegExp Validation
const validation = validKeys
.filter(name => {
const cond = !validate[name](formData[name]);
if (cond) {
// Adding Error
dispatch(addError(name, errorMsj[name]));
} else {
dispatch(addError(name, ''));
}
return cond;
});
if (validation.length) {
return dispatch(hasError());
}
dispatch(clearErrors());
next();
};
export const submitSettings = () => (dispatch, getState) => {
const settingsFormData = getState().install.toJS().data.settings;
validation(settingsFormData, dispatch, function() {
dispatch(nextStep());
});
};
export const submitUser = () => (dispatch, getState) => {
const userFormData = getState().install.toJS().data.user;
validation(userFormData, dispatch, function() {
dispatch(nextStep());
});
};
export const finishInstall = () => (dispatch, getState) => {
const data = getState().install.toJS().data;
dispatch(installRequest());
return coralApi('/setup', {method: 'POST', body: data})
.then(() => {
dispatch(installSuccess());
dispatch(nextStep());
})
.catch(error => {
console.error(error);
dispatch(installFailure(`${error.translation_key}`));
});
};
export const updateSettingsFormData = (name, value) => ({type: actions.UPDATE_FORMDATA_SETTINGS, name, value});
export const updateUserFormData = (name, value) => ({type: actions.UPDATE_FORMDATA_USER, name, value});
export const updatePermittedDomains = (value) => ({type: actions.UPDATE_PERMITTED_DOMAINS_SETTINGS, value});
const checkInstallRequest = () => ({type: actions.CHECK_INSTALL_REQUEST});
const checkInstallSuccess = installed => ({type: actions.CHECK_INSTALL_SUCCESS, installed});
const checkInstallFailure = error => ({type: actions.CHECK_INSTALL_FAILURE, error});
export const checkInstall = next => dispatch => {
dispatch(checkInstallRequest());
coralApi('/setup')
.then(({installed}) => {
dispatch(checkInstallSuccess(installed));
if (installed) {
next();
}
})
.catch(error => {
console.error(error);
dispatch(checkInstallFailure(`${error.translation_key}`));
});
};
@@ -0,0 +1,8 @@
import * as actions from 'constants/moderation';
export const toggleModal = open => ({type: actions.TOGGLE_MODAL, open});
export const singleView = () => ({type: actions.SINGLE_VIEW});
// Ban User Dialog
export const showBanUserDialog = (user, commentId) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId});
export const hideBanUserDialog = (showDialog) => ({type: actions.HIDE_BANUSER_DIALOG, showDialog});
+6 -1
View File
@@ -11,6 +11,7 @@ export const SAVE_SETTINGS_SUCCESS = 'SAVE_SETTINGS_SUCCESS';
export const SAVE_SETTINGS_FAILED = 'SAVE_SETTINGS_FAILED';
export const WORDLIST_UPDATED = 'WORDLIST_UPDATED';
export const DOMAINLIST_UPDATED = 'DOMAINLIST_UPDATED';
export const fetchSettings = () => dispatch => {
dispatch({type: SETTINGS_LOADING});
@@ -33,8 +34,12 @@ export const updateWordlist = (listName, list) => {
return {type: WORDLIST_UPDATED, listName, list};
};
export const updateDomainlist = (listName, list) => {
return {type: DOMAINLIST_UPDATED, listName, list};
};
export const saveSettingsToServer = () => (dispatch, getState) => {
let settings = getState().settings.toJS().settings;
let settings = getState().settings.toJS();
if (settings.charCount) {
settings.charCount = parseInt(settings.charCount);
}
+20 -4
View File
@@ -1,5 +1,5 @@
import coralApi from '../../../coral-framework/helpers/response';
import * as actions from '../constants/user';
import * as userTypes from '../constants/users';
/**
* Action disptacher related to users
@@ -7,9 +7,25 @@ import * as actions from '../constants/user';
// change status of a user
export const userStatusUpdate = (status, userId, commentId) => {
return (dispatch) => {
dispatch({type: actions.UPDATE_STATUS_REQUEST});
dispatch({type: userTypes.UPDATE_STATUS_REQUEST});
return coralApi(`/users/${userId}/status`, {method: 'POST', body: {status: status, comment_id: commentId}})
.then(res => dispatch({type: actions.UPDATE_STATUS_SUCCESS, res}))
.catch(error => dispatch({type: actions.UPDATE_STATUS_FAILURE, error}));
.then(res => dispatch({type: userTypes.UPDATE_STATUS_SUCCESS, res}))
.catch(error => dispatch({type: userTypes.UPDATE_STATUS_FAILURE, error}));
};
};
// change status of a user
export const sendNotificationEmail = (userId, subject, body) => {
return (dispatch) => {
return coralApi(`/users/${userId}/email`, {method: 'POST', body: {subject, body}})
.catch(error => dispatch({type: userTypes.USER_EMAIL_FAILURE, error}));
};
};
// let a user edit their username
export const enableUsernameEdit = (userId) => {
return (dispatch) => {
return coralApi(`/users/${userId}/username-enable`, {method: 'POST'})
.catch(error => dispatch({type: userTypes.USERNAME_ENABLE_FAILURE, error}));
};
};
@@ -0,0 +1,17 @@
import React from 'react';
import styles from './ModerationList.css';
import {Button} from 'coral-ui';
import {menuActionsMap} from '../containers/ModerationQueue/helpers/moderationQueueActionsMap';
const ActionButton = ({type = '', ...props}) => {
return (
<Button
className={`${type.toLowerCase()} ${styles.actionButton}`}
cStyle={type.toLowerCase()}
icon={menuActionsMap[type].icon}
onClick={type === 'APPROVE' ? props.acceptComment : props.rejectComment}
>{menuActionsMap[type].text}</Button>
);
};
export default ActionButton;
@@ -0,0 +1,92 @@
import React, {PropTypes} from 'react';
import Layout from 'coral-admin/src/components/ui/Layout';
import styles from './NotFound.css';
import {Button, TextField, Alert, Success} from 'coral-ui';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
class AdminLogin extends React.Component {
constructor (props) {
super(props);
this.state = {email: '', password: '', requestPassword: false};
}
handleSignIn = e => {
e.preventDefault();
this.props.handleLogin(this.state.email, this.state.password);
}
handleRequestPassword = e => {
e.preventDefault();
this.props.requestPasswordReset(this.state.email);
}
render () {
const {errorMessage} = this.props;
const signInForm = (
<form onSubmit={this.handleSignIn}>
{errorMessage && <Alert>{lang.t(`errors.${errorMessage}`)}</Alert>}
<TextField
label='Email Address'
value={this.state.email}
onChange={e => this.setState({email: e.target.value})} />
<TextField
label='Password'
value={this.state.password}
onChange={e => this.setState({password: e.target.value})}
type='password' />
<div style={{height: 10}}></div>
<Button
type='submit'
cStyle='black'
full
onClick={this.handleSignIn}>Sign In</Button>
<p className={styles.forgotPasswordCTA}>
Forgot your password? <a href="#" className={styles.forgotPasswordLink} onClick={e => {
e.preventDefault();
this.setState({requestPassword: true});
}}>Request a new one.</a>
</p>
</form>
);
const requestPasswordForm = (
this.props.passwordRequestSuccess
? <p className={styles.passwordRequestSuccess} onClick={() => {
location.href = location.href;
}}>
{this.props.passwordRequestSuccess} <a className={styles.signInLink} href="#">Sign in</a>
<Success />
</p>
: <form onSubmit={this.handleRequestPassword}>
<TextField
label='Email Address'
value={this.state.email}
onChange={e => this.setState({email: e.target.value})} />
<Button
type='submit'
cStyle='black'
full
onClick={this.handleRequestPassword}>Reset Password</Button>
</form>
);
return (
<Layout fixedDrawer restricted={true}>
<div className={styles.loginLayout}>
<h1 className={styles.loginHeader}>Team sign in</h1>
<p className={styles.loginCTA}>Sign in to interact with your community.</p>
{ this.state.requestPassword ? requestPasswordForm : signInForm }
</div>
</Layout>
);
}
}
AdminLogin.propTypes = {
handleLogin: PropTypes.func.isRequired,
passwordRequestSuccess: PropTypes.string,
loginError: PropTypes.string
};
export default AdminLogin;
@@ -0,0 +1,11 @@
.banButton {
-webkit-transform: scale(.8);
transform: scale(.8);
margin: 0;
i {
vertical-align: middle;
margin-right: 10px;
font-size: 14px;
}
}
@@ -0,0 +1,26 @@
import React, {PropTypes} from 'react';
import {Button, Icon} from 'coral-ui';
import styles from './BanUserButton.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations.json';
const lang = new I18n(translations);
const BanUserButton = ({user, ...props}) => (
<div className={styles.ban}>
<Button cStyle='ban'
className={`ban ${styles.banButton}`}
disabled={user.status === 'BANNED' ? 'disabled' : ''}
onClick={props.onClick}
raised>
<Icon name='not_interested' />
{lang.t('comment.ban_user')}
</Button>
</div>
);
BanUserButton.propTypes = {
onClick: PropTypes.func.isRequired
};
export default BanUserButton;
@@ -22,17 +22,17 @@
}
}
.formField {
.textField {
margin-top: 15px;
}
.formField label {
.textField label {
font-size: 1.08em;
font-weight: bold;
margin-bottom: 5px;
}
.formField input {
.textField input {
width: 100%;
display: block;
border: none;
@@ -1,4 +1,4 @@
import React from 'react';
import React, {PropTypes} from 'react';
import {Dialog} from 'coral-ui';
import styles from './BanUserDialog.css';
@@ -8,34 +8,28 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
const BanUserDialog = ({open, handleClose, onClickBanUser, user = {}}) => (
const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
<Dialog
className={styles.dialog}
id="banuserDialog"
open={open}
onClose={() => handleClose()}
onCancel={() => handleClose()}
onClose={handleClose}
onCancel={handleClose}
title={lang.t('bandialog.ban_user')}>
<span className={styles.close} onClick={handleClose}>×</span>
<div>
<div className={styles.header}>
<h2>
{lang.t('bandialog.ban_user')}
</h2>
<h2>{lang.t('bandialog.ban_user')}</h2>
</div>
<div className={styles.separator}>
<h3>
{lang.t('bandialog.are_you_sure', user.userName)}
</h3>
<i>
{lang.t('bandialog.note')}
</i>
<h3>{lang.t('bandialog.are_you_sure', user.name)}</h3>
<i>{lang.t('bandialog.note')}</i>
</div>
<div className={styles.buttons}>
<Button cStyle="cancel" className={styles.cancel} onClick={() => handleClose()} raised>
<Button cStyle="cancel" className={styles.cancel} onClick={handleClose} raised>
{lang.t('bandialog.cancel')}
</Button>
<Button cStyle="black" className={styles.ban} onClick={() => onClickBanUser(user.userId, user.commentId)} raised>
<Button cStyle="black" className={styles.ban} onClick={() => handleBanUser({userId: user.id})} raised>
{lang.t('bandialog.yes_ban_user')}
</Button>
</div>
@@ -43,4 +37,10 @@ const BanUserDialog = ({open, handleClose, onClickBanUser, user = {}}) => (
</Dialog>
);
BanUserDialog.propTypes = {
handleBanUser: PropTypes.func.isRequired,
handleClose: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
};
export default BanUserDialog;
+18 -42
View File
@@ -2,18 +2,19 @@ import React from 'react';
import timeago from 'timeago.js';
import Linkify from 'react-linkify';
import styles from './CommentList.css';
import styles from './ModerationList.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations.json';
import Highlighter from 'react-highlight-words';
import {FabButton, Button, Icon} from 'coral-ui';
import {Icon} from 'coral-ui';
import ActionButton from './ActionButton';
const linkify = new Linkify();
// Render a single comment for the list
export default props => {
const Comment = props => {
const {comment, author} = props;
let authorStatus = author.status;
const links = linkify.getMatches(comment.body);
@@ -22,7 +23,7 @@ export default props => {
<li tabIndex={props.index} className={`mdl-card mdl-shadow--2dp ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
<div className={styles.itemHeader}>
<div className={styles.author}>
<span>{author.displayName || lang.t('comment.anon')}</span>
<span>{author.username || lang.t('comment.anon')}</span>
<span className={styles.created}>{timeago().format(comment.createdAt || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}</span>
{comment.flagged ? <p className={styles.flagged}>{lang.t('comment.flagged')}</p> : null}
</div>
@@ -30,7 +31,18 @@ export default props => {
{links ?
<span className={styles.hasLinks}><Icon name='error_outline'/> Contains Link</span> : null}
<div className={`actions ${styles.actions}`}>
{props.modActions.map((action, i) => getActionButton(action, i, props))}
{props.modActions.map(
(action, i) =>
<ActionButton
option={action}
key={i}
type='COMMENTS'
comment={comment}
user={author}
menuOptionsMap={props.menuOptionsMap}
onClickAction={props.onClickAction}
onClickShowBanDialog={props.onClickShowBanDialog}/>
)}
</div>
{authorStatus === 'banned' ?
<span className={styles.banned}><Icon name='error_outline'/> {lang.t('comment.banned_user')}</span> : null}
@@ -49,43 +61,7 @@ export default props => {
);
};
// Get the button of the action performed over a comment if any
const getActionButton = (action, i, props) => {
const {comment, author} = props;
const status = comment.status;
const flagged = comment.flagged;
const banned = (author.status === 'banned');
if (action === 'flag' && (status || flagged === true)) {
return null;
}
if (action === 'ban') {
return (
<div className={styles.ban}>
<Button
className={`ban ${styles.banButton}`}
cStyle='darkGrey'
disabled={banned ? 'disabled' : ''}
onClick={() => props.onClickShowBanDialog(author.id, author.displayName, comment.id)}
key={i}
raised
>
<Icon name='not_interested' className={styles.banIcon} />
{lang.t('comment.ban_user')}
</Button>
</div>
);
}
return (
<FabButton
className={`${action} ${styles.actionButton}`}
cStyle={action}
icon={props.actionsMap[action].icon}
key={i}
onClick={() => props.onClickAction(props.actionsMap[action].status, comment)}
/>
);
};
export default Comment;
const linkStyles = {
backgroundColor: 'rgb(255, 219, 135)',
@@ -1,166 +0,0 @@
import React, {PropTypes} from 'react';
import styles from './CommentList.css';
import key from 'keymaster';
import Hammer from 'hammerjs';
import Comment from 'components/Comment';
// Each action has different meaning and configuration
const modActions = {
'reject': {status: 'rejected', icon: 'close', key: 'r'},
'approve': {status: 'accepted', icon: 'done', key: 't'},
'flag': {status: 'flagged', icon: 'flag', filter: 'Untouched'},
'ban': {status: 'banned', icon: 'not interested'}
};
// Renders a comment list and allow performing actions
export default class CommentList extends React.Component {
static propTypes = {
isActive: PropTypes.bool,
singleView: PropTypes.bool,
commentIds: PropTypes.arrayOf(PropTypes.string).isRequired,
comments: PropTypes.object.isRequired,
users: PropTypes.object.isRequired,
onClickAction: PropTypes.func,
// list of actions (flags, etc) associated with the comments
modActions: PropTypes.arrayOf(PropTypes.string).isRequired,
loading: PropTypes.bool,
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired
}
constructor (props) {
super(props);
this.state = {active: null};
this.onClickAction = this.onClickAction.bind(this);
this.onClickShowBanDialog = this.onClickShowBanDialog.bind(this);
}
// remove key handlers before leaving
componentWillUnmount () {
this.unbindKeyHandlers();
}
// add key handlers and gestures
componentDidMount () {
this.bindKeyHandlers();
// this.bindGestures() // need to check whether we're on a mobile device or this throws an Error
}
// If entering to singleview and no active, active is the first eleement
componentWillReceiveProps (nextProps) {
if (nextProps.singleView && !this.state.active) {
this.setState({active: nextProps.commentIds[0]});
}
}
// Add swipe to approve or reject
bindGestures () {
const {modActions} = this.props;
this._hammer = new Hammer(this.base);
this._hammer.get('swipe').set({direction: Hammer.DIRECTION_HORIZONTAL});
if (modActions.indexOf('reject') !== -1) {
this._hammer.on('swipeleft', () => this.props.singleView && this.actionKeyHandler('Rejected'));
}
if (modActions.indexOf('approve') !== -1) {
this._hammer.on('swiperight', () => this.props.singleView && this.actionKeyHandler('Approved'));
}
}
// Add key handlers. Each action has one and added j/k for moving around
bindKeyHandlers () {
this.props.modActions.filter(action => modActions[action].key).forEach(action => {
key(modActions[action].key, 'commentList', () => this.props.isActive && this.actionKeyHandler(modActions[action].status));
});
key('j', 'commentList', () => this.props.isActive && this.moveKeyHandler('down'));
key('k', 'commentList', () => this.props.isActive && this.moveKeyHandler('up'));
key.setScope('commentList');
}
// Perform an action using the keys only if the comment is active
actionKeyHandler (action) {
if (this.props.isActive && this.state.active) {
this.onClickAction(action, this.state.active);
}
}
// move around with j/k
moveKeyHandler (direction) {
if (!this.props.isActive) {
return;
}
const {commentIds} = this.props;
const {active} = this.state;
// check boundaries
if (active === null || !commentIds.length) {
this.setState({active: commentIds[0]});
} else if (direction === 'up' && active !== commentIds[0]) {
this.setState({active: commentIds[commentIds.indexOf(active) - 1]});
} else if (direction === 'down' && active !== commentIds[commentIds.length - 1]) {
this.setState({active: commentIds[commentIds.indexOf(active) + 1]});
}
// scroll to the position
const index = Math.max(commentIds.indexOf(this.state.active), 0);
this.base.childNodes[index] && this.base.childNodes[index].focus();
}
unbindKeyHandlers () {
key.deleteScope('commentList');
}
// If we are performing an action over a comment (aka removing from the list) we need to select a new active.
// TODO: In the future this can be improved and look at the actual state to
// resolve since the content of the list could change externally. For now it works as expected
onClickAction (action, id, author_id) {
// activate the next comment
if (id === this.state.active) {
const {commentIds} = this.props;
if (commentIds[commentIds.length - 1] === this.state.active) {
this.setState({active: commentIds[commentIds.length - 2]});
} else {
this.setState({active: commentIds[Math.min(commentIds.indexOf(this.state.active) + 1, commentIds.length - 1)]});
}
}
this.props.onClickAction(action, id, author_id);
}
onClickShowBanDialog(userId, userName, commentId) {
this.props.onClickShowBanDialog(userId, userName, commentId);
}
render () {
const {singleView, commentIds, comments, users, hideActive, key, suspectWords} = this.props;
const {active} = this.state;
return (
<ul
className={`${styles.list} ${singleView ? styles.singleView : ''}`} {...key}
id='commentList'
>
{commentIds.map((commentId, index) => {
const comment = comments[commentId];
const author = users[comment.author_id];
return <Comment
suspectWords={suspectWords}
comment={comment}
author={author}
key={index}
index={index}
onClickAction={this.onClickAction}
onClickShowBanDialog={this.onClickShowBanDialog}
modActions={this.props.modActions}
actionsMap={modActions}
isActive={commentId === active}
hideActive={hideActive} />;
})}
</ul>
);
}
}
@@ -0,0 +1,14 @@
import React, {PropTypes} from 'react';
import {Card} from 'coral-ui';
const EmptyCard = props => (
<Card style={{textAlign: 'center', maxWidth: 400, margin: '0 auto'}}>
{props.children}
</Card>
);
EmptyCard.propTypes = {
children: PropTypes.node.isRequired
};
export default EmptyCard;
@@ -0,0 +1,34 @@
.heading {
margin: 0;
font-size: 1.5rem;
font-weight: bold;
}
.widgetTable {
width: 100%;
border-collapse: collapse;
box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.2);
}
.widgetTable thead th {
border-bottom: 1px solid #f47e6b;
padding: 10px;
text-align: left;
}
.widgetTable tbody tr {
border-bottom: 1px solid lightgrey;
}
.widgetTable tbody tr:last-child {
border-bottom: none;
}
.widgetTable tbody td {
padding: 10px;
}
.lede {
font-size: 0.9em;
color: grey;
}
@@ -19,7 +19,6 @@
.inner {
width: 100vw;
height: 100vh;
background-color: #fff;
box-shadow: 2px 2px 5px 0px rgba(153,153,153,1);
padding: 20px;
@@ -37,7 +36,6 @@
@media (--big-viewport) {
.inner {
width: 600px;
height: 50vh;
border-radius: 4px;
}
}
@@ -69,6 +69,7 @@
justify-content: space-between;
.author {
min-width: 230px;
display: flex;
align-items: center;
}
@@ -95,11 +96,6 @@
margin-left: 40px;
}
.actionButton {
transform: scale(.8);
margin: 0;
}
.body {
margin-top: 20px;
flex: 1;
@@ -112,6 +108,12 @@
padding-top: 15px;
padding-left: 10px;
}
.flagCount{
font-size: 12px;
color: #d32f2f;
}
}
.empty {
@@ -175,3 +177,9 @@
font-size: 14px;
}
}
.actionButton {
transform: scale(.8);
margin: 0;
}
@@ -0,0 +1,227 @@
import React, {PropTypes} from 'react';
import styles from './ModerationList.css';
import key from 'keymaster';
import Hammer from 'hammerjs';
import Comment from './Comment';
import User from './User';
import SuspendUserModal from './SuspendUserModal';
// Each action has different meaning and configuration
const menuOptionsMap = {
'reject': {status: 'REJECTED', icon: 'close', key: 'r'},
'approve': {status: 'ACCEPTED', icon: 'done', key: 't'},
'flag': {status: 'FLAGGED', icon: 'flag', filter: 'Untouched'},
'ban': {status: 'BANNED', icon: 'not interested'}
};
// Renders a comment list and allow performing actions
export default class ModerationList extends React.Component {
static propTypes = {
isActive: PropTypes.bool,
singleView: PropTypes.bool,
commentIds: PropTypes.arrayOf(PropTypes.string),
actionIds: PropTypes.arrayOf(PropTypes.string),
comments: PropTypes.object,
users: PropTypes.object.isRequired,
actions: PropTypes.object,
userStatusUpdate: PropTypes.func.isRequired,
suspendUser: PropTypes.func.isRequired,
// list of actions (flags, etc) associated with the comments
modActions: PropTypes.arrayOf(PropTypes.string).isRequired,
loading: PropTypes.bool,
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired
}
state = {active: null, suspendUserModal: null, email: null};
// remove key handlers before leaving
componentWillUnmount () {
this.unbindKeyHandlers();
}
// add key handlers and gestures
componentDidMount () {
this.bindKeyHandlers();
// this.bindGestures() // need to check whether we're on a mobile device or this throws an Error
}
// If entering to singleview and no active, active is the first eleement
componentWillReceiveProps (nextProps) {
if (nextProps.singleView && !this.state.active) {
this.setState({active: nextProps.commentIds[0]});
}
}
// Add swipe to approve or reject
bindGestures () {
const {modActions} = this.props;
this._hammer = new Hammer(this.base);
this._hammer.get('swipe').set({direction: Hammer.DIRECTION_HORIZONTAL});
if (modActions.indexOf('reject') !== -1) {
this._hammer.on('swipeleft', () => this.props.singleView && this.actionKeyHandler('Rejected'));
}
if (modActions.indexOf('approve') !== -1) {
this._hammer.on('swiperight', () => this.props.singleView && this.actionKeyHandler('Approved'));
}
}
// Add key handlers. Each action has one and added j/k for moving around
bindKeyHandlers () {
const {modActions, isActive} = this.props;
modActions.filter(action => menuOptionsMap[action].key).forEach(action => {
key(menuOptionsMap[action].key, 'moderationList', () => isActive && this.actionKeyHandler(menuOptionsMap[action].status));
});
key('j', 'moderationList', () => isActive && this.moveKeyHandler('down'));
key('k', 'moderationList', () => isActive && this.moveKeyHandler('up'));
key.setScope('moderationList');
}
// Perform an action using the keys only if the comment is active
actionKeyHandler (action) {
if (this.props.isActive && this.state.active) {
this.onClickAction(action, this.state.active);
}
}
// move around with j/k
moveKeyHandler (direction) {
if (!this.props.isActive) {
return;
}
const {commentIds} = this.props;
const {active} = this.state;
// check boundaries
if (active === null || !commentIds.length) {
this.setState({active: commentIds[0]});
} else if (direction === 'up' && active !== commentIds[0]) {
this.setState({active: commentIds[commentIds.indexOf(active) - 1]});
} else if (direction === 'down' && active !== commentIds[commentIds.length - 1]) {
this.setState({active: commentIds[commentIds.indexOf(active) + 1]});
}
// scroll to the position
const index = Math.max(commentIds.indexOf(this.state.active), 0);
this.base.childNodes[index] && this.base.childNodes[index].focus();
}
unbindKeyHandlers () {
key.deleteScope('moderationList');
}
// If we are performing an action over a comment (aka removing from the list) we need to select a new active.
// TODO: In the future this can be improved and look at the actual state to
// resolve since the content of the list could change externally. For now it works as expected
onClickAction = (menuOption, id, action) => {
// activate the next comment
if (id === this.state.active) {
const moderationIds = this.getModerationIds();
if (moderationIds[moderationIds.length - 1] === this.state.active) {
this.setState({active: moderationIds[moderationIds.length - 2]});
} else {
this.setState({active: moderationIds[Math.min(moderationIds.indexOf(this.state.active) + 1, moderationIds.length - 1)]});
}
}
// Update the status right away if this is a comment
if (action.item_type === 'COMMENTS') {
this.props.updateCommentStatus(menuOption, id);
} else if (action.item_type === 'USERS') {
// If a user bio or name is rejected, bring up a dialog before suspending them.
if (menuOption === 'REJECTED') {
this.setState({suspendUserModal: action});
} else if (menuOption === 'ACCEPTED') {
this.props.userStatusUpdate('APPROVED', action.item_id);
}
}
}
onClickShowBanDialog = (userId, userName, commentId) => {
this.props.onClickShowBanDialog(userId, userName, commentId);
}
mapModItems = (itemId, index) => {
const {comments = {}, users, actions = {}, modActions, suspectWords, hideActive} = this.props;
const {active} = this.state;
// Because ids are unique, the id will either appear as an action or as a comment.
const item = comments[itemId] || actions[itemId];
let modItem;
if (item.body) {
// If the item is a comment...
const author = users[item.author_id];
modItem = <Comment
suspectWords={suspectWords}
comment={item}
author={author}
key={index}
index={index}
onClickAction={this.onClickAction}
onClickShowBanDialog={this.onClickShowBanDialog}
modActions={modActions}
menuOptionsMap={menuOptionsMap}
isActive={itemId === active}
hideActive={hideActive} />;
} else {
// If the item is an action...
const user = users[item.item_id];
modItem = user && <User
suspectWords={suspectWords}
action={item}
user={user}
key={index}
index={index}
onClickAction={this.onClickAction}
onClickShowBanDialog={this.onClickShowBanDialog}
modActions={modActions}
menuOptionsMap={menuOptionsMap}
isActive={itemId === active}
hideActive={hideActive} />;
}
return modItem;
}
getModerationIds = () => {
const {commentIds = [], actionIds = [], comments, actions} = this.props;
if (comments && actions) {
return [ ...commentIds, ...actionIds ].sort((a, b) => {
const itemA = comments[a] || actions[a];
const itemB = comments[b] || actions[b];
return itemB.updated_at - itemA.updated_at;
});
} else {
return comments ? commentIds : actionIds;
}
}
render () {
const {singleView, key, suspendUser} = this.props;
// Combine moderations and actions into a single stream and sort by most recently updated.
const moderationIds = this.getModerationIds();
return (
<ul
className={`${styles.list} ${singleView ? styles.singleView : ''}`} {...key}
id='moderationList'>
{moderationIds.map(this.mapModItems)}
<SuspendUserModal
action = {this.state.suspendUserModal}
onClose={() => this.setState({suspendUserModal:null})}
suspendUser={suspendUser} />
</ul>
);
}
}
+30 -5
View File
@@ -1,12 +1,37 @@
.layout {
max-width: 800px;
margin: 0 auto;
max-width: 800px;
margin: 0 auto;
}
.loginLayout {
max-width: 400px;
margin: 0 auto;
}
.loginHeader, .loginCTA, .forgotPasswordCTA, .passwordRequestSuccess {
text-align: center;
font-size: 16px;
}
.forgotPasswordLink, .signInLink {
color: blue;
font-weight: normal;
text-decoration: none;
}
.forgotPasswordLink:hover, .signInLink:hover {
text-decoration: underline;
}
.layout h1 {
font-size: 40px;
font-size: 40px;
}
.layout img {
width: 100%;
.loginHeader {
font-size: 30px;
}
.passwordRequestSuccess {
cursor: pointer;
padding: 8px 14px;
}
@@ -1,13 +0,0 @@
import React from 'react';
import {Layout} from 'react-mdl';
import styles from './NotFound.css';
export const PermissionRequired = () => (
<Layout fixedDrawer>
<div className={styles.layout} >
<h1>Permission Required</h1>
<p>Were sorry, but you dont have access to that page.</p>
<img src="https://coralproject.net/images/communicorn.jpg" alt="Communicorn"/>
</div>
</Layout>
);
@@ -0,0 +1,31 @@
.modalButtons {
display: flex;
justify-content: flex-end;
margin-top: 50px;
}
.emailInput {
border-radius: 3px;
width: 100%;
padding: 10px;
font-size: 14px;
}
.writeContainer {
margin-top: 20px;
}
.emailContainer {
display: flex;
}
.emailMessage {
font-weight: 800;
}
.title {
font-size: 20px;
font-weight: 800;
margin-bottom: 20px;
}
@@ -0,0 +1,106 @@
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations.json';
import React, {Component, PropTypes} from 'react';
import Modal from 'components/Modal';
import styles from './SuspendUserModal.css';
import {Button} from 'coral-ui';
const stages = [
{
title: 'suspenduser.title_0',
description: 'suspenduser.description_0',
options: {
'j': 'suspenduser.no_cancel',
'k': 'suspenduser.yes_suspend'
}
},
{
title: 'suspenduser.title_1',
description: 'suspenduser.description_1',
options: {
'j': 'bandialog.cancel',
'k': 'suspenduser.send'
}
}
];
class SuspendUserModal extends Component {
state = {email: '', stage: 0}
static propTypes = {
stage: PropTypes.number,
actionType: PropTypes.string,
onClose: PropTypes.func.isRequired,
suspendUser: PropTypes.func.isRequired
}
componentDidMount() {
const about = lang.t('suspenduser.username');
this.setState({email: lang.t('suspenduser.email', about)});
}
/*
* When an admin clicks to suspend a user a dialog is shown, this function
* handles the possible actions for that dialog.
*/
onActionClick = (stage, menuOption) => () => {
const {suspendUser, action} = this.props;
const {stage, email} = this.state;
const cancel = this.props.onClose;
const next = () => this.setState({stage: stage + 1});
const suspend = () => suspendUser(action.item_id, lang.t('suspenduser.email_subject'), email)
.then(this.props.onClose);
const suspendModalActions = [
[ cancel, next ],
[ cancel, suspend ]
];
return suspendModalActions[stage][menuOption]();
}
onEmailChange = (e) => this.setState({email: e.target.value})
render () {
const {action, onClose} = this.props;
if (!action) {
return null;
}
const {stage} = this.state;
const actionType = action.actionType;
const about = actionType === 'flag_bio' ? lang.t('suspenduser.bio') : lang.t('suspenduser.username');
return <Modal open={true} onClose={onClose}>
<div className={styles.title}>{lang.t(stages[stage].title, about)}</div>
<div className={styles.container}>
<div className={styles.description}>
{lang.t(stages[stage].description, about)}
</div>
{
stage === 1 &&
<div className={styles.writeContainer}>
<div className={styles.emailMessage}>{lang.t('suspenduser.write_message')}</div>
<div className={styles.emailContainer}>
<textarea
rows={5}
className={styles.emailInput}
value={this.state.email}
onChange={this.onEmailChange}/>
</div>
</div>
}
<div className={styles.modalButtons}>
{Object.keys(stages[stage].options).map((key, i) => (
<Button key={i} onClick={this.onActionClick(stage, i)}>
{lang.t(stages[stage].options[key], about)}
</Button>
))}
</div>
</div>
</Modal>;
}
}
export default SuspendUserModal;
const lang = new I18n(translations);
+50
View File
@@ -0,0 +1,50 @@
import React from 'react';
import styles from './ModerationList.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations.json';
import {Icon} from 'react-mdl';
import ActionButton from './ActionButton';
// Render a single comment for the list
const User = props => {
const {action, user} = props;
let userStatus = user.status;
// Do not display unless the user status is 'pending' or 'banned'.
// This means that they have already been reviewed and approved.
return (userStatus === 'PENDING' || userStatus === 'BANNED') &&
<li tabIndex={props.index} className={`mdl-card mdl-shadow--2dp ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
<div className={styles.itemHeader}>
<div className={styles.author}>
<span>{user.username}</span>
</div>
<div className={styles.sideActions}>
<div className={`actions ${styles.actions}`}>
{props.modActions.map(
(action, i) =>
<ActionButton
type={action.toUpperCase()}
key={i}
user={user}
menuOptionsMap={props.menuOptionsMap}
onClickAction={props.onClickAction}
onClickShowBanDialog={props.onClickShowBanDialog}/>
)}
</div>
</div>
<div>
{userStatus === 'banned' ?
<span className={styles.banned}><Icon name='error_outline'/> {lang.t('comment.banned_user')}</span> : null}
</div>
</div>
<div className={styles.flagCount}>
{`${action.count} ${action.action_type === 'flag_bio' ? lang.t('user.bio_flags') : lang.t('user.username_flags')}`}
</div>
</li>;
};
export default User;
const lang = new I18n(translations);
+47 -10
View File
@@ -1,18 +1,55 @@
import React from 'react';
import React, {PropTypes} from 'react';
import {Navigation, Drawer} from 'react-mdl';
import {Link} from 'react-router';
import styles from './Header.css';
import {IndexLink, Link} from 'react-router';
import styles from './Drawer.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
export default () => (
<Drawer>
<Navigation>
<Link className={styles.navLink} to="/admin">{lang.t('configure.moderate')}</Link>
<Link className={styles.navLink} to="/admin/community">{lang.t('configure.community')}</Link>
<Link className={styles.navLink} to="/admin/configure">{lang.t('configure.configure')}</Link>
</Navigation>
const CoralDrawer = ({handleLogout, restricted = false}) => (
<Drawer className={styles.header}>
{ !restricted ?
<div>
<Navigation className={styles.nav}>
<IndexLink
className={styles.navLink}
to="/admin/dashboard"
activeClassName={styles.active}>
{lang.t('configure.dashboard')}
</IndexLink>
<Link
className={styles.navLink}
to="/admin/moderate"
activeClassName={styles.active}>
{lang.t('configure.moderate')}
</Link>
<Link className={styles.navLink}
to="/admin/streams"
activeClassName={styles.active}>
{lang.t('configure.streams')}
</Link>
<Link className={styles.navLink}
to="/admin/community"
activeClassName={styles.active}>
{lang.t('configure.community')}
</Link>
<Link
className={styles.navLink}
to="/admin/configure"
activeClassName={styles.active}>
{lang.t('configure.configure')}
</Link>
<a onClick={handleLogout}>Sign Out</a>
<span>{`v${process.env.VERSION}`}</span>
</Navigation>
</div> : null }
</Drawer>
);
CoralDrawer.propTypes = {
handleLogout: PropTypes.func.isRequired,
restricted: PropTypes.bool // hide app elements from a logged out user
};
const lang = new I18n(translations);
export default CoralDrawer;
@@ -1,7 +1,25 @@
@custom-media --table-viewport (max-width: 1024px);
@media (--table-viewport) {
.logo {
margin-left: 58px;
h1 {
background: #696969;
}
span {
color: #fcfcfc;
}
}
.nav {
display: none;
}
}
.header {
background-color: transparent;
box-shadow: none;
min-height: 58px;
display: block;
}
.header > div {
@@ -16,6 +34,7 @@
.rightPanel {
position: absolute;
top: 0;
right: 0;
width: 170px;
height: 100%;
+62 -28
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, {PropTypes} from 'react';
import {Navigation, Header, IconButton, MenuItem, Menu} from 'react-mdl';
import {Link, IndexLink} from 'react-router';
import styles from './Header.css';
@@ -6,35 +6,69 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import {Logo} from './Logo';
export default ({handleLogout}) => (
const CoralHeader = ({handleLogout, restricted = false}) => (
<Header className={styles.header}>
<Logo />
<Navigation className={styles.nav}>
<IndexLink className={styles.navLink} to="/admin"
activeClassName={styles.active}>{lang.t('configure.moderate')}</IndexLink>
<Link className={styles.navLink} to="/admin/community"
activeClassName={styles.active}>{lang.t('configure.community')}</Link>
<Link className={styles.navLink} to="/admin/configure"
activeClassName={styles.active}>{lang.t('configure.configure')}</Link>
<Link className={styles.navLink} to="/admin/streams"
activeClassName={styles.active}>{lang.t('configure.streams')}</Link>
</Navigation>
<div className={styles.rightPanel}>
<ul>
<li className={styles.settings}>
<div>
<IconButton name="settings" id="menu-settings"/>
<Menu target="menu-settings" align="right">
<MenuItem onClick={handleLogout}>Sign Out</MenuItem>
</Menu>
</div>
</li>
<li>
{`v${process.env.VERSION}`}
</li>
</ul>
</div>
<Logo className={styles.logo} />
{
!restricted ?
<div>
<Navigation className={styles.nav}>
<IndexLink
className={styles.navLink}
to="/admin/dashboard"
activeClassName={styles.active}>
{lang.t('configure.dashboard')}
</IndexLink>
<Link
className={styles.navLink}
to="/admin/moderate"
activeClassName={styles.active}>
{lang.t('configure.moderate')}
</Link>
<Link className={styles.navLink}
to="/admin/streams"
activeClassName={styles.active}>
{lang.t('configure.streams')}
</Link>
<Link className={styles.navLink}
to="/admin/community"
activeClassName={styles.active}>
{lang.t('configure.community')}
</Link>
<Link
className={styles.navLink}
to="/admin/configure"
activeClassName={styles.active}>
{lang.t('configure.configure')}
</Link>
</Navigation>
<div className={styles.rightPanel}>
<ul>
<li className={styles.settings}>
<div>
<IconButton name="settings" id="menu-settings"/>
<Menu target="menu-settings" align="right">
<MenuItem onClick={handleLogout}>Sign Out</MenuItem>
</Menu>
</div>
</li>
<li>
{`v${process.env.VERSION}`}
</li>
</ul>
</div>
</div>
:
null
}
</Header>
);
CoralHeader.propTypes = {
handleLogout: PropTypes.func.isRequired,
restricted: PropTypes.bool // hide elemnts from a user that's logged out
};
const lang = new I18n(translations);
export default CoralHeader;
+12 -5
View File
@@ -1,15 +1,22 @@
import React from 'react';
import React, {PropTypes} from 'react';
import {Layout as LayoutMDL} from 'react-mdl';
import Header from './Header';
import Drawer from './Drawer';
import styles from './Layout.css';
export const Layout = ({children, ...props}) => (
const Layout = ({children, handleLogout = () => {}, restricted = false, ...props}) => (
<LayoutMDL fixedDrawer>
<Header {...props}/>
<Drawer />
<div className={styles.layout} >
<Header handleLogout={handleLogout} restricted={restricted} {...props} />
<Drawer handleLogout={handleLogout} restricted={restricted} {...props} />
<div className={styles.layout}>
{children}
</div>
</LayoutMDL>
);
Layout.propTypes = {
handleLogout: PropTypes.func,
restricted: PropTypes.bool // hide elements from a user that's logged out
};
export default Layout;
@@ -19,6 +19,7 @@
background: #E5E5E5;
height: 100%;
width: 128px;
z-index: 10;
}
.base {
+2 -2
View File
@@ -2,8 +2,8 @@ import React from 'react';
import styles from './Logo.css';
import {CoralLogo} from 'coral-ui';
export const Logo = () => (
<div className={styles.logo}>
export const Logo = ({className = ''}) => (
<div className={`${styles.logo} ${className}`}>
<h1>
<CoralLogo className={styles.base} />
<span>Talk</span>
@@ -1,6 +1,9 @@
export const FETCH_ASSETS_REQUEST = 'FETCH_ASSETS_REQUEST';
export const FETCH_ASSETS_SUCCESS = 'FETCH_ASSETS_SUCCESS';
export const FETCH_ASSETS_FAILURE = 'FETCH_ASSETS_FAILURE';
export const UPDATE_ASSET_STATE_REQUEST = 'UPDATE_ASSET_STATE_REQUEST';
export const UPDATE_ASSET_STATE_SUCCESS = 'UPDATE_ASSET_STATE_SUCCESS';
export const UPDATE_ASSET_STATE_FAILURE = 'UPDATE_ASSET_STATE_FAILURE';
export const UPDATE_ASSETS = 'UPDATE_ASSETS';
+8
View File
@@ -7,3 +7,11 @@ export const CHECK_CSRF_TOKEN = 'CHECK_CSRF_TOKEN';
export const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';
export const FETCH_FORGOT_PASSWORD_REQUEST = 'FETCH_FORGOT_PASSWORD_REQUEST';
export const FETCH_FORGOT_PASSWORD_SUCCESS = 'FETCH_FORGOT_PASSWORD_SUCCESS';
export const FETCH_FORGOT_PASSWORD_FAILURE = 'FETCH_FORGOT_PASSWORD_FAILURE';
@@ -0,0 +1,17 @@
export const NEXT_STEP = 'NEXT_STEP';
export const GO_TO_STEP = 'GO_TO_STEP';
export const PREVIOUS_STEP = 'PREVIOUS_STEP';
export const ADD_ERROR = 'ADD_ERROR';
export const HAS_ERROR = 'HAS_ERROR';
export const SHOW_ERRORS = 'SHOW_ERRORS';
export const CLEAR_ERRORS = 'CLEAR_ERRORS';
export const INSTALL_REQUEST = 'INSTALL_REQUEST';
export const INSTALL_SUCCESS = 'INSTALL_SUCCESS';
export const INSTALL_FAILURE = 'INSTALL_FAILURE';
export const UPDATE_FORMDATA_USER = 'UPDATE_FORMDATA_USER';
export const UPDATE_FORMDATA_SETTINGS = 'UPDATE_FORMDATA_SETTINGS';
export const UPDATE_PERMITTED_DOMAINS_SETTINGS = 'UPDATE_PERMITTED_DOMAINS_SETTINGS';
export const CHECK_INSTALL_REQUEST = 'CHECK_INSTALL_REQUEST';
export const CHECK_INSTALL_SUCCESS = 'CHECK_INSTALL_SUCCESS';
export const CHECK_INSTALL_FAILURE = 'CHECK_INSTALL_FAILURE';
@@ -0,0 +1,4 @@
export const TOGGLE_MODAL = 'TOGGLE_MODAL';
export const SINGLE_VIEW = 'SINGLE_VIEW';
export const SHOW_BANUSER_DIALOG = 'SHOW_BANUSER_DIALOG';
export const HIDE_BANUSER_DIALOG = 'HIDE_BANUSER_DIALOG';
-3
View File
@@ -1,3 +0,0 @@
export const UPDATE_STATUS_REQUEST = 'UPDATE_STATUS_REQUEST';
export const UPDATE_STATUS_SUCCESS = 'UPDATE_STATUS_SUCCESS';
export const UPDATE_STATUS_FAILURE = 'UPDATE_STATUS_FAILURE';
@@ -0,0 +1,6 @@
export const UPDATE_STATUS_REQUEST = 'UPDATE_STATUS_REQUEST';
export const UPDATE_STATUS_SUCCESS = 'UPDATE_STATUS_SUCCESS';
export const UPDATE_STATUS_FAILURE = 'UPDATE_STATUS_FAILURE';
export const USER_EMAIL_FAILURE = 'USER_EMAIL_FAILURE';
export const USERNAME_ENABLE_FAILURE = 'USERNAME_ENABLE_FAILURE';
export const USERS_MODERATION_QUEUE_FETCH_SUCCESS = 'USERS_MODERATION_QUEUE_FETCH_SUCCESS';
@@ -1,13 +0,0 @@
@custom-media --big-viewport (min-width: 780px);
.container {
max-width: 860px;
margin: 0 auto;
}
@media (--big-viewport) {
.tab {
flex: none;
}
}
@@ -1,66 +0,0 @@
import React from 'react';
import styles from './CommentStream.css';
import {Snackbar} from 'react-mdl';
import {connect} from 'react-redux';
import {createComment, flagComment} from 'actions/comments';
import CommentList from 'components/CommentList';
import CommentBox from 'components/CommentBox';
/**
* Renders a comment stream using a CommentList component
* and adds a box for adding a new comment
*/
class CommentStream extends React.Component {
constructor (props) {
super(props);
this.state = {snackbar: false, snackbarMsg: ''};
this.onSubmit = this.onSubmit.bind(this);
this.onClickAction = this.onClickAction.bind(this);
}
// Fetch the comments before mounting
componentWillMount () {
this.props.dispatch({type: 'COMMENT_STREAM_FETCH'});
}
// Submit the new comment
onSubmit (comment) {
this.props.dispatch(createComment(comment.name, comment.body));
}
// The only action for now is flagging
onClickAction (action, id) {
if (action === 'flag') {
this.props.dispatch(flagComment(id));
clearTimeout(this._snackTimeout);
this.setState({snackbar: true, snackbarMsg: 'Thank you for reporting this comment. Our moderation team has been notified and will review it shortly.'});
this._snackTimeout = setTimeout(() => this.setState({snackbar: false}), 30000);
}
}
// Render the comment box along with the CommentList
render ({comments, users}, {snackbar, snackbarMsg}) {
return (
<div className={styles.container}>
<CommentBox onSubmit={this.onSubmit}/>
<CommentList isActive hideActive
singleView={false}
commentIds={comments.ids}
comments={comments.byId}
users={users.byId}
onClickAction={this.onClickAction}
actions={['flag']}
loading={comments.loading} />
<Snackbar active={snackbar}>{snackbarMsg}</Snackbar>
</div>
);
}
}
const mapStateToProps = state => ({
comments: state.comments.toJS(),
users: state.users.toJS()
});
export default connect(mapStateToProps)(CommentStream);
@@ -1,19 +1,19 @@
import React from 'react';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import translations from 'coral-admin/src/translations.json';
import styles from './Community.css';
import Table from './Table';
import Loading from './Loading';
import NoResults from './NoResults';
import {Pager} from 'coral-ui';
import EmptyCard from '../../components/EmptyCard';
const lang = new I18n(translations);
const tableHeaders = [
{
title: lang.t('community.username_and_email'),
field: 'displayName'
field: 'username'
},
{
title: lang.t('community.account_creation_date'),
@@ -54,13 +54,14 @@ const Community = ({isFetching, commenters, ...props}) => {
</div>
<div className={styles.mainContent}>
{ isFetching && <Loading /> }
{ !hasResults && <NoResults /> }
{ hasResults &&
<Table
headers={tableHeaders}
data={commenters}
onHeaderClickHandler={props.onHeaderClickHandler}
/>
{
hasResults
? <Table
headers={tableHeaders}
data={commenters}
onHeaderClickHandler={props.onHeaderClickHandler}
/>
: <EmptyCard>{lang.t('community.no-results')}</EmptyCard>
}
<Pager
totalPages={props.totalPages}
@@ -1,9 +0,0 @@
import React from 'react';
const NoResults = () => (
<div>
No users found with that user name or email address
</div>
);
export default NoResults;
@@ -44,7 +44,7 @@ class Table extends Component {
{commenters.map((row, i)=> (
<tr key={i}>
<td className="mdl-data-table__cell--non-numeric">
{row.displayName}
{row.username}
<span className={styles.email}>{row.profiles.map(({id}) => id)}</span>
</td>
<td className="mdl-data-table__cell--non-numeric">
@@ -55,8 +55,8 @@ class Table extends Component {
className={styles.selectField}
label={lang.t('community.status')}
onChange={status => this.onCommenterStatusChange(row.id, status)}>
<Option value={'active'}>{lang.t('community.active')}</Option>
<Option value={'banned'}>{lang.t('community.banned')}</Option>
<Option value={'ACTIVE'}>{lang.t('community.active')}</Option>
<Option value={'BANNED'}>{lang.t('community.banned')}</Option>
</SelectField>
</td>
<td className="mdl-data-table__cell--non-numeric">
@@ -65,8 +65,8 @@ class Table extends Component {
label={lang.t('community.role')}
onChange={role => this.onRoleChange(row.id, role)}>
<Option value={''}>.</Option>
<Option value={'moderator'}>{lang.t('community.moderator')}</Option>
<Option value={'admin'}>{lang.t('community.admin')}</Option>
<Option value={'MODERATOR'}>{lang.t('community.moderator')}</Option>
<Option value={'ADMIN'}>{lang.t('community.admin')}</Option>
</SelectField>
</td>
</tr>
@@ -1,192 +0,0 @@
import React from 'react';
import {SelectField, Option} from 'react-mdl-selectfield';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import styles from './Configure.css';
import {Textfield, Checkbox} from 'react-mdl';
import {Card, Icon, Spinner} from 'coral-ui';
const TIMESTAMPS = {
weeks: 60 * 60 * 24 * 7,
days: 60 * 60 * 24,
hours: 60 * 60
};
const updateCharCountEnable = (updateSettings, charCountChecked) => () => {
const charCountEnable = !charCountChecked;
updateSettings({charCountEnable});
};
const updateCharCount = (updateSettings, settingsError) => (event) => {
const charCount = event.target.value;
if (charCount.match(/[^0-9]/) || charCount.length === 0) {
settingsError('charCount', true);
} else {
settingsError('charCount', false);
}
updateSettings({charCount: charCount});
};
const updateModeration = (updateSettings, mod) => () => {
const moderation = mod === 'pre' ? 'post' : 'pre';
updateSettings({moderation});
};
const updateInfoBoxEnable = (updateSettings, infoBox) => () => {
const infoBoxEnable = !infoBox;
updateSettings({infoBoxEnable});
};
const updateInfoBoxContent = (updateSettings) => (event) => {
const infoBoxContent = event.target.value;
updateSettings({infoBoxContent});
};
const updateClosedMessage = (updateSettings) => (event) => {
const closedMessage = event.target.value;
updateSettings({closedMessage});
};
// If we are changing the measure we need to recalculate using the old amount
// Same thing if we are just changing the amount
const updateClosedTimeout = (updateSettings, ts, isMeasure) => (event) => {
if (isMeasure) {
const amount = getTimeoutAmount(ts);
const closedTimeout = amount * TIMESTAMPS[event];
updateSettings({closedTimeout});
} else {
const val = event.target.value;
const measure = getTimeoutMeasure(ts);
const closedTimeout = val * TIMESTAMPS[measure];
updateSettings({closedTimeout});
}
};
const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError, settings, errors}) => {
if (fetchingSettings) {
return <Card shadow="4"><Spinner/>Loading settings...</Card>;
}
return (
<div className={styles.commentSettingsSection}>
<h3>{title}</h3>
<Card className={`${styles.configSetting} ${settings.moderation === 'pre' ? styles.enabledSetting : styles.disabledSetting}`}>
<div className={styles.action}>
<Checkbox
onChange={updateModeration(updateSettings, settings.moderation)}
checked={settings.moderation === 'pre'} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.enable-pre-moderation')}</div>
<p className={settings.moderation === 'pre' ? '' : styles.disabledSettingText}>
{lang.t('configure.enable-pre-moderation-text')}
</p>
</div>
</Card>
<Card className={`${styles.configSetting} ${settings.charCountEnable ? styles.enabledSetting : styles.disabledSetting}`}>
<div className={styles.action}>
<Checkbox
onChange={updateCharCountEnable(updateSettings, settings.charCountEnable)}
checked={settings.charCountEnable} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.comment-count-header')}</div>
<p className={settings.charCountEnable ? '' : styles.disabledSettingText}>
<span>{lang.t('configure.comment-count-text-pre')}</span>
<input type='text'
className={`${styles.charCountTexfield} ${settings.charCountEnable && styles.charCountTexfieldEnabled}`}
htmlFor='charCount'
onChange={updateCharCount(updateSettings, settingsError)}
value={settings.charCount}/>
<span>{lang.t('configure.comment-count-text-post')}</span>
{
errors.charCount &&
<span className={styles.settingsError}>
<br/>
<Icon name="error_outline"/>
{lang.t('configure.comment-count-error')}
</span>
}
</p>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? styles.enabledSetting : styles.disabledSetting}`}>
<div className={styles.action}>
<Checkbox
onChange={updateInfoBoxEnable(updateSettings, settings.infoBoxEnable)}
checked={settings.infoBoxEnable} />
</div>
<div className={styles.content}>
{lang.t('configure.include-comment-stream')}
<p>
{lang.t('configure.include-comment-stream-desc')}
</p>
<div className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? null : styles.hidden}`} >
<div className={styles.content}>
<Textfield
onChange={updateInfoBoxContent(updateSettings)}
value={settings.infoBoxContent}
label={lang.t('configure.include-text')}
rows={3}/>
</div>
</div>
</div>
</Card>
<Card className={styles.configSettingInfoBox}>
<div className={styles.content}>
{lang.t('configure.closed-comments-desc')}
<div>
<Textfield
onChange={updateClosedMessage(updateSettings)}
value={settings.closedMessage}
label={lang.t('configure.closed-comments-label')}
rows={3}/>
</div>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox}`}>
<div className={styles.content}>
{lang.t('configure.close-after')}
<br />
<Textfield
type='number'
pattern='[0-9]+'
style={{width: 50}}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout)}
value={getTimeoutAmount(settings.closedTimeout)}
label={lang.t('configure.closed-comments-label')} />
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout, true)}>
<Option value={'hours'}>{lang.t('configure.hours')}</Option>
<Option value={'days'}>{lang.t('configure.days')}</Option>
<Option value={'weeks'}>{lang.t('configure.weeks')}</Option>
</SelectField>
</div>
</div>
</Card>
</div>
);
};
export default CommentSettings;
// To see if we are talking about weeks, days or hours
// We talk the remainder of the division and see if it's 0
const getTimeoutMeasure = ts => {
if (ts % TIMESTAMPS['weeks'] === 0) {
return 'weeks';
} else if (ts % TIMESTAMPS['days'] === 0) {
return 'days';
} else if (ts % TIMESTAMPS['hours'] === 0) {
return 'hours';
}
};
// Dividing the amount by it's measure (hours, days, weeks) we
// obtain the amount of time
const getTimeoutAmount = ts => ts / TIMESTAMPS[getTimeoutMeasure(ts)];
const lang = new I18n(translations);
@@ -3,7 +3,7 @@
h3 {
color: black;
font-size: 1.76em;
font-size: 1.26em;
font-weight: 500;
}
}
@@ -24,6 +24,10 @@
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
h3 {
margin: 0;
}
}
.settingsError {
@@ -118,18 +122,19 @@
}
#bannedWordlist, #suspectWordlist {
width: 100%;
padding: 10px;
display: block;
input {
width: 100%;
}
}
.wordlistHeader {
font-weight: bold;
font-size:18px;
margin-bottom:3px;
.customCSSInput {
width: 100%;
font-size: 14px;
padding: 14px;
letter-spacing: 0.03em;
color: #555;
box-sizing: border-box;
}
.enabledSetting {
@@ -150,7 +155,7 @@
margin-top: 38px;
}
.commentSettingsSection {
.settingsSection {
padding-bottom: 200px;
.action {
display: inline-block;
@@ -5,23 +5,23 @@ import {
updateSettings,
saveSettingsToServer,
updateWordlist,
updateDomainlist
} from '../../actions/settings';
import {Button, List, Item} from 'coral-ui';
import {Button, List, Item, Card, Spinner} from 'coral-ui';
import styles from './Configure.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import EmbedLink from './EmbedLink';
import CommentSettings from './CommentSettings';
import Wordlist from './Wordlist';
import has from 'lodash/has';
import translations from 'coral-admin/src/translations.json';
import StreamSettings from './StreamSettings';
import ModerationSettings from './ModerationSettings';
import TechSettings from './TechSettings';
class Configure extends Component {
constructor (props) {
super(props);
this.state = {
activeSection: 'comments',
activeSection: 'stream',
changed: false,
errors: {}
};
@@ -47,6 +47,11 @@ class Configure extends Component {
this.props.dispatch(updateWordlist(listName, list));
}
onChangeDomainlist = (listName, list) => {
this.setState({changed: true});
this.props.dispatch(updateDomainlist(listName, list));
}
onSettingUpdate = (setting) => {
this.setState({changed: true});
this.props.dispatch(updateSettings(setting));
@@ -63,33 +68,48 @@ class Configure extends Component {
getSection (section) {
const pageTitle = this.getPageTitle(section);
let sectionComponent;
switch(section){
case 'comments':
return <CommentSettings
title={pageTitle}
fetchingSettings={this.props.fetchingSettings}
case 'stream':
sectionComponent = <StreamSettings
settings={this.props.settings}
updateSettings={this.onSettingUpdate}
errors={this.state.errors}
settingsError={this.onSettingError}/>;
case 'embed':
return <EmbedLink title={pageTitle} />;
case 'wordlist':
return has(this, 'props.settings.wordlist')
? <Wordlist
bannedWords={this.props.settings.wordlist.banned}
suspectWords={this.props.settings.wordlist.suspect}
onChangeWordlist={this.onChangeWordlist} />
: <p>loading wordlists</p>;
break;
case 'moderation':
sectionComponent = <ModerationSettings
onChangeWordlist={this.onChangeWordlist}
settings={this.props.settings}
updateSettings={this.onSettingUpdate} />;
break;
case 'tech':
sectionComponent = <TechSettings
onChangeDomainlist={this.onChangeDomainlist}
settings={this.props.settings}
updateSettings={this.onSettingUpdate} />;
}
if (this.props.settings.fetchingSettings) {
return <Card shadow="4"><Spinner/>Loading settings...</Card>;
}
return (
<div className={styles.settingsSection}>
<h3>{pageTitle}</h3>
{sectionComponent}
</div>
);
}
getPageTitle (section) {
switch(section) {
case 'comments':
return lang.t('configure.comment-settings');
case 'embed':
return lang.t('configure.embed-comment-stream');
case 'stream':
return lang.t('configure.stream-settings');
case 'moderation':
return lang.t('configure.moderation-settings');
case 'tech':
return lang.t('configure.tech-settings');
default:
return '';
}
@@ -106,14 +126,14 @@ class Configure extends Component {
<div className={styles.container}>
<div className={styles.leftColumn}>
<List onChange={this.changeSection} activeItem={activeSection}>
<Item itemId='comments' icon="settings">
{lang.t('configure.comment-settings')}
<Item itemId='stream' icon='speaker_notes'>
{lang.t('configure.stream-settings')}
</Item>
<Item itemId='embed' icon='code'>
{lang.t('configure.embed-comment-stream')}
<Item itemId='moderation' icon='thumbs_up_down'>
{lang.t('configure.moderation-settings')}
</Item>
<Item itemId='wordlist' icon='settings'>
{lang.t('configure.wordlist')}
<Item itemId='tech' icon='code'>
{lang.t('configure.tech-settings')}
</Item>
</List>
<div className={styles.saveBox}>
@@ -151,7 +171,9 @@ class Configure extends Component {
}
}
const mapStateToProps = state => state.settings.toJS();
const mapStateToProps = state => ({
settings: state.settings.toJS()
});
export default connect(mapStateToProps)(Configure);
const lang = new I18n(translations);
@@ -0,0 +1,26 @@
import React from 'react';
import {Card} from 'coral-ui';
import styles from './Configure.css';
import TagsInput from 'react-tagsinput';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
const lang = new I18n(translations);
const Domainlist = ({domains, onChangeDomainlist}) => {
return (
<Card id={styles.domainlist} className={styles.configSetting}>
<h3>{lang.t('configure.domain-list-title')}</h3>
<p className={styles.domainlistDesc}>{lang.t('configure.domain-list-text')}</p>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
addOnPaste={true}
pasteSplit={data => data.split(',').map(d => d.trim())}
onChange={tags => onChangeDomainlist('whitelist', tags)}
/>
</Card>
);
};
export default Domainlist;
@@ -25,22 +25,34 @@ class EmbedLink extends Component {
}
render () {
const embedText = `<div id='coralStreamEmbed'></div><script type='text/javascript' src='${window.location.protocol}//pym.nprapps.org/pym.v1.min.js'></script><script>var pymParent = new pym.Parent('coralStreamEmbed', '${window.location.protocol}//${window.location.host}/embed/stream', {title: 'Comments'});</script>`;
const location = window.location;
const talkBaseUrl = [
location.protocol,
'//',
location.hostname,
location.port ? (`:${ window.location.port}`) : ''
].join('');
const coralJsUrl = [talkBaseUrl, '/embed.js'].join('');
const nonce = String(Math.random()).slice(2);
const streamElementId = `coral_talk_${nonce}`;
const embedText = `
<div id="${streamElementId}"></div>
<script src="${coralJsUrl}" async onload="
Coral.Talk.render(document.getElementById('${streamElementId}'), {
talk: '${talkBaseUrl}'
});
"></script>
`.trim();
return (
<div>
<h3>{this.props.title}</h3>
<div>
<Card shadow="2">
<p>{lang.t('configure.copy-and-paste')}</p>
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
{lang.t('embedlink.copy')}
</Button>
<div className={styles.copiedText}>{this.state.copied && 'Copied!'}</div>
</Card>
</div>
</div>
<Card shadow="2" className={styles.configSetting}>
<h3>Embed Comment Stream</h3>
<p>{lang.t('configure.copy-and-paste')}</p>
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
{lang.t('embedlink.copy')}
</Button>
<div className={styles.copiedText}>{this.state.copied && 'Copied!'}</div>
</Card>
);
}
}
@@ -0,0 +1,73 @@
import React, {PropTypes} from 'react';
import styles from './Configure.css';
import {Card} from 'coral-ui';
import {Checkbox} from 'react-mdl';
import Wordlist from './Wordlist';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
const lang = new I18n(translations);
const updateModeration = (updateSettings, mod) => () => {
const moderation = mod === 'PRE' ? 'POST' : 'PRE';
updateSettings({moderation});
};
const updateEmailConfirmation = (updateSettings, verify) => () => {
updateSettings({requireEmailConfirmation: !verify});
};
const ModerationSettings = ({settings, updateSettings, onChangeWordlist}) => {
// just putting this here for shorthand below
const on = styles.enabledSetting;
const off = styles.disabledSetting;
return (
<div>
<Card className={`${styles.configSetting} ${settings.requireEmailConfirmation ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateEmailConfirmation(updateSettings, settings.requireEmailConfirmation)}
checked={settings.requireEmailConfirmation} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.require-email-verification')}</div>
<p className={settings.requireEmailConfirmation ? '' : styles.disabledSettingText}>
{lang.t('configure.require-email-verification-text')}
</p>
</div>
</Card>
<Card className={`${styles.configSetting} ${settings.moderation === 'PRE' ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateModeration(updateSettings, settings.moderation)}
checked={settings.moderation === 'PRE'} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.enable-pre-moderation')}</div>
<p className={settings.moderation === 'PRE' ? '' : styles.disabledSettingText}>
{lang.t('configure.enable-pre-moderation-text')}
</p>
</div>
</Card>
<Wordlist
bannedWords={settings.wordlist.banned}
suspectWords={settings.wordlist.suspect}
onChangeWordlist={onChangeWordlist} />
</div>
);
};
ModerationSettings.propTypes = {
onChangeWordlist: PropTypes.func.isRequired,
settings: PropTypes.shape({
moderation: PropTypes.string.isRequired,
wordlist: PropTypes.shape({
banned: PropTypes.array.isRequired,
suspect: PropTypes.array.isRequired
})
}).isRequired,
updateSettings: PropTypes.func.isRequired
};
export default ModerationSettings;
@@ -0,0 +1,174 @@
import React from 'react';
import {SelectField, Option} from 'react-mdl-selectfield';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import styles from './Configure.css';
import {Textfield, Checkbox} from 'react-mdl';
import {Card, Icon} from 'coral-ui';
const TIMESTAMPS = {
weeks: 60 * 60 * 24 * 7,
days: 60 * 60 * 24,
hours: 60 * 60
};
const updateCharCountEnable = (updateSettings, charCountChecked) => () => {
const charCountEnable = !charCountChecked;
updateSettings({charCountEnable});
};
const updateCharCount = (updateSettings, settingsError) => (event) => {
const charCount = event.target.value;
if (charCount.match(/[^0-9]/) || charCount.length === 0) {
settingsError('charCount', true);
} else {
settingsError('charCount', false);
}
updateSettings({charCount: charCount});
};
const updateInfoBoxEnable = (updateSettings, infoBox) => () => {
const infoBoxEnable = !infoBox;
updateSettings({infoBoxEnable});
};
const updateInfoBoxContent = (updateSettings) => (event) => {
const infoBoxContent = event.target.value;
updateSettings({infoBoxContent});
};
const updateClosedMessage = (updateSettings) => (event) => {
const closedMessage = event.target.value;
updateSettings({closedMessage});
};
// If we are changing the measure we need to recalculate using the old amount
// Same thing if we are just changing the amount
const updateClosedTimeout = (updateSettings, ts, isMeasure) => (event) => {
if (isMeasure) {
const amount = getTimeoutAmount(ts);
const closedTimeout = amount * TIMESTAMPS[event];
updateSettings({closedTimeout});
} else {
const val = event.target.value;
const measure = getTimeoutMeasure(ts);
const closedTimeout = val * TIMESTAMPS[measure];
updateSettings({closedTimeout});
}
};
const StreamSettings = ({updateSettings, settingsError, settings, errors}) => {
// just putting this here for shorthand below
const on = styles.enabledSetting;
const off = styles.disabledSetting;
return (
<div>
<Card className={`${styles.configSetting} ${settings.charCountEnable ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateCharCountEnable(updateSettings, settings.charCountEnable)}
checked={settings.charCountEnable} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.comment-count-header')}</div>
<p className={settings.charCountEnable ? '' : styles.disabledSettingText}>
<span>{lang.t('configure.comment-count-text-pre')}</span>
<input type='text'
className={`${styles.charCountTexfield} ${settings.charCountEnable && styles.charCountTexfieldEnabled}`}
htmlFor='charCount'
onChange={updateCharCount(updateSettings, settingsError)}
value={settings.charCount}/>
<span>{lang.t('configure.comment-count-text-post')}</span>
{
errors.charCount &&
<span className={styles.settingsError}>
<br/>
<Icon name="error_outline"/>
{lang.t('configure.comment-count-error')}
</span>
}
</p>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateInfoBoxEnable(updateSettings, settings.infoBoxEnable)}
checked={settings.infoBoxEnable} />
</div>
<div className={styles.content}>
{lang.t('configure.include-comment-stream')}
<p>
{lang.t('configure.include-comment-stream-desc')}
</p>
<div className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? null : styles.hidden}`} >
<div className={styles.content}>
<Textfield
onChange={updateInfoBoxContent(updateSettings)}
value={settings.infoBoxContent}
label={lang.t('configure.include-text')}
rows={3}/>
</div>
</div>
</div>
</Card>
<Card className={styles.configSettingInfoBox}>
<div className={styles.content}>
{lang.t('configure.closed-comments-desc')}
<div>
<Textfield
onChange={updateClosedMessage(updateSettings)}
value={settings.closedMessage}
label={lang.t('configure.closed-comments-label')}
rows={3}/>
</div>
</div>
</Card>
<Card className={styles.configSettingInfoBox}>
<div className={styles.content}>
{lang.t('configure.close-after')}
<br />
<Textfield
type='number'
pattern='[0-9]+'
style={{width: 50}}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout)}
value={getTimeoutAmount(settings.closedTimeout)}
label={lang.t('configure.closed-comments-label')} />
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout, true)}>
<Option value={'hours'}>{lang.t('configure.hours')}</Option>
<Option value={'days'}>{lang.t('configure.days')}</Option>
<Option value={'weeks'}>{lang.t('configure.weeks')}</Option>
</SelectField>
</div>
</div>
</Card>
</div>
);
};
export default StreamSettings;
// To see if we are talking about weeks, days or hours
// We talk the remainder of the division and see if it's 0
const getTimeoutMeasure = ts => {
if (ts % TIMESTAMPS['weeks'] === 0) {
return 'weeks';
} else if (ts % TIMESTAMPS['days'] === 0) {
return 'days';
} else if (ts % TIMESTAMPS['hours'] === 0) {
return 'hours';
}
};
// Dividing the amount by it's measure (hours, days, weeks) we
// obtain the amount of time
const getTimeoutAmount = ts => ts / TIMESTAMPS[getTimeoutMeasure(ts)];
const lang = new I18n(translations);
@@ -0,0 +1,44 @@
import React, {PropTypes} from 'react';
import {Card} from 'coral-ui';
import Domainlist from './Domainlist';
import EmbedLink from './EmbedLink';
import styles from './Configure.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
const lang = new I18n(translations);
const updateCustomCssUrl = (updateSettings) => (event) => {
const customCssUrl = event.target.value;
updateSettings({customCssUrl});
};
const TechSettings = ({settings, onChangeDomainlist, updateSettings}) => {
return (
<div>
<Domainlist
domains={settings.domains.whitelist}
onChangeDomainlist={onChangeDomainlist} />
<EmbedLink />
<Card className={styles.configSetting}>
<h3>{lang.t('configure.custom-css-url')}</h3>
<p>{lang.t('configure.custom-css-url-desc')}</p>
<br />
<input
className={styles.customCSSInput}
value={settings.customCssUrl}
onChange={updateCustomCssUrl(updateSettings)} />
</Card>
</div>
);
};
TechSettings.propTypes = {
settings: PropTypes.shape({
domains: PropTypes.shape({
whitelist: PropTypes.array.isRequired
})
}).isRequired,
updateSettings: PropTypes.func.isRequired
};
export default TechSettings;
@@ -7,9 +7,8 @@ import {Card} from 'coral-ui';
const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
<div>
<h3>{lang.t('configure.banned-words-title')}</h3>
<Card id={styles.bannedWordlist}>
<p className={styles.wordlistHeader}>{lang.t('configure.banned-word-header')}</p>
<Card id={styles.bannedWordlist} className={styles.configSetting}>
<h3>{lang.t('configure.banned-words-title')}</h3>
<p className={styles.wordlistDesc}>{lang.t('configure.banned-word-text')}</p>
<TagsInput
value={bannedWords}
@@ -19,9 +18,8 @@ const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
onChange={tags => onChangeWordlist('banned', tags)}
/>
</Card>
<h3>{lang.t('configure.suspect-words-title')}</h3>
<Card id={styles.suspectWordlist}>
<p className={styles.wordlistHeader}>{lang.t('configure.suspect-word-header')}</p>
<Card id={styles.suspectWordlist} className={styles.configSetting}>
<h3>{lang.t('configure.suspect-words-title')}</h3>
<p className={styles.wordlistDesc}>{lang.t('configure.suspect-word-text')}</p>
<TagsInput
value={suspectWords}
@@ -0,0 +1,9 @@
.Dashboard {
display: flex;
}
.heading {
margin: 0;
font-size: 1.5rem;
font-weight: bold;
}
@@ -0,0 +1,46 @@
import React from 'react';
import styles from './Dashboard.css';
import {compose} from 'react-apollo';
import {connect} from 'react-redux';
import {getMetrics} from 'coral-admin/src/graphql/queries';
import FlagWidget from './FlagWidget';
import LikeWidget from './LikeWidget';
import {showBanUserDialog, hideBanUserDialog} from 'coral-admin/src/actions/moderation';
import {Spinner} from 'coral-ui';
class Dashboard extends React.Component {
render () {
if (this.props.data && this.props.data.loading) {
return <Spinner />;
}
const {data: {assetsByLike, assetsByFlag}} = this.props;
return (
<div className={styles.Dashboard}>
<FlagWidget assets={assetsByFlag} />
<LikeWidget assets={assetsByLike} />
</div>
);
}
}
const mapStateToProps = state => {
return {
settings: state.settings.toJS(),
moderation: state.moderation.toJS()
};
};
const mapDispatchToProps = dispatch => ({
showBanUserDialog: (user, commentId) => dispatch(showBanUserDialog(user, commentId)),
hideBanUserDialog: () => dispatch(hideBanUserDialog(false))
});
export default compose(
connect(mapStateToProps, mapDispatchToProps),
getMetrics
)(Dashboard);
@@ -0,0 +1,55 @@
import React from 'react';
import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
const lang = new I18n(translations);
const FlagWidget = (props) => {
const {assets} = props;
return (
<div className={styles.widget}>
<h2 className={styles.heading}>Articles with the most flags</h2>
<table className={styles.widgetTable}>
<thead className={styles.widgetHead}>
<tr>
<th>{lang.t('streams.article')}</th>
<th>{lang.t('dashboard.flags')}</th>
</tr>
</thead>
<tbody>
{
assets.length
? assets.map(asset => {
const flagSummary = asset.action_summaries.find(s => s.type === 'FlagAssetActionSummary');
return (
<tr className={styles.rowLinkify} key={asset.id}>
<td>
<Link className={styles.linkToAsset} to={`/admin/moderate/flagged/${asset.id}`}>
<p className={styles.assetTitle}>{asset.title}</p>
<p className={styles.lede}>{asset.author} Published: {new Date(asset.created_at).toLocaleDateString()}</p>
</Link>
</td>
<td>
<Link className={styles.linkToAsset} to={`/admin/moderate/flagged/${asset.id}`}>
<p className={styles.widgetCount}>{flagSummary ? flagSummary.actionCount : 0}</p>
</Link>
</td>
</tr>
);
})
: <tr className={styles.rowLinkify}><td colSpan="2">{lang.t('dashboard.no_flags')}</td></tr>
}
{ /* rows in a table with a fixed height will expand and ignore height.
this extra row will expand to fill the extra space. */
assets.length < 10 ? <tr></tr> : null
}
</tbody>
</table>
</div>
);
};
export default FlagWidget;
@@ -0,0 +1,56 @@
import React from 'react';
import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
const lang = new I18n(translations);
const LikeWidget = (props) => {
const {assets} = props;
return (
<div className={styles.widget}>
<h2 className={styles.heading}>Articles with the most likes</h2>
<table className={styles.widgetTable}>
<thead className={styles.widgetHead}>
<tr>
<th>{lang.t('streams.article')}</th>
<th>{lang.t('modqueue.likes')}</th>
</tr>
</thead>
<tbody>
{
assets.length
? assets.map(asset => {
const likeSummary = asset.action_summaries.find(s => s.type === 'LikeAssetActionSummary');
return (
<tr className={styles.rowLinkify} key={asset.id}>
<td>
<Link className={styles.linkToAsset} to={`/admin/moderate/flagged/${asset.id}`}>
<p className={styles.assetTitle}>{asset.title}</p>
<p className={styles.lede}>{asset.author} Published: {new Date(asset.created_at).toLocaleDateString()}</p>
</Link>
</td>
<td>
<Link className={styles.linkToAsset} to={`/admin/moderate/flagged/${asset.id}`}>
<p className={styles.widgetCount}>{likeSummary ? likeSummary.actionCount : 0}</p>
</Link>
</td>
</tr>
);
})
: <tr className={styles.rowLinkify}><td colSpan="2">{lang.t('dashboard.no_likes')}</td></tr>
}
{ /* rows in a table with a fixed height will expand and ignore height.
this extra row will expand to fill the extra space. */
assets.length < 10 ? <tr></tr> : null
}
</tbody>
</table>
</div>
);
};
export default LikeWidget;
@@ -0,0 +1,40 @@
import React from 'react';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
import ModerationQueue from 'coral-admin/src/containers/ModerationQueue/ModerationQueue';
import styles from './Widget.css';
import BanUserDialog from 'coral-admin/src/components/BanUserDialog';
const lang = new I18n(translations);
const MostLikedCommentsWidget = props => {
const {
comments,
moderation,
settings,
handleBanUser,
showBanUserDialog,
hideBanUserDialog,
acceptComment,
rejectComment
} = props;
return (
<div className={styles.widget}>
<h2 className={styles.heading}>{lang.t('most_liked_comments')}</h2>
<ModerationQueue
comments={comments}
suspectWords={settings.wordlist.suspect}
showBanUserDialog={showBanUserDialog}
acceptComment={acceptComment}
rejectComment={rejectComment} />
<BanUserDialog
open={moderation.banDialog}
user={moderation.user}
handleClose={hideBanUserDialog}
handleBanUser={handleBanUser} />
</div>
);
};
export default MostLikedCommentsWidget;
@@ -0,0 +1,84 @@
:root {
--row-height: 80px;
}
.widget {
box-sizing: border-box;
margin: 10px 5px 5px 5px;
box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.2);
padding: 15px;
flex: 1;
background-color: white;
}
.heading {
margin: 0;
padding-left: 10px;
font-size: 1.5rem;
font-weight: bold;
}
.widgetTable {
width: 100%;
border-collapse: collapse;
user-select: none;
height: calc(var(--row-height) * 10);
}
.widgetTable thead th {
color: rgb(35, 102, 223);
padding: 10px;
text-align: left;
text-transform: capitalize;
}
.widgetTable thead th:last-child {
width: 10%;
}
.rowLinkify {
cursor: pointer;
border-bottom: 1px solid lightgrey;
color: #555;
height: var(--row-height);
}
.rowLinkify:last-child {
border-bottom: none;
}
.rowLinkify:hover {
background-color: #f8f8f8;
}
.widgetTable tbody td {
padding: 10px;
}
.linkToAsset {
display: block;
text-decoration: none;
}
.lede {
font-size: 0.9em;
color: #aaa;
}
.assetTitle {
color: #555;
text-decoration: none;
font-size: 1.2em;
font-weight: normal;
margin: 0;
}
.assetTitle:hover {
text-decoration: underline;
}
.widgetCount {
color: #555;
font-size: 1.3em;
font-weight: 400;
}
@@ -0,0 +1,101 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import styles from './style.css';
import {Wizard, WizardNav} from 'coral-ui';
import Layout from 'coral-admin/src/components/ui/Layout';
import {
goToStep,
nextStep,
submitUser,
checkInstall,
previousStep,
finishInstall,
submitSettings,
updateUserFormData,
updateSettingsFormData,
updatePermittedDomains
} from '../../actions/install';
import InitialStep from './components/Steps/InitialStep';
import AddOrganizationName from './components/Steps/AddOrganizationName';
import CreateYourAccount from './components/Steps/CreateYourAccount';
import PermittedDomainsStep from './components/Steps/PermittedDomainsStep';
import FinalStep from './components/Steps/FinalStep';
class InstallContainer extends Component {
componentDidMount() {
const {checkInstall} = this.props;
checkInstall(() => {
this.context.router.push('/admin');
});
}
render() {
const {install} = this.props;
return (
<Layout restricted={true}>
<div className={styles.Install}>
{
!install.alreadyInstalled ? (
<div>
<h2>Welcome to the Coral Project</h2>
{ install.step !== 0 ? <WizardNav items={install.navItems} currentStep={install.step} icon='check'/> : null }
<Wizard currentStep={install.step} {...this.props}>
<InitialStep/>
<AddOrganizationName/>
<CreateYourAccount/>
<PermittedDomainsStep/>
<FinalStep/>
</Wizard>
</div>
) : (
<div>Talk is already installed</div>
)
}
</div>
</Layout>
);
}
}
InstallContainer.contextTypes = {
router: React.PropTypes.object
};
const mapStateToProps = state => ({
install: state.install.toJS()
});
const mapDispatchToProps = dispatch => ({
nextStep: () => dispatch(nextStep()),
goToStep: step => dispatch(goToStep(step)),
previousStep: () => dispatch(previousStep()),
finishInstall: () => dispatch(finishInstall()),
checkInstall: next => dispatch(checkInstall(next)),
handleDomainsChange: value => {
dispatch(updatePermittedDomains(value));
},
handleSettingsChange: e => {
const {name, value} = e.currentTarget;
dispatch(updateSettingsFormData(name, value));
},
handleUserChange: e => {
const {name, value} = e.currentTarget;
dispatch(updateUserFormData(name, value));
},
handleSettingsSubmit: e => {
e.preventDefault();
dispatch(submitSettings());
},
handleUserSubmit: e => {
e.preventDefault();
dispatch(submitUser());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(InstallContainer);
@@ -0,0 +1,31 @@
import React from 'react';
import styles from './style.css';
import {TextField, Button} from 'coral-ui';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const AddOrganizationName = props => {
const {handleSettingsChange, handleSettingsSubmit, install} = props;
return (
<div className={styles.step}>
<p>{lang.t('ADD_ORGANIZATION.DESCRIPTION')}</p>
<div className={styles.form}>
<form onSubmit={handleSettingsSubmit}>
<TextField
className={styles.TextField}
id="organizationName"
type="text"
label={lang.t('ADD_ORGANIZATION.LABEL')}
onChange={handleSettingsChange}
showErrors={install.showErrors}
errorMsg={install.errors.organizationName} />
<Button type="submit" cStyle='black' full>{lang.t('ADD_ORGANIZATION.SAVE')}</Button>
</form>
</div>
</div>
);
};
export default AddOrganizationName;
@@ -0,0 +1,69 @@
import React from 'react';
import styles from './style.css';
import {TextField, Button, Spinner} from 'coral-ui';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const InitialStep = props => {
const {handleUserChange, handleUserSubmit, install} = props;
return (
<div className={styles.step}>
<div className={styles.form}>
<form onSubmit={handleUserSubmit}>
<TextField
className={styles.textField}
id="email"
type="email"
label={lang.t('CREATE.EMAIL')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.email}
noValidate
/>
<TextField
className={styles.textField}
id="username"
type="text"
label={lang.t('CREATE.USERNAME')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.username}
/>
<TextField
className={styles.textField}
id="password"
type="password"
label={lang.t('CREATE.PASSWORD')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.password}
/>
<TextField
className={styles.textField}
id="confirmPassword"
type="password"
label={lang.t('CREATE.CONFIRM_PASSWORD')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.confirmPassword}
/>
{
!props.install.isLoading ?
<Button cStyle='black' type="submit" full>{lang.t('CREATE.SAVE')}</Button>
:
<Spinner />
}
{props.install.installRequest === 'FAILURE' && <div className={styles.error}>Error: {props.install.installRequestError}</div>}
</form>
</div>
</div>
);
};
export default InitialStep;
@@ -0,0 +1,20 @@
import React from 'react';
import styles from './style.css';
import {Button} from 'coral-ui';
import {Link} from 'react-router';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const InitialStep = () => {
return (
<div className={`${styles.step} ${styles.finalStep}`}>
<p>{lang.t('FINAL.DESCRIPTION')}</p>
<Button raised><Link to='/admin'>{lang.t('FINAL.LAUNCH')}</Link></Button>
<Button cStyle='black' raised><a href="http://coralproject.net">{lang.t('FINAL.CLOSE')}</a></Button>
</div>
);
};
export default InitialStep;
@@ -0,0 +1,19 @@
import React from 'react';
import styles from './style.css';
import {Button} from 'coral-ui';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const InitialStep = props => {
const {nextStep} = props;
return (
<div className={styles.step}>
<p>{lang.t('INITIAL.DESCRIPTION')}</p>
<Button cStyle='green' onClick={nextStep} raised>{lang.t('INITIAL.SUBMIT')}</Button>
</div>
);
};
export default InitialStep;
@@ -0,0 +1,44 @@
import React from 'react';
import styles from './style.css';
import {Button, Select, Option, TextField} from 'coral-ui';
const InviteTeamMembers = props => {
const {nextStep} = props;
return (
<div className={styles.step}>
<h3>Invite Team Members </h3>
<p>
Once registered, new team members will receive an email to Create
their password.
</p>
<div className={styles.form}>
<form>
<TextField
className={styles.textField}
id="email"
type="email"
label='Email address' required/>
<TextField
className={styles.textField}
id="username"
type="text"
label='Username' required/>
<div className={styles.textField}>
<label htmlFor='role'>Assing a role</label>
<Select id='role' label='Select Role'>
<Option>Admin</Option>
<Option>Moderator</Option>
</Select>
</div>
<Button cStyle='black' onClick={nextStep} icon='arrow_forward' full>Invite team member</Button>
</form>
</div>
</div>
);
};
export default InviteTeamMembers;
@@ -0,0 +1,31 @@
import React from 'react';
import styles from './style.css';
import {Button, Card} from 'coral-ui';
import TagsInput from 'react-tagsinput';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const PermittedDomainsStep = props => {
const {finishInstall, install, handleDomainsChange} = props;
const domains = install.data.settings.domains.whitelist;
return (
<div className={styles.step}>
<h3>{lang.t('PERMITTED_DOMAINS.TITLE')}</h3>
<Card className={styles.card}>
<p>{lang.t('PERMITTED_DOMAINS.DESCRIPTION')}</p>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
addOnPaste={true}
pasteSplit={data => data.split(',').map(d => d.trim())}
onChange={tags => handleDomainsChange(tags)}
/>
</Card>
<Button cStyle='green' onClick={finishInstall} raised>{lang.t('PERMITTED_DOMAINS.SUBMIT')}</Button>
</div>
);
};
export default PermittedDomainsStep;
@@ -0,0 +1,83 @@
.step {
padding: 20px 0;
h3 {
max-width: 450px;
margin: 0 auto;
text-align: left;
font-size: 1.4em;
font-weight: bold;
}
p {
max-width: 450px;
margin: 0 auto 30px;
font-size: 1.1em;
text-align: left;
}
> button {
min-width: 145px;
a {
text-decoration: none;
color: inherit;
}
}
.form {
max-width: 300px;
margin: 30px auto;
text-align: left;
form > button {
margin: 30px 0;
}
.textField {
text-align: left;
label {
text-align: left;
display: block;
margin: 10px 0;
font-weight: 400;
font-size: 1.08em;
}
> input {
border: solid 1px rgba(0, 0, 0, 0.23);
padding: 10px;
border-radius: 3px;
width: 100%;
box-sizing: border-box;
transition: border-color 0.2s ease;
font-size: 1em;
&:focus {
border-color: black;
}
}
}
}
.card {
max-width: 500px;
margin: 20px auto;
text-align: left;
}
}
.finalStep {
button {
width: 225px;
margin-right: 10px;
}
}
.error {
background: #FFEBEE;
color: #B71C1C;
padding: 10px;
margin-bottom: 20px;
border-radius: 2px;
}
@@ -0,0 +1,12 @@
.Install {
max-width: 900px;
margin: 0 auto;
text-align: center;
padding: 50px 0;
h2 {
font-size: 2em;
font-weight: 500;
margin: 0;
}
}
@@ -0,0 +1,58 @@
{
"en": {
"INITIAL" : {
"DESCRIPTION": "Let's set up your Talk community in just a few short steps.",
"SUBMIT": "Get Started"
},
"ADD_ORGANIZATION": {
"DESCRIPTION": "Please tell us the name of your organization. This will appear in emails when inviting new team members.",
"LABEL": "Organization Name",
"SAVE": "Save"
},
"CREATE": {
"EMAIL": "Email address",
"USERNAME": "Username",
"PASSWORD": "Password",
"CONFIRM_PASSWORD": "Confirm Password",
"SAVE": "Save"
},
"PERMITTED_DOMAINS": {
"TITLE": "Permitted domains",
"DESCRIPTION": "Enter the domains you would like to permit for Talk, e.g. your local, staging and production environments (ex. localhost:3000, staging.domain.com, domain.com).",
"SUBMIT": "Finish install"
},
"FINAL": {
"DESCRIPTION": "Thanks for installing Talk! We sent an email to verify your email address. While you finish setting up the account, you can start engaging with your readers now.",
"LAUNCH": "Launch Talk",
"CLOSE": "Close this Installer"
}
},
"es": {
"INITIAL" : {
"DESCRIPTION": "Configuremos tu comunidad de Talk en sólo algunos pasos.",
"SUBMIT": "Empezá!"
},
"ADD_ORGANIZATION": {
"DESCRIPTION": "Por favor, dinos el nombre de tu organización. Este aparecerá en los emails cuando invites nuevos miembros.",
"LABEL": "Nombre de la Organización",
"SAVE": "Guardar"
},
"CREATE": {
"EMAIL": "Dirección de E-Mail",
"USERNAME": "Usuario",
"PASSWORD": "Contraseña",
"CONFIRM_PASSWORD": "Confirmar contraseña",
"SAVE": "Guardar"
},
"PERMITTED_DOMAINS": {
"TITLE": "Dominios Permitidos",
"DESCRIPTION": "Agrega dominios permitidos a Talk, e.g. tu localhost, staging y ambientes de production (ex. localhost:3000, staging.domain.com, domain.com).",
"SUBMIT": "Finalizar instalación"
},
"FINAL": {
"DESCRIPTION": "Gracias por instalar Talk! Te enviamos un email para verificar tu identidad. Mientras se termina de configurar la cuenta, ya puedes empezar a interactuar con tus lectores",
"LAUNCH": "Lanzar Talk",
"CLOSE": "Cerrar este instalador"
}
}
}
@@ -1,9 +1,9 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Layout} from '../components/ui/Layout';
import {checkLogin, logout} from '../actions/auth';
import Layout from '../components/ui/Layout';
import {checkLogin, handleLogin, logout, requestPasswordReset} from '../actions/auth';
import {FullLoading} from '../components/FullLoading';
import {PermissionRequired} from '../components/PermissionRequired';
import AdminLogin from '../components/AdminLogin';
class LayoutContainer extends Component {
componentWillMount () {
@@ -11,10 +11,23 @@ class LayoutContainer extends Component {
checkLogin();
}
render () {
const {isAdmin, loggedIn, loadingUser} = this.props.auth;
const {
isAdmin,
loggedIn,
loadingUser,
loginError,
passwordRequestSuccess
} = this.props.auth;
const {handleLogout} = this.props;
if (loadingUser) { return <FullLoading />; }
if (!isAdmin) { return <PermissionRequired />; }
if (isAdmin && loggedIn) { return <Layout {...this.props} />; }
if (!isAdmin) {
return <AdminLogin
handleLogin={this.props.handleLogin}
requestPasswordReset={this.props.requestPasswordReset}
passwordRequestSuccess={passwordRequestSuccess}
errorMessage={loginError} />;
}
if (isAdmin && loggedIn) { return <Layout handleLogout={handleLogout} {...this.props} />; }
return <FullLoading />;
}
}
@@ -25,6 +38,8 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
checkLogin: () => dispatch(checkLogin()),
handleLogin: (username, password) => dispatch(handleLogin(username, password)),
requestPasswordReset: email => dispatch(requestPasswordReset(email)),
handleLogout: () => dispatch(logout())
});
@@ -32,4 +47,3 @@ export default connect(
mapStateToProps,
mapDispatchToProps
)(LayoutContainer);
@@ -1,42 +1,32 @@
import React from 'react';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {compose} from 'react-apollo';
import key from 'keymaster';
import isEqual from 'lodash/isEqual';
import {modQueueQuery} from '../../graphql/queries';
import {banUser, setCommentStatus} from '../../graphql/mutations';
import {
updateStatus,
showBanUserDialog,
hideBanUserDialog,
fetchPendingQueue,
fetchRejectedQueue,
fetchFlaggedQueue,
fetchModerationQueueComments,
} from 'actions/comments';
import {userStatusUpdate} from 'actions/users';
import {fetchSettings} from 'actions/settings';
import {updateAssets} from 'actions/assets';
import {toggleModal, singleView, showBanUserDialog, hideBanUserDialog} from 'actions/moderation';
import {Spinner} from 'coral-ui';
import BanUserDialog from '../../components/BanUserDialog';
import ModerationQueue from './ModerationQueue';
import ModerationMenu from './components/ModerationMenu';
import ModerationHeader from './components/ModerationHeader';
import NotFoundAsset from './components/NotFoundAsset';
class ModerationContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: 'pending',
singleView: false,
modalOpen: false
};
this.onClose = this.onClose.bind(this);
this.onTabClick = this.onTabClick.bind(this);
}
class ModerationContainer extends Component {
componentWillMount() {
this.props.fetchModerationQueueComments();
const {toggleModal, singleView} = this.props;
this.props.fetchSettings();
key('s', () => this.setState({singleView: !this.state.singleView}));
key('shift+/', () => this.setState({modalOpen: true}));
key('esc', () => this.setState({modalOpen: false}));
key('s', () => singleView());
key('shift+/', () => toggleModal(true));
key('esc', () => toggleModal(false));
}
componentWillUnmount() {
@@ -45,78 +35,87 @@ class ModerationContainer extends React.Component {
key.unbind('esc');
}
componentDidMount() {
// Hack for dynamic mdl tabs
if (typeof componentHandler !== 'undefined') {
// FIXME: fix this hack
componentHandler.upgradeAllRegistered(); // eslint-disable-line no-undef
componentWillReceiveProps(nextProps) {
const {updateAssets} = this.props;
if(!isEqual(nextProps.data.assets, this.props.data.assets)) {
updateAssets(nextProps.data.assets);
}
}
onTabClick(activeTab) {
this.setState({activeTab});
if (activeTab === 'pending') {
this.props.fetchPendingQueue();
} else if (activeTab === 'rejected') {
this.props.fetchRejectedQueue();
} else if (activeTab === 'flagged') {
this.props.fetchFlaggedQueue();
} else {
this.props.fetchModerationQueueComments();
}
}
onClose() {
this.setState({modalOpen: false});
}
render () {
const {comments} = this.props;
const premodIds = comments.ids.filter(id => comments.byId[id].status === 'premod');
const rejectedIds = comments.ids.filter(id => comments.byId[id].status === 'rejected');
const flaggedIds = comments.ids.filter(id =>
comments.byId[id].flagged === true &&
comments.byId[id].status !== 'rejected' &&
comments.byId[id].status !== 'accepted'
);
const {data, moderation, settings, assets, modQueueResort, ...props} = this.props;
const providedAssetId = this.props.params.id;
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
let asset;
if (data.loading) {
return <div><Spinner/></div>;
}
if (data.error) {
return <div>Error</div>;
}
if (providedAssetId) {
asset = assets.find(asset => asset.id === this.props.params.id);
if (!asset) {
return <NotFoundAsset assetId={providedAssetId} />;
}
}
const comments = data[activeTab];
return (
<ModerationQueue
onTabClick={this.onTabClick}
onClose={this.onClose}
premodIds={premodIds}
rejectedIds={rejectedIds}
flaggedIds={flaggedIds}
{...this.props}
{...this.state}
/>
<div>
<ModerationHeader asset={asset} />
<ModerationMenu
asset={asset}
premodCount={data.premodCount}
rejectedCount={data.rejectedCount}
flaggedCount={data.flaggedCount}
modQueueResort={modQueueResort}
/>
<ModerationQueue
currentAsset={asset}
comments={comments}
activeTab={activeTab}
suspectWords={settings.wordlist.suspect}
showBanUserDialog={props.showBanUserDialog}
acceptComment={props.acceptComment}
rejectComment={props.rejectComment}
/>
<BanUserDialog
open={moderation.banDialog}
user={moderation.user}
handleClose={props.hideBanUserDialog}
handleBanUser={props.banUser}
/>
</div>
);
}
}
const mapStateToProps = state => ({
comments: state.comments.toJS(),
moderation: state.moderation.toJS(),
settings: state.settings.toJS(),
users: state.users.toJS()
assets: state.assets.get('assets')
});
const mapDispatchToProps = dispatch => {
return {
fetchSettings: () => dispatch(fetchSettings()),
fetchModerationQueueComments: () => dispatch(fetchModerationQueueComments()),
fetchPendingQueue: () => dispatch(fetchPendingQueue()),
fetchRejectedQueue: () => dispatch(fetchRejectedQueue()),
fetchFlaggedQueue: () => dispatch(fetchFlaggedQueue()),
showBanUserDialog: (userId, userName, commentId) => dispatch(showBanUserDialog(userId, userName, commentId)),
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
banUser: (userId, commentId) => dispatch(userStatusUpdate('banned', userId, commentId)).then(() => {
dispatch(fetchModerationQueueComments());
}),
updateStatus: (action, comment) => dispatch(updateStatus(action, comment))
};
};
const mapDispatchToProps = dispatch => ({
toggleModal: toggle => dispatch(toggleModal(toggle)),
onClose: () => dispatch(toggleModal(false)),
singleView: () => dispatch(singleView()),
updateAssets: assets => dispatch(updateAssets(assets)),
fetchSettings: () => dispatch(fetchSettings()),
showBanUserDialog: (user, commentId) => dispatch(showBanUserDialog(user, commentId)),
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
});
export default connect(mapStateToProps, mapDispatchToProps)(ModerationContainer);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
setCommentStatus,
modQueueQuery,
banUser
)(ModerationContainer);
@@ -0,0 +1,9 @@
import React from 'react';
const ModerationLayout = props => (
<div>
{props.children}
</div>
);
export default ModerationLayout;
@@ -1,53 +0,0 @@
@custom-media --big-viewport (min-width: 780px);
.listContainer {
max-width: 860px;
margin: 0 auto;
}
.tabBar {
background: #262626;
z-index: 5;
}
.tab {
flex: 1;
color: white;
text-transform: capitalize;
font-weight: 500;
font-size: 15px;
letter-spacing: 1px;
transition: border-bottom 200ms;
}
.active {
color: white;
box-sizing: border-box;
border-bottom: solid 5px #F36451;
}
.active > span {
color: white;
}
.active:after {
background: transparent !important;
}
.showShortcuts {
position: absolute;
right: 130px;
display: flex;
align-items: center;
font-size: 13px;
span {
margin-left: 7px;
}
}
@media (--big-viewport) {
.tab {
flex: none;
}
}
@@ -1,104 +1,47 @@
import React from 'react';
import styles from './ModerationQueue.css';
import ModerationKeysModal from 'components/ModerationKeysModal';
import CommentList from 'components/CommentList';
import BanUserDialog from 'components/BanUserDialog';
import React, {PropTypes} from 'react';
import Comment from './components/Comment';
import EmptyCard from '../../components/EmptyCard';
import {actionsMap} from './helpers/moderationQueueActionsMap';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import translations from 'coral-admin/src/translations';
const lang = new I18n(translations);
export default ({onTabClick, ...props}) => (
<div>
<div className='mdl-tabs'>
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
<a href='#pending'
onClick={(e) => {
e.preventDefault();
onTabClick('pending');
}}
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'pending' ? styles.active : ''}`}
>
{lang.t('modqueue.pending')}
</a>
<a href='#rejected'
onClick={(e) => {
e.preventDefault();
onTabClick('rejected');
}}
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'rejected' ? styles.active : ''}`}
>
{lang.t('modqueue.rejected')}
</a>
<a href='#flagged'
onClick={(e) => {
e.preventDefault();
onTabClick('flagged');
}}
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'flagged' ? styles.active : ''}`}
>
{lang.t('modqueue.flagged')}
</a>
</div>
<div className={`mdl-tabs__panel is-active ${styles.listContainer}`} id='pending'>
{
props.activeTab === 'pending'
? <div>
<CommentList
suspectWords={props.settings.settings.wordlist.suspect}
isActive={props.activeTab === 'pending'}
singleView={props.singleView}
commentIds={props.premodIds}
comments={props.comments.byId}
users={props.users.byId}
onClickAction={props.updateStatus}
onClickShowBanDialog={props.showBanUserDialog}
modActions={['reject', 'approve', 'ban']}
loading={props.comments.loading} />
<BanUserDialog
open={props.comments.showBanUserDialog}
handleClose={props.hideBanUserDialog}
onClickBanUser={props.banUser}
user={props.comments.banUser} />
</div>
: null
}
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='rejected'>
const ModerationQueue = ({comments, ...props}) => {
return (
<div id="moderationList">
<ul style={{paddingLeft: 0}}>
{
props.activeTab === 'rejected'
? <CommentList
suspectWords={props.settings.settings.wordlist.suspect}
isActive={props.activeTab === 'rejected'}
singleView={props.singleView}
commentIds={props.rejectedIds}
comments={props.comments.byId}
users={props.users.byId}
onClickAction={props.updateStatus}
modActions={['approve']}
loading={props.comments.loading} />
: null
comments.length
? comments.map((comment, i) => {
const status = comment.action_summaries ? 'FLAGGED' : comment.status;
return <Comment
key={i}
index={i}
comment={comment}
commentType={props.activeTab}
suspectWords={props.suspectWords}
actions={actionsMap[status]}
showBanUserDialog={props.showBanUserDialog}
acceptComment={props.acceptComment}
rejectComment={props.rejectComment}
currentAsset={props.currentAsset}
/>;
})
: <EmptyCard>{lang.t('modqueue.emptyqueue')}</EmptyCard>
}
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='flagged'>
{
props.activeTab === 'flagged'
? <CommentList
suspectWords={props.settings.settings.wordlist.suspect}
isActive={props.activeTab === 'flagged'}
singleView={props.singleView}
commentIds={props.flaggedIds}
comments={props.comments.byId}
users={props.users.byId}
onClickAction={props.updateStatus}
modActions={['reject', 'approve']}
loading={props.comments.loading} />
: null
}
</div>
<ModerationKeysModal open={props.modalOpen} onClose={props.closeModal} />
</ul>
</div>
</div>
);
);
};
ModerationQueue.propTypes = {
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
currentAsset: PropTypes.object,
showBanUserDialog: PropTypes.func.isRequired,
rejectComment: PropTypes.func.isRequired,
acceptComment: PropTypes.func.isRequired,
comments: PropTypes.array.isRequired
};
export default ModerationQueue;
@@ -0,0 +1,98 @@
import React, {PropTypes} from 'react';
import timeago from 'timeago.js';
import Linkify from 'react-linkify';
import Highlighter from 'react-highlight-words';
import {Link} from 'react-router';
import styles from './styles.css';
import {Icon} from 'coral-ui';
import FlagBox from './FlagBox';
import CommentType from './CommentType';
import ActionButton from 'coral-admin/src/components/ActionButton';
import BanUserButton from 'coral-admin/src/components/BanUserButton';
const linkify = new Linkify();
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations.json';
const lang = new I18n(translations);
const Comment = ({actions = [], ...props}) => {
const links = linkify.getMatches(props.comment.body);
const actionSummaries = props.comment.action_summaries;
return (
<li tabIndex={props.index}
className={`mdl-card mdl-shadow--2dp ${styles.Comment} ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
<div className={styles.container}>
<div className={styles.itemHeader}>
<div className={styles.author}>
<span>{props.comment.user.name}</span>
<span className={styles.created}>
{timeago().format(props.comment.created_at || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}
</span>
<BanUserButton user={props.comment.user} onClick={() => props.showBanUserDialog(props.comment.user, props.comment.id)} />
<CommentType type={props.commentType} />
</div>
<div className={styles.sideActions}>
{links ? <span className={styles.hasLinks}><Icon name='error_outline'/> Contains Link</span> : null}
<div className={`actions ${styles.actions}`}>
{actions.map((action, i) =>
<ActionButton key={i}
type={action}
user={props.comment.user}
acceptComment={() => props.acceptComment({commentId: props.comment.id})}
rejectComment={() => props.rejectComment({commentId: props.comment.id})}
/>
)}
</div>
{props.comment.user.status === 'banned' ?
<span className={styles.banned}>
<Icon name='error_outline'/>
{lang.t('comment.banned_user')}
</span>
: null}
</div>
</div>
{!props.currentAsset && (
<div className={styles.moderateArticle}>
Story: {props.comment.asset.title} <Link to={`/admin/moderate/${props.comment.asset.id}`}>Moderate &rarr;</Link>
</div>
)}
<div className={styles.itemBody}>
<p className={styles.body}>
<Linkify component='span' properties={{style: linkStyles}}>
<Highlighter searchWords={props.suspectWords} textToHighlight={props.comment.body}/>
</Linkify>
</p>
</div>
</div>
{actionSummaries && <FlagBox actionSummaries={actionSummaries} />}
</li>
);
};
Comment.propTypes = {
acceptComment: PropTypes.func.isRequired,
rejectComment: PropTypes.func.isRequired,
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
currentAsset: PropTypes.object,
comment: PropTypes.shape({
body: PropTypes.string.isRequired,
action_summaries: PropTypes.array,
created_at: PropTypes.string.isRequired,
user: PropTypes.shape({
status: PropTypes.string
}),
asset: PropTypes.shape({
title: PropTypes.string,
id: PropTypes.string
})
})
};
const linkStyles = {
backgroundColor: 'rgb(255, 219, 135)',
padding: '1px 2px'
};
export default Comment;
@@ -0,0 +1,14 @@
.count {
display: inline-block;
background: #989797;
margin: 2px;
vertical-align: middle;
padding: 4px 7px;
border-radius: 2px;
margin-left: 10px;
line-height: 20px;
box-sizing: border-box;
height: 27px;
right: 0;
margin-top: -2px;
}

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