diff --git a/app.js b/app.js index 108ad2e11..0be7a1830 100644 --- a/app.js +++ b/app.js @@ -15,6 +15,7 @@ const apollo = require('graphql-server-express'); const accepts = require('accepts'); const compression = require('compression'); const cookieParser = require('cookie-parser'); +const {ROOT_URL} = require('./config'); const app = express(); @@ -74,6 +75,12 @@ app.use('/public', express.static(path.join(__dirname, 'public'))); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); +// Set the BASE_URL as the ROOT_URL. +app.locals.BASE_URL = ROOT_URL; +if (app.locals.BASE_URL[app.locals.BASE_URL.length - 1] !== '/') { + app.locals.BASE_URL += '/'; +} + //============================================================================== // PASSPORT MIDDLEWARE //============================================================================== diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index e89679036..f0e94f1ad 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -1,5 +1,6 @@ import React from 'react'; import {Router, Route, IndexRedirect, browserHistory, Redirect} from 'react-router'; +import {useBasename} from 'history'; import Configure from 'routes/Configure'; import Dashboard from 'routes/Dashboard'; @@ -59,6 +60,8 @@ const routes = ( ); -const AppRouter = () => ; +const AppRouter = () => browserHistory)({ + basename: '/talk/' +})} routes={routes}/>; export default AppRouter; diff --git a/client/coral-admin/src/routes/Configure/components/EmbedLink.js b/client/coral-admin/src/routes/Configure/components/EmbedLink.js index 1cd48cdc5..4506d2f06 100644 --- a/client/coral-admin/src/routes/Configure/components/EmbedLink.js +++ b/client/coral-admin/src/routes/Configure/components/EmbedLink.js @@ -2,6 +2,7 @@ import React, {Component} from 'react'; import t from 'coral-framework/services/i18n'; import styles from './Configure.css'; import {Button, Card} from 'coral-ui'; +import {BASE_URL} from 'coral-framework/constants/url'; class EmbedLink extends Component { @@ -24,21 +25,14 @@ class EmbedLink extends Component { } render () { - const location = window.location; - const talkBaseUrl = [ - location.protocol, - '//', - location.hostname, - location.port ? (`:${window.location.port}`) : '' - ].join(''); - const coralJsUrl = [talkBaseUrl, '/embed.js'].join(''); + const coralJsUrl = [BASE_URL, '/embed.js'].join(''); const nonce = String(Math.random()).slice(2); const streamElementId = `coral_talk_${nonce}`; const embedText = `
`.trim(); diff --git a/client/coral-framework/constants/url.js b/client/coral-framework/constants/url.js new file mode 100644 index 000000000..92144bdec --- /dev/null +++ b/client/coral-framework/constants/url.js @@ -0,0 +1,7 @@ +import url from 'url'; + +const base = document.querySelector('base'); +const baseUrl = base && url.parse(base.href) || {}; + +export const BASE_URL = base.href; +export const BASE_PATH = baseUrl.pathname; diff --git a/client/coral-framework/helpers/request.js b/client/coral-framework/helpers/request.js index 57e3a6cde..782aa83f6 100644 --- a/client/coral-framework/helpers/request.js +++ b/client/coral-framework/helpers/request.js @@ -2,6 +2,7 @@ import bowser from 'bowser'; import * as Storage from './storage'; import merge from 'lodash/merge'; import {getStore} from 'coral-framework/services/store'; +import {BASE_PATH} from 'coral-framework/constants/url'; /** * getAuthToken returns the active auth token or null @@ -75,7 +76,7 @@ const handleResp = (res) => { } }; -export const base = '/api/v1'; +export const base = `${BASE_PATH}api/v1`; export default (url, options) => { return fetch(`${base}${url}`, buildOptions(options)).then(handleResp); diff --git a/client/coral-framework/services/client.js b/client/coral-framework/services/client.js index b266822fb..6f044f621 100644 --- a/client/coral-framework/services/client.js +++ b/client/coral-framework/services/client.js @@ -4,6 +4,7 @@ import {SubscriptionClient, addGraphQLSubscriptions} from 'subscriptions-transpo import MessageTypes from 'subscriptions-transport-ws/dist/message-types'; import {getAuthToken} from '../helpers/request'; import introspectionQueryResultData from '../graphql/introspection.json'; +import {BASE_PATH} from 'coral-framework/constants/url'; let client, wsClient = null, wsClientToken = null; @@ -33,7 +34,7 @@ export function getClient(options = {}) { } const protocol = location.protocol === 'https:' ? 'wss' : 'ws'; - wsClient = new SubscriptionClient(`${protocol}://${location.host}/api/v1/live`, { + wsClient = new SubscriptionClient(`${protocol}://${location.host}${BASE_PATH}api/v1/live`, { reconnect: true, lazy: true, connectionParams: { diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js index f11c20888..060bfc34b 100644 --- a/client/coral-framework/services/transport.js +++ b/client/coral-framework/services/transport.js @@ -1,12 +1,13 @@ import {createNetworkInterface} from 'apollo-client'; import {getAuthToken} from '../helpers/request'; +import {BASE_PATH} from 'coral-framework/constants/url'; //============================================================================== // NETWORK INTERFACE //============================================================================== const networkInterface = createNetworkInterface({ - uri: '/api/v1/graph/ql', + uri: `${BASE_PATH}api/v1/graph/ql`, opts: { credentials: 'same-origin' } diff --git a/config.js b/config.js index 83cb5089f..c6ea774a3 100644 --- a/config.js +++ b/config.js @@ -72,7 +72,7 @@ const CONFIG = { PORT: process.env.TALK_PORT || '3000', // The URL for this Talk Instance as viewable from the outside. - ROOT_URL: process.env.TALK_ROOT_URL, + ROOT_URL: process.env.TALK_ROOT_URL || null, // The keepalive timeout (in ms) that should be used to send keep alive // messages through the websocket to keep the socket alive. @@ -102,13 +102,22 @@ const CONFIG = { // DISABLE_AUTOFLAG_SUSPECT_WORDS is true when the suspect words that are // matched should not be flagged. - DISABLE_AUTOFLAG_SUSPECT_WORDS: process.env.TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS === 'TRUE' + DISABLE_AUTOFLAG_SUSPECT_WORDS: process.env.TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS === 'TRUE', + + // TRUST_THRESHOLDS defines the thresholds used for automoderation. + TRUST_THRESHOLDS: process.env.TRUST_THRESHOLDS || 'comment:-1,-1;flag:-1,-1' }; //============================================================================== // CONFIG VALIDATION //============================================================================== +if (process.env.NODE_ENV === 'test' && !CONFIG.ROOT_URL) { + CONFIG.ROOT_URL = 'http://localhost:3000'; +} else if (!CONFIG.ROOT_URL) { + throw new Error('TALK_ROOT_URL must be provided'); +} + //------------------------------------------------------------------------------ // JWT based configuration //------------------------------------------------------------------------------ diff --git a/docs/_docs/02-01-configuration.md b/docs/_docs/02-01-configuration.md index f8bb762fc..cb9a51d23 100644 --- a/docs/_docs/02-01-configuration.md +++ b/docs/_docs/02-01-configuration.md @@ -37,6 +37,15 @@ environment variables. Refer to the [config.js](https://github.com/coralproject/talk/blob/master/config.js) file to see how the configuration is parsed. +### Webpack + +These are only used during the webpack build. + +- `TALK_THREADING_LEVEL` (_optional_) - specify the maximum depth of the comment + thread. (Default `3`) +- `TALK_DEFAULT_STREAM_TAB` (_optional_) - specify the default stream tab in the + admin. (Default `all`) + ### Database - `TALK_MONGO_URL` (*required*) - the database connection string for the MongoDB database. @@ -91,6 +100,29 @@ on the contents of those variables.** - `TALK_RECAPTCHA_SECRET` (*required for reCAPTCHA support*) - server secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout. - `TALK_RECAPTCHA_PUBLIC` (*required for reCAPTCHA support*) - client secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout. +### Trust + +Trust can automoderate comments based on user history. By specifying this +option, the beheviour can be changed to offer different results. + +- `TRUST_THRESHOLDS` (_optional_) - configure the reliability thresholds for + flagging and commenting. (Default `comment:-1,-1;flag:-1,-1`) + +The form of the environment variable: + +``` +:,;:,;... +``` + +The default could be read as: + +- When a commenter has one comment rejected, their next comment must be + premoderated once in order to post freely again. If they instead get rejected + again, then they must have two of their comments approved in order to get + added back to the queue. +- At the moment of writing, beheviour is not attached to the flagging + reliability, but it is recorded. + ### Plugins - `TALK_PLUGINS_JSON` (_optional_) - used to specify the plugin config via the diff --git a/docs/_sass/minimal-mistakes/_masthead.scss b/docs/_sass/minimal-mistakes/_masthead.scss index 39ebd2c7d..2b24a2950 100755 --- a/docs/_sass/minimal-mistakes/_masthead.scss +++ b/docs/_sass/minimal-mistakes/_masthead.scss @@ -5,10 +5,10 @@ .masthead { position: relative; border-bottom: 1px solid $border-color; - -webkit-animation: intro 0.3s both; - animation: intro 0.3s both; - -webkit-animation-delay: 0.15s; - animation-delay: 0.15s; + // -webkit-animation: intro 0.3s both; + // animation: intro 0.3s both; + // -webkit-animation-delay: 0.15s; + // animation-delay: 0.15s; z-index: 20; &__inner-wrap { diff --git a/docs/_sass/minimal-mistakes/_navigation.scss b/docs/_sass/minimal-mistakes/_navigation.scss index 4c1748f97..c1b081891 100755 --- a/docs/_sass/minimal-mistakes/_navigation.scss +++ b/docs/_sass/minimal-mistakes/_navigation.scss @@ -14,10 +14,10 @@ padding-left: 2em; padding-right: 2em; font-family: $sans-serif; - -webkit-animation: intro 0.3s both; - animation: intro 0.3s both; - -webkit-animation-delay: 0.30s; - animation-delay: 0.30s; + // -webkit-animation: intro 0.3s both; + // animation: intro 0.3s both; + // -webkit-animation-delay: 0.30s; + // animation-delay: 0.30s; @include breakpoint($large) { padding-left: 1em; diff --git a/docs/_sass/minimal-mistakes/_page.scss b/docs/_sass/minimal-mistakes/_page.scss index 6b77b3262..bb185f02d 100755 --- a/docs/_sass/minimal-mistakes/_page.scss +++ b/docs/_sass/minimal-mistakes/_page.scss @@ -8,10 +8,10 @@ margin-top: 2em; padding-left: 1em; padding-right: 1em; - -webkit-animation: intro 0.3s both; - animation: intro 0.3s both; - -webkit-animation-delay: 0.35s; - animation-delay: 0.35s; + // -webkit-animation: intro 0.3s both; + // animation: intro 0.3s both; + // -webkit-animation-delay: 0.35s; + // animation-delay: 0.35s; @include breakpoint($x-large) { max-width: $x-large; @@ -113,10 +113,10 @@ position: relative; margin-bottom: 2em; @include clearfix; - -webkit-animation: intro 0.3s both; - animation: intro 0.3s both; - -webkit-animation-delay: 0.25s; - animation-delay: 0.25s; + // -webkit-animation: intro 0.3s both; + // animation: intro 0.3s both; + // -webkit-animation-delay: 0.25s; + // animation-delay: 0.25s; &--overlay { position: relative; @@ -126,10 +126,10 @@ background-size: cover; background-repeat: no-repeat; background-position: center; - -webkit-animation: intro 0.3s both; - animation: intro 0.3s both; - -webkit-animation-delay: 0.25s; - animation-delay: 0.25s; + // -webkit-animation: intro 0.3s both; + // animation: intro 0.3s both; + // -webkit-animation-delay: 0.25s; + // animation-delay: 0.25s; a { color: #fff; diff --git a/docs/_sass/minimal-mistakes/_sidebar.scss b/docs/_sass/minimal-mistakes/_sidebar.scss index 1470aa80f..2100e13e5 100755 --- a/docs/_sass/minimal-mistakes/_sidebar.scss +++ b/docs/_sass/minimal-mistakes/_sidebar.scss @@ -16,8 +16,8 @@ @include breakpoint($large) { @include span(2 of 12); opacity: 0.75; - -webkit-transition: opacity 0.2s ease-in-out; - transition: opacity 0.2s ease-in-out; + // -webkit-transition: opacity 0.2s ease-in-out; + // transition: opacity 0.2s ease-in-out; &:hover { opacity: 1; diff --git a/docs/_sass/minimal-mistakes/_variables.scss b/docs/_sass/minimal-mistakes/_variables.scss index 2b59066c2..a485b5579 100755 --- a/docs/_sass/minimal-mistakes/_variables.scss +++ b/docs/_sass/minimal-mistakes/_variables.scss @@ -91,7 +91,7 @@ $xing-color : #006567 !default; /* links */ -$link-color : $info-color !default; +$link-color : #f67150 !default; $link-color-hover : mix(#000, $link-color, 25%) !default; $link-color-visited : mix(#fff, $link-color, 25%) !default; $masthead-link-color : $primary-color !default; @@ -126,4 +126,4 @@ $border-radius : 4px !default; $box-shadow : 0 1px 1px rgba(0, 0, 0, 0.125) !default; $navicon-width : 1.5rem !default; $navicon-height : 0.25rem !default; -$global-transition : all 0.2s ease-in-out !default; +$global-transition : none !default; diff --git a/package.json b/package.json index 54a136cee..476e7eda5 100644 --- a/package.json +++ b/package.json @@ -178,6 +178,7 @@ "graphql-docs": "^0.2.0", "graphql-tag": "^1.2.3", "hammerjs": "^2.0.8", + "history": "^3.0.0", "ignore-styles": "^5.0.1", "immutable": "^3.8.1", "imports-loader": "^0.7.1", diff --git a/plugins/talk-plugin-facebook-auth/server/passport.js b/plugins/talk-plugin-facebook-auth/server/passport.js index ae69ec4bd..14849b7da 100644 --- a/plugins/talk-plugin-facebook-auth/server/passport.js +++ b/plugins/talk-plugin-facebook-auth/server/passport.js @@ -1,13 +1,20 @@ const FacebookStrategy = require('passport-facebook').Strategy; const UsersService = require('services/users'); const {ValidateUserLogin} = require('services/passport'); +let { + ROOT_URL +} = require('config'); + +if (ROOT_URL[ROOT_URL.length - 1] !== '/') { + ROOT_URL += '/'; +} module.exports = (passport) => { if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && process.env.TALK_ROOT_URL) { passport.use(new FacebookStrategy({ clientID: process.env.TALK_FACEBOOK_APP_ID, clientSecret: process.env.TALK_FACEBOOK_APP_SECRET, - callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`, + callbackURL: `${ROOT_URL}api/v1/auth/facebook/callback`, passReqToCallback: true, profileFields: ['id', 'displayName', 'picture.type(large)'] }, async (req, accessToken, refreshToken, profile, done) => { diff --git a/routes/admin/index.js b/routes/admin/index.js index 44ce83222..30bb69f5e 100644 --- a/routes/admin/index.js +++ b/routes/admin/index.js @@ -23,7 +23,7 @@ router.get('*', (req, res) => { TALK_RECAPTCHA_PUBLIC: RECAPTCHA_PUBLIC }; - res.render('admin', {basePath: '/client/coral-admin', data}); + res.render('admin', {data}); }); module.exports = router; diff --git a/services/karma.js b/services/karma.js index c76bcebb3..a2c087eaa 100644 --- a/services/karma.js +++ b/services/karma.js @@ -1,22 +1,25 @@ const debug = require('debug')('talk:services:karma'); const UserModel = require('../models/user'); +const { + TRUST_THRESHOLDS +} = require('../config'); /** * This will create an object with the property name of the action type as the * key and an object as it's value. This will contain a RELIABLE, and UNRELIABLE * property with the number of karma points associated with their particular * state. - * + * * If only the RELIABLE variable is provided, then it will also be used as the * UNRELIABLE variable. - * + * * The form of the environment variable is: - * + * * :,;:,;... - * + * * The default used is: - * - * comment:1,1;flag:-1,-1 + * + * comment:-1,-1;flag:-1,-1 */ const parseThresholds = (thresholds) => thresholds .split(';') @@ -50,16 +53,16 @@ const parseThresholds = (thresholds) => thresholds return acc; }, { comment: { - RELIABLE: -1, - UNRELIABLE: -1 + RELIABLE: 0, + UNRELIABLE: 0 }, flag: { - RELIABLE: -1, - UNRELIABLE: -1 + RELIABLE: 0, + UNRELIABLE: 0 } }); -const THRESHOLDS = parseThresholds(process.env.TRUST_THRESHOLDS || ''); +const THRESHOLDS = parseThresholds(TRUST_THRESHOLDS); debug('using thresholds: ', THRESHOLDS); diff --git a/views/admin.ejs b/views/admin.ejs index 54c666395..0a7c1c218 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -4,19 +4,19 @@ Talk - Coral Admin - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -37,10 +37,11 @@ <% if (data != null) { %> <% } %> +
- + diff --git a/views/article.ejs b/views/article.ejs index d31653baa..a98aa81c3 100644 --- a/views/article.ejs +++ b/views/article.ejs @@ -17,16 +17,17 @@ } <%= title %> +

<%= title %>

<%= body %>

-

Admin - All Assets

+

Admin - All Assets

- <% } %> +
- + diff --git a/webpack.config.js b/webpack.config.js index 21a70ad93..1b3c4b763 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -111,7 +111,7 @@ const config = { new webpack.EnvironmentPlugin({ 'TALK_PLUGINS_JSON': '{}', 'TALK_THREADING_LEVEL': '3', - 'TALK_DEFAULT_STREAM_TAB': 'all', + 'TALK_DEFAULT_STREAM_TAB': 'all' }) ], resolveLoader: {