diff --git a/.eslintignore b/.eslintignore index fc0d50a25..cd0c80673 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,13 @@ dist +docs client/lib **/*.html plugins/* !plugins/coral-plugin-facebook-auth +!plugins/coral-plugin-auth !plugins/coral-plugin-respect +!plugins/coral-plugin-offtopic +!plugins/coral-plugin-like +!plugins/coral-plugin-mod +!plugins/coral-plugin-love node_modules diff --git a/.gitignore b/.gitignore index f0c3b2a4b..11e738d57 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ coverage/ plugins.json plugins/* !plugins/coral-plugin-facebook-auth +!plugins/coral-plugin-auth !plugins/coral-plugin-respect !plugins/coral-plugin-offtopic !plugins/coral-plugin-like diff --git a/PLUGINS.md b/PLUGINS.md index 4462f8577..8bac10aba 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -280,6 +280,41 @@ send data to the client. If the type in question contains args, clients may subs For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions). +#### Field: `tokenUserNotFound` + +```js +tokenUserNotFound: async ({jwt, token}) => { + let profile = await someExternalService(token); + if (!profile) { + return null; + } + + let user = await UserModel.findOneAndUpdate({ + id: profile.id + }, { + id: profile.id, + username: profile.username, + lowercaseUsername: profile.username.toLowerCase(), + roles: [], + profiles: [] + }, { + setDefaultsOnInsert: true, + new: true, + upsert: true + }); + + return user; +} +``` + +The `tokenUserNotFound` hook allows auth integrations to hook into the event +when a valid token is provided but a user can't be found in the database that +matches the provided id. + +The function is async, and should return the user object that was created in the +database, or null if the user wasn't found. The `jwt` paramenter of the object +is the unpacked token, while `token` is the original jwt token string. + #### Field: `router` ```js diff --git a/README.md b/README.md index 718847d9e..48055e326 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ To set up a development environment or build from source, see [INSTALL.md](https 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 The Talk application looks for the following configuration values either as environment variables: @@ -45,6 +44,12 @@ sign and verify tokens via a `HS256` algorithm. Refer to the wiki page on [Configuration Loading](https://github.com/coralproject/talk/wiki/Configuration-Loading) for alternative methods of loading configuration during development. +### Using Trust + +Talk ships with core components we call "Trust". This allows Talk to automate certain actions based on previous user behavior. + +Our first feature is the notion of Karma. Talk will automatically pre-moderate comments of users who have a negative karma score. You can [see more how karma works here](/services/karma.js). + ## Supported Browsers ### Web @@ -58,6 +63,8 @@ alternative methods of loading configuration during development. - iPad - iPad Pro +- iPhone 7 Plus +- iPhone 7 - iPhone 6 Plus - iPhone 6 - iPhone 5 diff --git a/app.js b/app.js index 16840f965..959aed0a3 100644 --- a/app.js +++ b/app.js @@ -6,11 +6,14 @@ const helmet = require('helmet'); const authentication = require('./middleware/authentication'); const {passport} = require('./services/passport'); const plugins = require('./services/plugins'); +const i18n = require('./services/i18n'); const enabled = require('debug').enabled; const errors = require('./errors'); const {createGraphOptions} = require('./graph'); const apollo = require('graphql-server-express'); const accepts = require('accepts'); +const compression = require('compression'); +const cookieParser = require('cookie-parser'); const app = express(); @@ -31,6 +34,8 @@ app.set('trust proxy', 1); app.use(helmet({ frameguard: false })); +app.use(compression()); +app.use(cookieParser()); app.use(bodyParser.json()); //============================================================================== @@ -153,6 +158,8 @@ app.use('/', (err, req, res, next) => { console.error(err); } + i18n.init(req); + if (err instanceof errors.APIError) { res.status(err.status); res.render('error', { diff --git a/bin/cli-assets b/bin/cli-assets index 166152baf..97b5b043c 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -5,7 +5,7 @@ */ const program = require('./commander'); -const parseDuration = require('parse-duration'); +const parseDuration = require('ms'); const Table = require('cli-table'); const AssetModel = require('../models/asset'); const mongoose = require('../services/mongoose'); diff --git a/client/coral-admin/config.json b/client/coral-admin/config.json deleted file mode 100644 index 156740de5..000000000 --- a/client/coral-admin/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "basePath": "admin" -} diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index 989316dd9..3bde5f931 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -1,25 +1,20 @@ import React from 'react'; import {Router, Route, IndexRedirect, browserHistory} from 'react-router'; -import Stories from 'containers/Stories/Stories'; -import Configure from 'containers/Configure/Configure'; -import LayoutContainer from 'containers/LayoutContainer'; -import InstallContainer from 'containers/Install/InstallContainer'; +import Configure from 'routes/Configure'; +import Dashboard from 'routes/Dashboard'; +import Install from 'routes/Install'; +import Stories from 'routes/Stories'; +import {CommunityLayout, Community} from 'routes/Community'; +import {ModerationLayout, Moderation} from 'routes/Moderation'; -import CommunityLayout from 'containers/Community/CommunityLayout'; -import CommunityContainer from 'containers/Community/CommunityContainer'; - -import ModerationLayout from 'containers/ModerationQueue/ModerationLayout'; -import ModerationContainer from 'containers/ModerationQueue/ModerationContainer'; - -import Dashboard from 'containers/Dashboard/Dashboard'; +import Layout from 'containers/Layout'; const routes = (
{lang.t('modqueue.mod-faster')}
-{lang.t('modqueue.try-these')}:
+{t('modqueue.mod_faster')}
+{t('modqueue.try_these')}:
{lang.t('modqueue.view-more-shortcuts')} {lang.t('modqueue.shift-key')} + /
+{t('modqueue.view_more_shortcuts')} {t('modqueue.shift_key')} + /
| {lang.t(shortcut.title)} | +{t(shortcut.title)} | ||
|---|---|---|---|
| {key} | -{lang.t(shortcut.shortcuts[key])} | +{t(shortcut.shortcuts[key])} |
| onHeaderClickHandler({field: header.field})}> - {header.title} - | - ))} -|||
|---|---|---|---|
| - {row.username} - {row.profiles.map(({id}) => id)} - | -- {row.created_at} - | -
- |
-
- |
-
{lang.t('INITIAL.DESCRIPTION')}
- -Member since {new Date(user.created_at).toLocaleString()}
-
- Account summary
-
Data represents the last six months of activity
-
Total Comments
-{totalComments}
-Reject Rate
-{`${(rejectedPercent).toFixed(1)}%`}
-