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/README.md b/README.md index 718847d9e..a514d1ee1 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,10 @@ 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. +## Plugins + +Talk ships with a plugin architecture that allows developers to significantly extend the platform. For more information, see our [plugin documentation](docs/PLUGINS.md). + ## Supported Browsers ### Web @@ -58,6 +61,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..d0f0f316b 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,8 @@ 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 +33,8 @@ app.set('trust proxy', 1); app.use(helmet({ frameguard: false })); +app.use(compression()); +app.use(cookieParser()); app.use(bodyParser.json()); //============================================================================== diff --git a/client/coral-admin/src/actions/assets.js b/client/coral-admin/src/actions/assets.js index f5c2f848a..8a18a8e3f 100644 --- a/client/coral-admin/src/actions/assets.js +++ b/client/coral-admin/src/actions/assets.js @@ -8,7 +8,7 @@ import { UPDATE_ASSETS } from '../constants/assets'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; /** * Action disptacher related to assets diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 29971e294..ca48764f1 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,6 +1,7 @@ +import bowser from 'bowser'; import * as actions from '../constants/auth'; +import coralApi from 'coral-framework/helpers/request'; import * as Storage from 'coral-framework/helpers/storage'; -import coralApi from 'coral-framework/helpers/response'; import {handleAuthToken} from 'coral-framework/actions/auth'; //============================================================================== @@ -9,16 +10,31 @@ import {handleAuthToken} from 'coral-framework/actions/auth'; export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => { dispatch({type: actions.LOGIN_REQUEST}); - const params = {method: 'POST', body: {email, password}}; + + const params = { + method: 'POST', + body: { + email, + password + } + }; + if (recaptchaResponse) { - params.headers = {'X-Recaptcha-Response': recaptchaResponse}; + params.headers = { + 'X-Recaptcha-Response': recaptchaResponse + }; } + return coralApi('/auth/local', params) .then(({user, token}) => { + if (!user) { - Storage.removeItem('token'); + if (!bowser.safari && !bowser.ios) { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } + dispatch(handleAuthToken(token)); dispatch(checkLoginSuccess(user)); }) @@ -52,7 +68,9 @@ const forgotPassowordFailure = () => ({ export const requestPasswordReset = (email) => (dispatch) => { dispatch(forgotPassowordRequest(email)); - return coralApi('/account/password/reset', {method: 'POST', body: {email}}) + const redirectUri = location.href; + + return coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}}) .then(() => dispatch(forgotPassowordSuccess())) .catch((error) => dispatch(forgotPassowordFailure(error))); }; @@ -81,7 +99,9 @@ export const checkLogin = () => (dispatch) => { return coralApi('/auth') .then(({user}) => { if (!user) { - Storage.removeItem('token'); + if (!bowser.safari && !bowser.ios) { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js index 9a8743762..36b9ffb0a 100644 --- a/client/coral-admin/src/actions/community.js +++ b/client/coral-admin/src/actions/community.js @@ -14,7 +14,7 @@ import { HIDE_SUSPENDUSER_DIALOG } from '../constants/community'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; export const fetchAccounts = (query = {}) => (dispatch) => { diff --git a/client/coral-admin/src/actions/install.js b/client/coral-admin/src/actions/install.js index 21ff58de1..15413e220 100644 --- a/client/coral-admin/src/actions/install.js +++ b/client/coral-admin/src/actions/install.js @@ -1,4 +1,4 @@ -import coralApi from 'coral-framework/helpers/response'; +import coralApi from 'coral-framework/helpers/request'; import * as actions from '../constants/install'; import validate from 'coral-framework/helpers/validate'; import errorMsj from 'coral-framework/helpers/error'; diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js index b0530e77e..65c200420 100644 --- a/client/coral-admin/src/actions/settings.js +++ b/client/coral-admin/src/actions/settings.js @@ -1,4 +1,4 @@ -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; export const SETTINGS_LOADING = 'SETTINGS_LOADING'; export const SETTINGS_RECEIVED = 'SETTINGS_RECEIVED'; diff --git a/client/coral-admin/src/actions/users.js b/client/coral-admin/src/actions/users.js index 2642db90d..e888d5b74 100644 --- a/client/coral-admin/src/actions/users.js +++ b/client/coral-admin/src/actions/users.js @@ -1,4 +1,4 @@ -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; import * as userTypes from '../constants/users'; /** diff --git a/client/coral-admin/src/components/ui/Header.css b/client/coral-admin/src/components/ui/Header.css index eba94d892..7cb68ecbf 100644 --- a/client/coral-admin/src/components/ui/Header.css +++ b/client/coral-admin/src/components/ui/Header.css @@ -73,6 +73,8 @@ font-weight: 500; background-color: transparent; transition: background-color 200ms; + font-weight: 100; + letter-spacing: .8; &:hover { background-color: #232323; @@ -80,6 +82,7 @@ &.active { background-color: #232323; + font-weight: 400; } } diff --git a/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.css b/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.css index 6ca30866a..b684d0f4a 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.css +++ b/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.css @@ -3,12 +3,13 @@ background: #989797; margin: 2px; vertical-align: middle; - padding: 4px 7px; + padding: 1px 7px; border-radius: 2px; - margin-left: 10px; + margin-left: 2px; line-height: 20px; box-sizing: border-box; - height: 27px; + height: 21px; right: 0; margin-top: -2px; + font-size: 12px; } diff --git a/client/coral-admin/src/containers/ModerationQueue/components/ModerationHeader.js b/client/coral-admin/src/containers/ModerationQueue/components/ModerationHeader.js index cbe8b8cbf..c6909226b 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/ModerationHeader.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/ModerationHeader.js @@ -11,7 +11,7 @@ const ModerationHeader = (props) => ( props.asset ?
{t('modqueue.all_streams')} - + {props.asset.title} diff --git a/client/coral-admin/src/containers/ModerationQueue/components/styles.css b/client/coral-admin/src/containers/ModerationQueue/components/styles.css index 52800fe43..bfc79355d 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/styles.css +++ b/client/coral-admin/src/containers/ModerationQueue/components/styles.css @@ -18,18 +18,21 @@ .tab { flex: 1; - color: white; + color: #EEEEEE; text-transform: capitalize; - font-weight: 500; + font-weight: 100; font-size: 15px; letter-spacing: 1px; transition: border-bottom 200ms; + padding: 0px 5px; + margin-right: 30px; } .active { color: white; box-sizing: border-box; - border-bottom: solid 5px #F36451; + border-bottom: solid 4px #F36451; + font-weight: 400; } .active > span { @@ -97,7 +100,7 @@ span { flex: 1; color: white; text-transform: capitalize; - font-weight: 500; + font-weight: 400; font-size: 15px; letter-spacing: 1px; transition: opacity 200ms; @@ -371,6 +374,7 @@ span { padding: 10px 15px; box-sizing: border-box; border-radius: 2px; + bor box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12); > div { @@ -388,6 +392,7 @@ span { font-size: 13px; letter-spacing: 0.7px; font-weight: 400; + border-bottom: 0px; } label { @@ -421,7 +426,8 @@ span { .tabIcon { position: relative; - top: 7px; + top: 2px; + font-size: 16px; } .username { diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js index 9a532856e..0af9a4a5a 100644 --- a/client/coral-admin/src/index.js +++ b/client/coral-admin/src/index.js @@ -8,6 +8,9 @@ import store from './services/store'; import App from './components/App'; import 'react-mdl/extra/material.js'; +import {loadPluginsTranslations} from 'coral-framework/helpers/plugins'; + +loadPluginsTranslations(); render( diff --git a/client/coral-embed-stream/src/AppRouter.js b/client/coral-embed-stream/src/AppRouter.js index e2553889c..6ee63caab 100644 --- a/client/coral-embed-stream/src/AppRouter.js +++ b/client/coral-embed-stream/src/AppRouter.js @@ -2,12 +2,12 @@ import React from 'react'; import {Router, Route, browserHistory} from 'react-router'; import Embed from './containers/Embed'; -import SignInContainer from 'coral-sign-in/containers/SignInContainer'; +import {LoginContainer} from 'coral-sign-in/containers/LoginContainer'; const routes = (
- - + +
); diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 7030caf74..3c94d5f81 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,72 +1,67 @@ import React from 'react'; -import t from 'coral-framework/services/i18n'; +import Stream from '../containers/Stream'; +import Slot from 'coral-framework/components/Slot'; import {can} from 'coral-framework/services/perms'; +import t from 'coral-framework/services/i18n'; import {TabBar, Tab, TabContent, Button} from 'coral-ui'; - -import Stream from '../containers/Stream'; import Count from 'coral-plugin-comment-count/CommentCount'; -import UserBox from 'coral-sign-in/components/UserBox'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; -import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer'; +import ConfigureStreamContainer + from 'coral-configure/containers/ConfigureStreamContainer'; export default class Embed extends React.Component { changeTab = (tab) => { - switch(tab) { + switch (tab) { case 0: this.props.setActiveTab('stream'); break; case 1: this.props.setActiveTab('profile'); - // TODO: move data fetching to profile container. + // TODO: move data fetching to profile container. this.props.data.refetch(); break; case 2: this.props.setActiveTab('config'); - // TODO: move data fetching to config container. + // TODO: move data fetching to config container. this.props.data.refetch(); break; } - } + }; handleShowProfile = () => this.props.setActiveTab('profile'); - render () { - const {activeTab, logout, viewAllComments, commentId} = this.props; + render() { + const {activeTab, viewAllComments, commentId} = this.props; const {asset: {totalCommentCount}} = this.props.root; - const {loggedIn, user} = this.props.auth; - - const userBox = ; + const {user} = this.props.auth; return (
- + {t('framework.my_profile')} {t('framework.configure_stream')} - { - commentId && - - } + {commentId && + } + - { loggedIn ? userBox : null } - { loggedIn ? userBox : null }
@@ -79,5 +74,5 @@ Embed.propTypes = { data: React.PropTypes.shape({ loading: React.PropTypes.bool, error: React.PropTypes.object - }).isRequired, + }).isRequired }; diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index d349d8d6f..d93044c9e 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -1,20 +1,18 @@ import React, {PropTypes} from 'react'; - -import {Button} from 'coral-ui'; import LoadMore from './LoadMore'; import NewCount from './NewCount'; + import Comment from '../containers/Comment'; +import SuspendedAccount from './SuspendedAccount'; +import RestrictedMessageBox + from 'coral-framework/components/RestrictedMessageBox'; +import Slot from 'coral-framework/components/Slot'; import InfoBox from 'coral-plugin-infobox/InfoBox'; +import {can} from 'coral-framework/services/perms'; import {ModerationLink} from 'coral-plugin-moderation'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; import QuestionBox from 'coral-plugin-questionbox/QuestionBox'; import IgnoredCommentTombstone from './IgnoredCommentTombstone'; -import SuspendedAccount from './SuspendedAccount'; -import RestrictedMessageBox - from 'coral-framework/components/RestrictedMessageBox'; -import {can} from 'coral-framework/services/perms'; -import ChangeUsernameContainer - from 'coral-sign-in/containers/ChangeUsernameContainer'; import t, {timeago} from 'coral-framework/services/i18n'; class Stream extends React.Component { @@ -52,7 +50,10 @@ class Stream extends React.Component { : comment; const banned = user && user.status === 'BANNED'; - const temporarilySuspended = user && user.suspension.until && new Date(user.suspension.until) > new Date(); + const temporarilySuspended = + user && + user.suspension.until && + new Date(user.suspension.until) > new Date(); const hasOlderComments = !!(asset && asset.lastComment && @@ -63,10 +64,15 @@ class Stream extends React.Component { ? asset.comments[0].created_at : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); const commentIsIgnored = (comment) => { - return me && me.ignoredUsers && me.ignoredUsers.find((u) => u.id === comment.user.id); + return ( + me && + me.ignoredUsers && + me.ignoredUsers.find((u) => u.id === comment.user.id) + ); }; return (
+ {open ?
- {!banned && temporarilySuspended && + {!banned && + temporarilySuspended && - { - t('sign_in.temporarily_suspended', - this.props.root.settings.organizationName, - timeago(user.suspension.until), - ) - } - - } + {t( + 'stream.temporarily_suspended', + this.props.root.settings.organizationName, + timeago(user.suspension.until) + )} + } {banned && - } - {loggedIn && !banned && !temporarilySuspended && + />} + {loggedIn && + !banned && + !temporarilySuspended && - } + addNotification={this.props.addNotification} + postComment={this.props.postComment} + appendItemArray={this.props.appendItemArray} + updateItem={this.props.updateItem} + setCommentCountCache={this.props.setCommentCountCache} + commentCountCache={commentCountCache} + assetId={asset.id} + premod={asset.settings.moderation} + isReply={false} + authorId={user.id} + charCountEnable={asset.settings.charCountEnable} + maxCharCount={asset.settings.charCount} + />}
:

{asset.settings.closedMessage}

} - {!loggedIn && - } {loggedIn && - user && - } - {loggedIn && } + } {/* the highlightedComment is isolated after the user followed a permalink */} {highlightedComment @@ -160,41 +158,38 @@ class Stream extends React.Component { setCommentCountCache={this.props.setCommentCountCache} />
- {comments.map( - (comment) => { - return (commentIsIgnored(comment) - ? - : - ); - } - )} + {comments.map((comment) => { + return commentIsIgnored(comment) + ? + : ; + })}
(dispatch) => { +export const showSignInDialog = () => (dispatch, getState) => { const signInPopUp = window.open( '/embed/stream/login', 'Login', @@ -19,6 +19,10 @@ export const showSignInDialog = () => (dispatch) => { let loaded = false; signInPopUp.onload = () => { loaded = true; + + // Fire some actions inside the popups reducer, to initialize required state. + const required = getState().asset.toJS().settings.requireEmailConfirmation; + signInPopUp.coralStore.dispatch(setRequireEmailVerification(required)); }; // Use `onunload` instead of `onbeforeunload` which is not supported in IOS Safari. @@ -94,6 +98,11 @@ export const cleanState = () => ({ type: actions.CLEAN_STATE }); +export const setRequireEmailVerification = (required) => ({ + type: actions.SET_REQUIRE_EMAIL_VERIFICATION, + required, +}); + // Sign In Actions const signInRequest = () => ({ @@ -119,26 +128,31 @@ export const handleAuthToken = (token) => (dispatch) => { // SIGN IN //============================================================================== -export const fetchSignIn = (formData) => (dispatch) => { - dispatch(signInRequest()); - return coralApi('/auth/local', {method: 'POST', body: formData}) - .then(({token}) => { - dispatch(handleAuthToken(token)); - dispatch(hideSignInDialog()); - }) - .catch((error) => { - if (error.metadata) { +export const fetchSignIn = (formData) => { + return (dispatch) => { + dispatch(signInRequest()); - // the user might not have a valid email. prompt the user user re-request the confirmation email - dispatch( - signInFailure(t('error.email_not_verified', error.metadata)) - ); - } else { + return coralApi('/auth/local', {method: 'POST', body: formData}) + .then(({token}) => { + if (!bowser.safari && !bowser.ios) { + dispatch(handleAuthToken(token)); + } + dispatch(hideSignInDialog()); + }) + .catch((error) => { + if (error.metadata) { - // invalid credentials - dispatch(signInFailure(t('error.email_password'))); - } - }); + // the user might not have a valid email. prompt the user user re-request the confirmation email + dispatch( + signInFailure(t('error.email_not_verified', error.metadata)) + ); + } else { + + // invalid credentials + dispatch(signInFailure(t('error.email_password'))); + } + }); + }; }; //============================================================================== @@ -185,7 +199,7 @@ export const fetchSignUpFacebook = () => (dispatch) => { ); }; -export const facebookCallback = (err, data) => (dispatch, getState) => { +export const facebookCallback = (err, data) => (dispatch) => { if (err) { dispatch(signInFacebookFailure(err)); return; @@ -194,10 +208,6 @@ export const facebookCallback = (err, data) => (dispatch, getState) => { dispatch(handleAuthToken(data.token)); dispatch(signInFacebookSuccess(data.user)); dispatch(hideSignInDialog()); - const {user: {canEditName, status}} = getState().auth.toJS(); - if (canEditName && status !== 'BANNED') { - dispatch(showCreateUsernameDialog()); - } } catch (err) { dispatch(signInFacebookFailure(err)); return; @@ -267,7 +277,9 @@ export const fetchForgotPassword = (email) => (dispatch) => { export const logout = () => (dispatch) => { return coralApi('/auth', {method: 'DELETE'}).then(() => { - Storage.removeItem('token'); + if (!bowser.safari && !bowser.ios) { + Storage.removeItem('token'); + } dispatch({type: actions.LOGOUT}); }); }; @@ -290,11 +302,18 @@ export const checkLogin = () => (dispatch) => { coralApi('/auth') .then((result) => { if (!result.user) { - Storage.removeItem('token'); + if (!bowser.safari && !bowser.ios) { + Storage.removeItem('token'); + } throw new Error('Not logged in'); } dispatch(checkLoginSuccess(result.user)); + + // Display create username dialog if necessary. + if (result.user.canEditName && result.user.status !== 'BANNED') { + dispatch(showCreateUsernameDialog()); + } }) .catch((error) => { console.error(error); diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index 53d59a743..b978f686d 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,5 +1,5 @@ import {addNotification} from '../actions/notification'; -import coralApi from '../helpers/response'; +import coralApi from '../helpers/request'; import * as actions from '../constants/auth'; import t from 'coral-framework/services/i18n'; diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index 24599ac50..a9ac52739 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -46,3 +46,6 @@ export const VERIFY_EMAIL_REQUEST = 'VERIFY_EMAIL_REQUEST'; export const VERIFY_EMAIL_SUCCESS = 'VERIFY_EMAIL_SUCCESS'; export const VERIFY_EMAIL_FAILURE = 'VERIFY_EMAIL_FAILURE'; export const UPDATE_USERNAME = 'UPDATE_USERNAME'; + +export const SET_REQUIRE_EMAIL_VERIFICATION = 'SET_REQUIRE_EMAIL_VERIFICATION'; + diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index a70c6f6f6..02ee8c3a7 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -6,6 +6,7 @@ import uniq from 'lodash/uniq'; import pick from 'lodash/pick'; import plugins from 'pluginsConfig'; import {getDefinitionName, mergeDocuments} from 'coral-framework/utils'; +import {loadTranslations} from 'coral-framework/services/i18n'; export const pluginReducers = merge( ...plugins @@ -88,9 +89,12 @@ export function getGraphQLExtensions() { .filter((o) => o); } -export function getTranslations() { +function getTranslations() { return plugins .map((o) => o.module.translations) .filter((o) => o); } +export function loadPluginsTranslations() { + getTranslations().forEach((t) => loadTranslations(t)); +} diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/request.js similarity index 75% rename from client/coral-framework/helpers/response.js rename to client/coral-framework/helpers/request.js index 794a73e30..0e15003e1 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/request.js @@ -1,22 +1,26 @@ +import bowser from 'bowser'; import * as Storage from './storage'; +import merge from 'lodash/merge'; const buildOptions = (inputOptions = {}) => { const defaultOptions = { method: 'GET', headers: { Accept: 'application/json', - Authorization: `Bearer ${Storage.getItem('token')}`, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, credentials: 'same-origin' }; - let options = Object.assign({}, defaultOptions, inputOptions); - options.headers = Object.assign( - {}, - defaultOptions.headers, - inputOptions.headers - ); + let options = merge({}, defaultOptions, inputOptions); + + if (!bowser.safari && !bowser.ios) { + let authorization = Storage.getItem('token'); + + if (authorization) { + options.headers.Authorization = `Bearer ${authorization}`; + } + } if (options.method.toLowerCase() !== 'get') { options.body = JSON.stringify(options.body); diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 8fa4ed75b..7a1744d59 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -16,7 +16,8 @@ const initialState = Map({ emailVerificationLoading: false, emailVerificationSuccess: false, successSignUp: false, - fromSignUp: false + fromSignUp: false, + requireEmailConfirmation: false, }); const purge = (user) => { @@ -142,6 +143,9 @@ export default function auth (state = initialState, action) { return state .set('emailVerificationSuccess', true) .set('emailVerificationLoading', false); + case actions.SET_REQUIRE_EMAIL_VERIFICATION: + return state + .set('requireEmailConfirmation', action.required); default : return state; } diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 882127972..6a9b07d05 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -6,7 +6,6 @@ import merge from 'lodash/merge'; import esTA from '../../../node_modules/timeago.js/locales/es'; import en from '../../../locales/en.yml'; import es from '../../../locales/es.yml'; -import * as plugins from '../helpers/plugins'; // Translations are happening at https://www.transifex.com/the-coral-project/talk-1/dashboard/. @@ -15,7 +14,6 @@ const translations = {...en, ...es}; let lang; let timeagoInstance; -let loadedPluginsTranslations = false; function setLocale(locale) { try { @@ -45,10 +43,6 @@ function init() { timeagoInstance = ta(); } -function loadPluginsTranslations() { - plugins.getTranslations().forEach((t) => loadTranslations(t)); -} - export function loadTranslations(newTranslations) { merge(translations, newTranslations); } @@ -67,10 +61,6 @@ export function timeago(time) { * any extra parameters are optional and replace a variable marked by {0}, {1}, etc in the translation. */ export function t(key, ...replacements) { - if (!loadedPluginsTranslations) { - loadPluginsTranslations(); - loadedPluginsTranslations = true; - } const fullKey = `${lang}.${key}`; if (has(translations, fullKey)) { let translation = get(translations, fullKey); diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js index e421ca3da..0c430a021 100644 --- a/client/coral-framework/services/transport.js +++ b/client/coral-framework/services/transport.js @@ -1,5 +1,6 @@ import {createNetworkInterface} from 'apollo-client'; import * as Storage from '../helpers/storage'; +import bowser from 'bowser'; //============================================================================== // NETWORK INTERFACE @@ -21,7 +22,11 @@ networkInterface.use([{ if (!req.options.headers) { req.options.headers = {}; // Create the header object if needed. } - req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`; + + if (!bowser.safari && !bowser.ios) { + req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`; + } + next(); } }]); diff --git a/client/coral-plugin-history/Comment.css b/client/coral-plugin-history/Comment.css index bc2134639..4e827cdde 100644 --- a/client/coral-plugin-history/Comment.css +++ b/client/coral-plugin-history/Comment.css @@ -13,10 +13,10 @@ } .assetURL { - font-size: 16px; - color: black; text-decoration: none; font-weight: bold; + font-size: 12px; + color: #2c3e50; } .commentBody { @@ -32,7 +32,7 @@ } li { - margin-bottom: 10px; + margin-bottom: 5px; &:nth-child(1) { color: #5394D7; @@ -46,7 +46,6 @@ i { margin-right: 5px; font-size: 15px; - vertical-align: bottom; } a:hover { diff --git a/client/coral-settings/components/NotLoggedIn.js b/client/coral-settings/components/NotLoggedIn.js index 38bd15977..3531b4a6a 100644 --- a/client/coral-settings/components/NotLoggedIn.js +++ b/client/coral-settings/components/NotLoggedIn.js @@ -6,7 +6,7 @@ import t from 'coral-framework/services/i18n'; export default ({showSignInDialog}) => (
- {t('sign_in')} {t('toAccess')} + {t('settings.sign_in')} {t('settings.to_access')}
{t('from_settings_page')} diff --git a/client/coral-sign-in/components/FakeComment.js b/client/coral-sign-in/components/FakeComment.js deleted file mode 100644 index b73532591..000000000 --- a/client/coral-sign-in/components/FakeComment.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import styles from 'coral-embed-stream/src/components/Comment.css'; - -import AuthorName from 'coral-plugin-author-name/AuthorName'; -import Content from 'coral-plugin-commentcontent/CommentContent'; -import PubDate from 'coral-plugin-pubdate/PubDate'; -import {ReplyButton} from 'coral-plugin-replies'; -import t from 'coral-framework/services/i18n'; - -class FakeComment extends React.Component { - constructor (props) { - super(props); - } - - render () { - const {username, created_at, body} = this.props; - - return ( -
-
- - - -
-
- -
- {}} - parentCommentId={'commentID'} - currentUserId={{}} - banned={false} - /> -
-
-
- -
-
- -
-
-
- ); - } -} - -export default FakeComment; diff --git a/client/coral-sign-in/components/ForgotContent.js b/client/coral-sign-in/components/ForgotContent.js deleted file mode 100644 index 502335c01..000000000 --- a/client/coral-sign-in/components/ForgotContent.js +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import styles from './styles.css'; -import Button from 'coral-ui/components/Button'; -import t from 'coral-framework/services/i18n'; - -class ForgotContent extends React.Component { - constructor (props) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit (e) { - e.preventDefault(); - this.props.fetchForgotPassword(this.emailInput.value); - } - - render () { - const {changeView, auth} = this.props; - const {passwordRequestSuccess, passwordRequestFailure} = auth; - - return ( -
-
-

{t('sign_in.recover_password')}

-
-
-
- - this.emailInput = input} - type="text" - style={{fontSize: 16}} - id="email" - name="email" /> -
- - { - passwordRequestSuccess - ?

{passwordRequestSuccess}

- : null - } - { - passwordRequestFailure - ?

{passwordRequestFailure}

- : null - } -
-
- {t('sign_in.need_an_account')} changeView('SIGNUP')}>{t('sign_in.register')} - {t('sign_in.already_have_an_account')} changeView('SIGNIN')}>{t('sign_in.sign_in')} -
-
- ); - } -} - -export default ForgotContent; diff --git a/client/coral-sign-in/components/UserBox.js b/client/coral-sign-in/components/UserBox.js deleted file mode 100644 index 4fe420cfc..000000000 --- a/client/coral-sign-in/components/UserBox.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import styles from './styles.css'; -import t from 'coral-framework/services/i18n'; - -const UserBox = ({className, user, onLogout, onShowProfile}) => ( -
- {t('sign_in.logged_in_as')} - {user.username}. {t('sign_in.not_you')} - {t('sign_in.logout')} -
-); - -export default UserBox; diff --git a/client/coral-sign-in/containers/LoginContainer.js b/client/coral-sign-in/containers/LoginContainer.js new file mode 100644 index 000000000..8d19e5198 --- /dev/null +++ b/client/coral-sign-in/containers/LoginContainer.js @@ -0,0 +1,6 @@ +import React from 'react'; +import Slot from 'coral-framework/components/Slot'; + +export const LoginContainer = () => ( + +); diff --git a/client/coral-ui/components/TextField.css b/client/coral-ui/components/TextField.css index 33b1389e7..5682748be 100644 --- a/client/coral-ui/components/TextField.css +++ b/client/coral-ui/components/TextField.css @@ -18,6 +18,7 @@ box-sizing: border-box; border-radius: 2px; margin: 5px auto; + font-size: 14px; } input.error{ diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..b33f7679f --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +_site/ +.sass-cache/ +.jekyll-metadata +_pdf +.idea/ +.DS_Store diff --git a/docs/.vscode/settings.json b/docs/.vscode/settings.json new file mode 100644 index 000000000..5af001d55 --- /dev/null +++ b/docs/.vscode/settings.json @@ -0,0 +1,6 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.associations": { + "*.html": "liquid" + } +} diff --git a/docs/404.md b/docs/404.md new file mode 100644 index 000000000..a7b58c002 --- /dev/null +++ b/docs/404.md @@ -0,0 +1,6 @@ +--- +title: "Page Not Found" +search: exclude +--- + +Sorry, but the page you were trying to view does not exist. Try searching for it or looking at the URL to see if it looks correct. diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 000000000..b1fa52c47 --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,26 @@ +FROM ruby:2.1 +MAINTAINER mrafayaleem@gmail.com + +RUN apt-get clean \ + && mv /var/lib/apt/lists /var/lib/apt/lists.broke \ + && mkdir -p /var/lib/apt/lists/partial + +RUN apt-get update + +RUN apt-get install -y \ + node \ + python-pygments \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/ + +WORKDIR /tmp +ADD Gemfile /tmp/ +ADD Gemfile.lock /tmp/ +RUN bundle install + +VOLUME /src +EXPOSE 4000 + +WORKDIR /src +ENTRYPOINT ["jekyll"] + diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 000000000..d2e1a4527 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gem 'github-pages', group: :jekyll_plugins +gem 'wdm', '>= 0.1.0' if Gem.win_platform? diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock new file mode 100644 index 000000000..04ce5e261 --- /dev/null +++ b/docs/Gemfile.lock @@ -0,0 +1,199 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.2.8) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + colorator (1.1.0) + ethon (0.10.1) + ffi (>= 1.3.0) + execjs (2.7.0) + faraday (0.12.1) + multipart-post (>= 1.2, < 3) + ffi (1.9.18) + forwardable-extended (2.6.0) + gemoji (3.0.0) + github-pages (138) + activesupport (= 4.2.8) + github-pages-health-check (= 1.3.3) + jekyll (= 3.4.3) + jekyll-avatar (= 0.4.2) + jekyll-coffeescript (= 1.0.1) + jekyll-default-layout (= 0.1.4) + jekyll-feed (= 0.9.2) + jekyll-gist (= 1.4.0) + jekyll-github-metadata (= 2.3.1) + jekyll-mentions (= 1.2.0) + jekyll-optional-front-matter (= 0.1.2) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.1.0) + jekyll-redirect-from (= 0.12.1) + jekyll-relative-links (= 0.4.0) + jekyll-sass-converter (= 1.5.0) + jekyll-seo-tag (= 2.2.3) + jekyll-sitemap (= 1.0.0) + jekyll-swiss (= 0.4.0) + jekyll-theme-architect (= 0.0.4) + jekyll-theme-cayman (= 0.0.4) + jekyll-theme-dinky (= 0.0.4) + jekyll-theme-hacker (= 0.0.4) + jekyll-theme-leap-day (= 0.0.4) + jekyll-theme-merlot (= 0.0.4) + jekyll-theme-midnight (= 0.0.4) + jekyll-theme-minimal (= 0.0.4) + jekyll-theme-modernist (= 0.0.4) + jekyll-theme-primer (= 0.1.8) + jekyll-theme-slate (= 0.0.4) + jekyll-theme-tactile (= 0.0.4) + jekyll-theme-time-machine (= 0.0.4) + jekyll-titles-from-headings (= 0.1.5) + jemoji (= 0.8.0) + kramdown (= 1.13.2) + liquid (= 3.0.6) + listen (= 3.0.6) + mercenary (~> 0.3) + minima (= 2.1.1) + rouge (= 1.11.1) + terminal-table (~> 1.4) + github-pages-health-check (1.3.3) + addressable (~> 2.3) + net-dns (~> 0.8) + octokit (~> 4.0) + public_suffix (~> 2.0) + typhoeus (~> 0.7) + html-pipeline (2.6.0) + activesupport (>= 2) + nokogiri (>= 1.4) + i18n (0.8.1) + jekyll (3.4.3) + addressable (~> 2.4) + colorator (~> 1.0) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 1.1) + kramdown (~> 1.3) + liquid (~> 3.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (~> 1.7) + safe_yaml (~> 1.0) + jekyll-avatar (0.4.2) + jekyll (~> 3.0) + jekyll-coffeescript (1.0.1) + coffee-script (~> 2.2) + jekyll-default-layout (0.1.4) + jekyll (~> 3.0) + jekyll-feed (0.9.2) + jekyll (~> 3.3) + jekyll-gist (1.4.0) + octokit (~> 4.2) + jekyll-github-metadata (2.3.1) + jekyll (~> 3.1) + octokit (~> 4.0, != 4.4.0) + jekyll-mentions (1.2.0) + activesupport (~> 4.0) + html-pipeline (~> 2.3) + jekyll (~> 3.0) + jekyll-optional-front-matter (0.1.2) + jekyll (~> 3.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.1.0) + jekyll (~> 3.0) + jekyll-redirect-from (0.12.1) + jekyll (~> 3.3) + jekyll-relative-links (0.4.0) + jekyll (~> 3.3) + jekyll-sass-converter (1.5.0) + sass (~> 3.4) + jekyll-seo-tag (2.2.3) + jekyll (~> 3.3) + jekyll-sitemap (1.0.0) + jekyll (~> 3.3) + jekyll-swiss (0.4.0) + jekyll-theme-architect (0.0.4) + jekyll (~> 3.3) + jekyll-theme-cayman (0.0.4) + jekyll (~> 3.3) + jekyll-theme-dinky (0.0.4) + jekyll (~> 3.3) + jekyll-theme-hacker (0.0.4) + jekyll (~> 3.3) + jekyll-theme-leap-day (0.0.4) + jekyll (~> 3.3) + jekyll-theme-merlot (0.0.4) + jekyll (~> 3.3) + jekyll-theme-midnight (0.0.4) + jekyll (~> 3.3) + jekyll-theme-minimal (0.0.4) + jekyll (~> 3.3) + jekyll-theme-modernist (0.0.4) + jekyll (~> 3.3) + jekyll-theme-primer (0.1.8) + jekyll (~> 3.3) + jekyll-theme-slate (0.0.4) + jekyll (~> 3.3) + jekyll-theme-tactile (0.0.4) + jekyll (~> 3.3) + jekyll-theme-time-machine (0.0.4) + jekyll (~> 3.3) + jekyll-titles-from-headings (0.1.5) + jekyll (~> 3.3) + jekyll-watch (1.5.0) + listen (~> 3.0, < 3.1) + jemoji (0.8.0) + activesupport (~> 4.0) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (>= 3.0) + kramdown (1.13.2) + liquid (3.0.6) + listen (3.0.6) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9.7) + mercenary (0.3.6) + mini_portile2 (2.1.0) + minima (2.1.1) + jekyll (~> 3.3) + minitest (5.10.2) + multipart-post (2.0.0) + net-dns (0.8.0) + nokogiri (1.6.8.1) + mini_portile2 (~> 2.1.0) + octokit (4.7.0) + sawyer (~> 0.8.0, >= 0.5.3) + pathutil (0.14.0) + forwardable-extended (~> 2.6) + public_suffix (2.0.5) + rb-fsevent (0.9.8) + rb-inotify (0.9.8) + ffi (>= 0.5.0) + rouge (1.11.1) + safe_yaml (1.0.4) + sass (3.4.24) + sawyer (0.8.1) + addressable (>= 2.3.5, < 2.6) + faraday (~> 0.8, < 1.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + typhoeus (0.8.0) + ethon (>= 0.8.0) + tzinfo (1.2.3) + thread_safe (~> 0.1) + unicode-display_width (1.2.1) + +PLATFORMS + ruby + +DEPENDENCIES + github-pages + +BUNDLED WITH + 1.15.0 diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..96ae10d52 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,102 @@ +repository: coralproject/talk + +output: web +# this property is useful for conditional filtering of content that is separate from the PDF. + +topnav_title: Coral Talk Documentation +# this appears on the top navigation bar next to the home button + +site_title: Coral Talk Documentation +# this appears in the html browser tab for the site title (seen mostly by search engines, not users) + +company_name: The Coral Project +# this appears in the footer + +github_editme_path: +# if you're using Github, provide the basepath to the branch you've created for reviews, following the sample here. if not, leave this value blank. + +disqus_shortname: +# if you're using disqus for comments, add the shortname here. if not, leave this value blank. + +host: 127.0.0.1 +# the preview server used. Leave as is. + +port: 4000 +# the port where the preview is rendered. You can leave this as is unless you have other Jekyll builds using this same port that might cause conflicts. in that case, use another port such as 4006. + +exclude: + - .idea/ + - .gitignore +# these are the files and directories that jekyll will exclude from the build + +feedback_subject_line: + +feedback_email: +# used as a contact email for the Feedback link in the top navigation bar + + feedback_disable: true +# if you uncomment the previous line, the Feedback link gets removed + +# feedback_text: "Need help?" +# if you uncomment the previous line, it changes the Feedback text + +# feedback_link: "http://helpy.io/" +# if you uncomment the previous line, it changes where the feedback link points to + +highlighter: rouge +# library used for syntax highlighting + +markdown: kramdown +kramdown: + input: GFM + auto_ids: true + hard_wrap: false + syntax_highlighter: rouge + +# filter used to process markdown. note that kramdown differs from github-flavored markdown in some subtle ways + +collections: + tooltips: + output: false +# collections are declared here. this renders the content in _tooltips and processes it, but doesn't output it as actual files in the output unless you change output to true + +defaults: + - + scope: + path: "" + type: "pages" + values: + layout: "page" + comments: true + search: true + sidebar: talk_sidebar + - + scope: + path: "" + type: "tooltips" + values: + layout: "page" + comments: true + search: true + tooltip: true + + - + scope: + path: "" + type: "posts" + values: + layout: "post" + comments: true + search: true + sidebar: home_sidebar + +# these are defaults used for the frontmatter for these file types + +sidebars: +- talk_sidebar + +description: "Documentation and guides for Coral Talk." +# the description is used in the feed.xml file + +# needed for sitemap.xml file only +url: https://coralproject.github.io/talk/ diff --git a/docs/_data/alerts.yml b/docs/_data/alerts.yml new file mode 100644 index 000000000..157e1622b --- /dev/null +++ b/docs/_data/alerts.yml @@ -0,0 +1,15 @@ +tip: '