From 87ada30eff70bd580ae6fdb6885a682b22563a35 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Fri, 19 May 2017 17:32:16 -0300 Subject: [PATCH 01/47] =?UTF-8?q?=C3=81dding=20Stream=20Slot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Embed.js | 11 +-- .../src/components/Stream.js | 94 +++++++++---------- 2 files changed, 51 insertions(+), 54 deletions(-) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 0d26cf740..2e050e836 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,13 +1,12 @@ import React from 'react'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-framework/translations'; const lang = new I18n(translations); - -import {TabBar, Tab, TabContent, Button} from 'coral-ui'; - import Stream from '../containers/Stream'; -import Count from 'coral-plugin-comment-count/CommentCount'; +import Slot from 'coral-framework/components/Slot'; +import I18n from 'coral-framework/modules/i18n/i18n'; import UserBox from 'coral-sign-in/components/UserBox'; +import translations from 'coral-framework/translations'; +import {TabBar, Tab, TabContent, Button} from 'coral-ui'; +import Count from 'coral-plugin-comment-count/CommentCount'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer'; diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index 90fa0aca8..e0c541341 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -4,6 +4,7 @@ import {Button} from 'coral-ui'; import LoadMore from './LoadMore'; import NewCount from './NewCount'; import Comment from '../containers/Comment'; +import Slot from 'coral-framework/components/Slot'; import InfoBox from 'coral-plugin-infobox/InfoBox'; import {ModerationLink} from 'coral-plugin-moderation'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; @@ -15,7 +16,7 @@ import ChangeUsernameContainer from 'coral-sign-in/containers/ChangeUsernameContainer'; class Stream extends React.Component { - setActiveReplyBox = (reactKey) => { + setActiveReplyBox = reactKey => { if (!this.props.auth.user) { this.props.showSignInDialog(); } else { @@ -58,11 +59,20 @@ class Stream extends React.Component { const firstCommentDate = asset.comments[0] ? 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); + const commentIsIgnored = comment => { + return ( + me && + me.ignoredUsers && + me.ignoredUsers.find(u => u.id === comment.user.id) + ); }; return (
+ + + {open ?
:

{asset.settings.closedMessage}

} - {!loggedIn && - } {loggedIn && user && } {loggedIn && } - {/* the highlightedComment is isolated after the user followed a permalink */} {highlightedComment ?
- {comments.map( - (comment) => { - return (commentIsIgnored(comment) - ? - : - ); - } - )} + {comments.map(comment => { + return commentIsIgnored(comment) + ? + : ; + })}
Date: Fri, 19 May 2017 17:32:54 -0300 Subject: [PATCH 02/47] Adding coral-plugin-auth --- .gitignore | 1 + plugins/coral-plugin-auth/client/.babelrc | 14 +++++++++++ .../coral-plugin-auth/client/.eslintrc.json | 23 +++++++++++++++++++ .../client/components/SignInButton.js | 22 ++++++++++++++++++ plugins/coral-plugin-auth/client/index.js | 7 ++++++ .../client/translations.json | 10 ++++++++ plugins/coral-plugin-auth/index.js | 1 + 7 files changed, 78 insertions(+) create mode 100644 plugins/coral-plugin-auth/client/.babelrc create mode 100644 plugins/coral-plugin-auth/client/.eslintrc.json create mode 100644 plugins/coral-plugin-auth/client/components/SignInButton.js create mode 100644 plugins/coral-plugin-auth/client/index.js create mode 100644 plugins/coral-plugin-auth/client/translations.json create mode 100644 plugins/coral-plugin-auth/index.js 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/coral-plugin-auth/client/.babelrc b/plugins/coral-plugin-auth/client/.babelrc new file mode 100644 index 000000000..60be246eb --- /dev/null +++ b/plugins/coral-plugin-auth/client/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + "es2015" + ], + "plugins": [ + "add-module-exports", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-react-jsx" + ] +} \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/.eslintrc.json b/plugins/coral-plugin-auth/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/plugins/coral-plugin-auth/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/plugins/coral-plugin-auth/client/components/SignInButton.js b/plugins/coral-plugin-auth/client/components/SignInButton.js new file mode 100644 index 000000000..fb58eeb9b --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/SignInButton.js @@ -0,0 +1,22 @@ +import React from 'react'; +import {Button} from 'coral-ui'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {showSignInDialog} from 'coral-framework/actions/auth'; + +class SignInButton extends React.Component { + render() { + return ( + + ); + } +} + +const mapStateToProps = ({auth}) => ({auth}); + +const mapDispatchToProps = dispatch => + bindActionCreators({showSignInDialog}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(SignInButton); diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js new file mode 100644 index 000000000..ff601992b --- /dev/null +++ b/plugins/coral-plugin-auth/client/index.js @@ -0,0 +1,7 @@ +import SignInButton from './components/SignInButton'; + +export default { + slots: { + stream: [SignInButton] + } +}; \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/translations.json b/plugins/coral-plugin-auth/client/translations.json new file mode 100644 index 000000000..93d73d3a2 --- /dev/null +++ b/plugins/coral-plugin-auth/client/translations.json @@ -0,0 +1,10 @@ +{ + "en": { + "like": "Like", + "liked": "Liked" + }, + "es": { + "like": "Me Gusta", + "liked": "Me Gustó" + } +} diff --git a/plugins/coral-plugin-auth/index.js b/plugins/coral-plugin-auth/index.js new file mode 100644 index 000000000..a09954537 --- /dev/null +++ b/plugins/coral-plugin-auth/index.js @@ -0,0 +1 @@ +module.exports = {}; \ No newline at end of file From 3af49de0cefc45c43d1226ff2654b87cd0bb6d5f Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Fri, 19 May 2017 18:04:54 -0300 Subject: [PATCH 03/47] New login slots and auth plugins --- client/coral-embed-stream/src/AppRouter.js | 6 +- .../containers/LoginContainer.js | 6 + .../containers/SignInContainer.js | 223 +----------------- .../client/components/SignInContainer.js | 210 +++++++++++++++++ plugins/coral-plugin-auth/client/index.js | 4 +- 5 files changed, 226 insertions(+), 223 deletions(-) create mode 100644 client/coral-sign-in/containers/LoginContainer.js create mode 100644 plugins/coral-plugin-auth/client/components/SignInContainer.js 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-sign-in/containers/LoginContainer.js b/client/coral-sign-in/containers/LoginContainer.js new file mode 100644 index 000000000..fa20de4ed --- /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 = () => ( + +) \ No newline at end of file diff --git a/client/coral-sign-in/containers/SignInContainer.js b/client/coral-sign-in/containers/SignInContainer.js index de1d2086e..b473626db 100644 --- a/client/coral-sign-in/containers/SignInContainer.js +++ b/client/coral-sign-in/containers/SignInContainer.js @@ -1,220 +1,5 @@ -import React, {Component, PropTypes} from 'react'; -import {connect} from 'react-redux'; -import SignDialog from '../components/SignDialog'; -import validate from 'coral-framework/helpers/validate'; -import errorMsj from 'coral-framework/helpers/error'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; -import {pym} from 'coral-framework'; -const lang = new I18n(translations); +import React from 'react'; -import { - changeView, - fetchSignUp, - fetchSignIn, - hideSignInDialog, - fetchSignInFacebook, - fetchSignUpFacebook, - fetchForgotPassword, - requestConfirmEmail, - facebookCallback, - invalidForm, - validForm, - checkLogin -} from 'coral-framework/actions/auth'; - -class SignInContainer extends Component { - initialState = { - formData: { - email: '', - username: '', - password: '', - confirmPassword: '' - }, - emailToBeResent: '', - errors: {}, - showErrors: false - }; - - constructor(props) { - super(props); - this.state = this.initialState; - this.addError = this.addError.bind(this); - this.handleAuth = this.handleAuth.bind(this); - this.handleSignUp = this.handleSignUp.bind(this); - this.handleSignIn = this.handleSignIn.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleChangeEmail = this.handleChangeEmail.bind(this); - this.handleResendVerification = this.handleResendVerification.bind(this); - } - - static propTypes = { - requireEmailConfirmation: PropTypes.bool.isRequired - }; - - componentWillMount() { - this.props.checkLogin(); - } - - componentDidMount() { - window.addEventListener('storage', this.handleAuth); - - const {formData} = this.state; - const errors = Object.keys(formData).reduce((map, prop) => { - map[prop] = lang.t('signIn.requiredField'); - return map; - }, {}); - this.setState({errors}); - } - - componentWillUnmount() { - window.removeEventListener('storage', this.handleAuth); - } - - handleAuth(e) { - - // Listening to FB changes - // FB localStorage key is 'auth' - const authCallback = this.props.facebookCallback; - - if (e.key === 'auth') { - const {err, data} = JSON.parse(e.newValue); - authCallback(err, data); - } - } - - handleChange(e) { - const {name, value} = e.target; - this.setState( - (state) => ({ - ...state, - formData: { - ...state.formData, - [name]: value - } - }), - () => { - this.validation(name, value); - } - ); - } - - handleChangeEmail(e) { - const {value} = e.target; - this.setState({emailToBeResent: value}); - } - - handleResendVerification(e) { - e.preventDefault(); - this.props - .requestConfirmEmail( - this.state.emailToBeResent, - pym.parentUrl || location.href - ) - .then(() => { - setTimeout(() => { - - // allow success UI to be shown for a second, and then close the modal - this.props.handleClose(); - }, 2500); - }); - } - - addError(name, error) { - return this.setState((state) => ({ - errors: { - ...state.errors, - [name]: error - } - })); - } - - validation(name, value) { - const {addError} = this; - const {formData} = this.state; - - if (!value.length) { - addError(name, lang.t('signIn.requiredField')); - } else if ( - name === 'confirmPassword' && - formData.confirmPassword !== formData.password - ) { - addError('confirmPassword', lang.t('signIn.passwordsDontMatch')); - } else if (!validate[name](value)) { - addError(name, errorMsj[name]); - } else { - const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line - // Removes Error - this.setState((state) => ({...state, errors})); - } - } - - isCompleted() { - const {formData} = this.state; - return !Object.keys(formData).filter((prop) => !formData[prop].length).length; - } - - displayErrors(show = true) { - this.setState({showErrors: show}); - } - - handleSignUp(e) { - e.preventDefault(); - const {errors} = this.state; - const {fetchSignUp, validForm, invalidForm} = this.props; - this.displayErrors(); - if (this.isCompleted() && !Object.keys(errors).length) { - fetchSignUp(this.state.formData, pym.parentUrl || location.href); - validForm(); - } else { - invalidForm(lang.t('signIn.checkTheForm')); - } - } - - handleSignIn(e) { - e.preventDefault(); - this.props.fetchSignIn(this.state.formData); - } - - render() { - const {auth, requireEmailConfirmation} = this.props; - const {emailVerificationLoading, emailVerificationSuccess} = auth; - - return ( -
- -
- ); - } -} - -const mapStateToProps = (state) => ({ - auth: state.auth.toJS() -}); - -const mapDispatchToProps = (dispatch) => ({ - checkLogin: () => dispatch(checkLogin()), - facebookCallback: (err, data) => dispatch(facebookCallback(err, data)), - fetchSignUp: (formData, url) => dispatch(fetchSignUp(formData, url)), - fetchSignIn: (formData) => dispatch(fetchSignIn(formData)), - fetchSignInFacebook: () => dispatch(fetchSignInFacebook()), - fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()), - fetchForgotPassword: (formData) => dispatch(fetchForgotPassword(formData)), - requestConfirmEmail: (email, url) => - dispatch(requestConfirmEmail(email, url)), - changeView: (view) => dispatch(changeView(view)), - handleClose: () => dispatch(hideSignInDialog()), - invalidForm: (error) => dispatch(invalidForm(error)), - validForm: () => dispatch(validForm()) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(SignInContainer); +export const SignInContainer = () => ( + +) \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/components/SignInContainer.js b/plugins/coral-plugin-auth/client/components/SignInContainer.js new file mode 100644 index 000000000..06a747556 --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/SignInContainer.js @@ -0,0 +1,210 @@ +import React, {Component, PropTypes} from 'react'; +import {connect} from 'react-redux'; +import validate from 'coral-framework/helpers/validate'; +import errorMsj from 'coral-framework/helpers/error'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from '../translations'; +import {pym} from 'coral-framework'; +const lang = new I18n(translations); + +import { + changeView, + fetchSignUp, + fetchSignIn, + hideSignInDialog, + fetchSignInFacebook, + fetchSignUpFacebook, + fetchForgotPassword, + requestConfirmEmail, + facebookCallback, + invalidForm, + validForm, + checkLogin +} from 'coral-framework/actions/auth'; + +class SignInContainer extends Component { + initialState = { + formData: { + email: '', + username: '', + password: '', + confirmPassword: '' + }, + emailToBeResent: '', + errors: {}, + showErrors: false + }; + + constructor(props) { + super(props); + this.state = this.initialState; + this.addError = this.addError.bind(this); + this.handleAuth = this.handleAuth.bind(this); + this.handleSignUp = this.handleSignUp.bind(this); + this.handleSignIn = this.handleSignIn.bind(this); + this.handleChange = this.handleChange.bind(this); + this.handleChangeEmail = this.handleChangeEmail.bind(this); + this.handleResendVerification = this.handleResendVerification.bind(this); + } + + static propTypes = { + requireEmailConfirmation: PropTypes.bool.isRequired + }; + + componentWillMount() { + this.props.checkLogin(); + } + + componentDidMount() { + window.addEventListener('storage', this.handleAuth); + + const {formData} = this.state; + const errors = Object.keys(formData).reduce((map, prop) => { + map[prop] = lang.t('signIn.requiredField'); + return map; + }, {}); + this.setState({errors}); + } + + componentWillUnmount() { + window.removeEventListener('storage', this.handleAuth); + } + + handleAuth(e) { + + // Listening to FB changes + // FB localStorage key is 'auth' + const authCallback = this.props.facebookCallback; + + if (e.key === 'auth') { + const {err, data} = JSON.parse(e.newValue); + authCallback(err, data); + } + } + + handleChange(e) { + const {name, value} = e.target; + this.setState( + (state) => ({ + ...state, + formData: { + ...state.formData, + [name]: value + } + }), + () => { + this.validation(name, value); + } + ); + } + + handleChangeEmail(e) { + const {value} = e.target; + this.setState({emailToBeResent: value}); + } + + handleResendVerification(e) { + e.preventDefault(); + this.props + .requestConfirmEmail( + this.state.emailToBeResent, + pym.parentUrl || location.href + ) + .then(() => { + setTimeout(() => { + + // allow success UI to be shown for a second, and then close the modal + this.props.handleClose(); + }, 2500); + }); + } + + addError(name, error) { + return this.setState((state) => ({ + errors: { + ...state.errors, + [name]: error + } + })); + } + + validation(name, value) { + const {addError} = this; + const {formData} = this.state; + + if (!value.length) { + addError(name, lang.t('signIn.requiredField')); + } else if ( + name === 'confirmPassword' && + formData.confirmPassword !== formData.password + ) { + addError('confirmPassword', lang.t('signIn.passwordsDontMatch')); + } else if (!validate[name](value)) { + addError(name, errorMsj[name]); + } else { + const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line + // Removes Error + this.setState((state) => ({...state, errors})); + } + } + + isCompleted() { + const {formData} = this.state; + return !Object.keys(formData).filter((prop) => !formData[prop].length).length; + } + + displayErrors(show = true) { + this.setState({showErrors: show}); + } + + handleSignUp(e) { + e.preventDefault(); + const {errors} = this.state; + const {fetchSignUp, validForm, invalidForm} = this.props; + this.displayErrors(); + if (this.isCompleted() && !Object.keys(errors).length) { + fetchSignUp(this.state.formData, pym.parentUrl || location.href); + validForm(); + } else { + invalidForm(lang.t('signIn.checkTheForm')); + } + } + + handleSignIn(e) { + e.preventDefault(); + this.props.fetchSignIn(this.state.formData); + } + + render() { + const {auth, requireEmailConfirmation} = this.props; + const {emailVerificationLoading, emailVerificationSuccess} = auth; + + return ( +
+ This is my login +
+ ); + } +} + +const mapStateToProps = (state) => ({ + auth: state.auth.toJS() +}); + +const mapDispatchToProps = (dispatch) => ({ + checkLogin: () => dispatch(checkLogin()), + facebookCallback: (err, data) => dispatch(facebookCallback(err, data)), + fetchSignUp: (formData, url) => dispatch(fetchSignUp(formData, url)), + fetchSignIn: (formData) => dispatch(fetchSignIn(formData)), + fetchSignInFacebook: () => dispatch(fetchSignInFacebook()), + fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()), + fetchForgotPassword: (formData) => dispatch(fetchForgotPassword(formData)), + requestConfirmEmail: (email, url) => + dispatch(requestConfirmEmail(email, url)), + changeView: (view) => dispatch(changeView(view)), + handleClose: () => dispatch(hideSignInDialog()), + invalidForm: (error) => dispatch(invalidForm(error)), + validForm: () => dispatch(validForm()) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(SignInContainer); diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js index ff601992b..8d2eecc3c 100644 --- a/plugins/coral-plugin-auth/client/index.js +++ b/plugins/coral-plugin-auth/client/index.js @@ -1,7 +1,9 @@ import SignInButton from './components/SignInButton'; +import SignInContainer from './components/SignInContainer'; export default { slots: { - stream: [SignInButton] + stream: [SignInButton], + login: [SignInContainer] } }; \ No newline at end of file From dbfd2aff7cdc2a8987f7740586d1d689ba4f6e48 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Fri, 19 May 2017 19:29:50 -0300 Subject: [PATCH 04/47] SignInButton --- .../src/components/Embed.js | 1 - .../src/components/Stream.js | 10 ++++----- .../containers/LoginContainer.js | 2 +- .../containers/SignInContainer.js | 2 +- .../client/components/SignInButton.js | 22 ++++++++++--------- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 2e050e836..3a898295f 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,7 +1,6 @@ import React from 'react'; const lang = new I18n(translations); import Stream from '../containers/Stream'; -import Slot from 'coral-framework/components/Slot'; import I18n from 'coral-framework/modules/i18n/i18n'; import UserBox from 'coral-sign-in/components/UserBox'; import translations from 'coral-framework/translations'; diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index e0c541341..60747e1a2 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -1,6 +1,4 @@ import React, {PropTypes} from 'react'; - -import {Button} from 'coral-ui'; import LoadMore from './LoadMore'; import NewCount from './NewCount'; import Comment from '../containers/Comment'; @@ -16,7 +14,7 @@ import ChangeUsernameContainer from 'coral-sign-in/containers/ChangeUsernameContainer'; class Stream extends React.Component { - setActiveReplyBox = reactKey => { + setActiveReplyBox = (reactKey) => { if (!this.props.auth.user) { this.props.showSignInDialog(); } else { @@ -59,11 +57,11 @@ class Stream extends React.Component { const firstCommentDate = asset.comments[0] ? asset.comments[0].created_at : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); - const commentIsIgnored = comment => { + const commentIsIgnored = (comment) => { return ( me && me.ignoredUsers && - me.ignoredUsers.find(u => u.id === comment.user.id) + me.ignoredUsers.find((u) => u.id === comment.user.id) ); }; return ( @@ -151,7 +149,7 @@ class Stream extends React.Component { setCommentCountCache={this.props.setCommentCountCache} />
- {comments.map(comment => { + {comments.map((comment) => { return commentIsIgnored(comment) ? : ( -) \ No newline at end of file +); diff --git a/client/coral-sign-in/containers/SignInContainer.js b/client/coral-sign-in/containers/SignInContainer.js index b473626db..4e883c765 100644 --- a/client/coral-sign-in/containers/SignInContainer.js +++ b/client/coral-sign-in/containers/SignInContainer.js @@ -2,4 +2,4 @@ import React from 'react'; export const SignInContainer = () => ( -) \ No newline at end of file +); diff --git a/plugins/coral-plugin-auth/client/components/SignInButton.js b/plugins/coral-plugin-auth/client/components/SignInButton.js index fb58eeb9b..b5a88431f 100644 --- a/plugins/coral-plugin-auth/client/components/SignInButton.js +++ b/plugins/coral-plugin-auth/client/components/SignInButton.js @@ -4,17 +4,19 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {showSignInDialog} from 'coral-framework/actions/auth'; -class SignInButton extends React.Component { - render() { - return ( - - ); - } -} +const SignInButton = ({loggedIn, showSignInDialog}) => ( +
+ { + !loggedIn ? ( + + ) : null + } +
+); -const mapStateToProps = ({auth}) => ({auth}); +const mapStateToProps = ({auth}) => ({loggedIn: auth.loggedIn}); const mapDispatchToProps = dispatch => bindActionCreators({showSignInDialog}, dispatch); From 801a6f8a534f23b59968d8e64c152ccb3db9dad3 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Fri, 19 May 2017 20:00:33 -0300 Subject: [PATCH 05/47] External plugin auth fully working, now refactor --- .../src/components/Embed.js | 10 +- .../src/components/Stream.js | 5 - client/coral-sign-in/components/UserBox.js | 15 --- .../containers/SignInContainer.js | 5 - .../components}/ChangeUsernameContainer.js | 51 ++++----- .../components/CreateUsernameDialog.js | 0 .../client}/components/FakeComment.js | 0 .../client}/components/ForgotContent.js | 0 .../client}/components/SignDialog.js | 0 .../client/components/SignInButton.js | 4 +- .../client/components/SignInContainer.js | 12 ++- .../client}/components/SignInContent.js | 0 .../client}/components/SignUpContent.js | 0 .../client/components/UserBox.js | 34 ++++++ .../client}/components/styles.css | 0 plugins/coral-plugin-auth/client/index.js | 4 +- .../client/translations.json | 100 +++++++++++++++++- 17 files changed, 177 insertions(+), 63 deletions(-) delete mode 100644 client/coral-sign-in/components/UserBox.js delete mode 100644 client/coral-sign-in/containers/SignInContainer.js rename {client/coral-sign-in/containers => plugins/coral-plugin-auth/client/components}/ChangeUsernameContainer.js (73%) rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/CreateUsernameDialog.js (100%) rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/FakeComment.js (100%) rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/ForgotContent.js (100%) rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/SignDialog.js (100%) rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/SignInContent.js (100%) rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/SignUpContent.js (100%) create mode 100644 plugins/coral-plugin-auth/client/components/UserBox.js rename {client/coral-sign-in => plugins/coral-plugin-auth/client}/components/styles.css (100%) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 3a898295f..e6ae3f367 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,8 +1,8 @@ import React from 'react'; const lang = new I18n(translations); import Stream from '../containers/Stream'; +import Slot from 'coral-framework/components/Slot'; import I18n from 'coral-framework/modules/i18n/i18n'; -import UserBox from 'coral-sign-in/components/UserBox'; import translations from 'coral-framework/translations'; import {TabBar, Tab, TabContent, Button} from 'coral-ui'; import Count from 'coral-plugin-comment-count/CommentCount'; @@ -34,12 +34,10 @@ export default class Embed extends React.Component { handleShowProfile = () => this.props.setActiveTab('profile'); render () { - const {activeTab, logout, viewAllComments, commentId} = this.props; + const {activeTab, viewAllComments, commentId} = this.props; const {asset: {totalCommentCount}} = this.props.root; const {loggedIn, isAdmin, user} = this.props.auth; - const userBox = ; - return (
@@ -48,6 +46,7 @@ export default class Embed extends React.Component { {lang.t('myProfile')} Configure Stream + { commentId &&
:

{asset.settings.closedMessage}

} - {loggedIn && - user && - } {loggedIn && } {highlightedComment diff --git a/client/coral-sign-in/components/UserBox.js b/client/coral-sign-in/components/UserBox.js deleted file mode 100644 index a44c3d4fb..000000000 --- a/client/coral-sign-in/components/UserBox.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import styles from './styles.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; -const lang = new I18n(translations); - -const UserBox = ({className, user, onLogout, onShowProfile}) => ( -
- {lang.t('signIn.loggedInAs')} - {user.username}. {lang.t('signIn.notYou')} - {lang.t('signIn.logout')} -
-); - -export default UserBox; diff --git a/client/coral-sign-in/containers/SignInContainer.js b/client/coral-sign-in/containers/SignInContainer.js deleted file mode 100644 index 4e883c765..000000000 --- a/client/coral-sign-in/containers/SignInContainer.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export const SignInContainer = () => ( - -); diff --git a/client/coral-sign-in/containers/ChangeUsernameContainer.js b/plugins/coral-plugin-auth/client/components/ChangeUsernameContainer.js similarity index 73% rename from client/coral-sign-in/containers/ChangeUsernameContainer.js rename to plugins/coral-plugin-auth/client/components/ChangeUsernameContainer.js index 12a9a75b8..4f0ad9895 100644 --- a/client/coral-sign-in/containers/ChangeUsernameContainer.js +++ b/plugins/coral-plugin-auth/client/components/ChangeUsernameContainer.js @@ -4,7 +4,7 @@ import {connect} from 'react-redux'; import validate from 'coral-framework/helpers/validate'; import errorMsj from 'coral-framework/helpers/error'; -import CreateUsernameDialog from '../components/CreateUsernameDialog'; +import CreateUsernameDialog from './CreateUsernameDialog'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; @@ -16,12 +16,12 @@ import { invalidForm, validForm, createUsername -} from '../../coral-framework/actions/auth'; +} from 'coral-framework/actions/auth'; class ChangeUsernameContainer extends Component { initialState = { formData: { - username: '', + username: '' }, errors: {}, showErrors: false @@ -39,19 +39,22 @@ class ChangeUsernameContainer extends Component { handleChange(e) { const {name, value} = e.target; - this.setState((state) => ({ - ...state, - formData: { - ...state.formData, - [name]: value + this.setState( + state => ({ + ...state, + formData: { + ...state.formData, + [name]: value + } + }), + () => { + this.validation(name, value); } - }), () => { - this.validation(name, value); - }); + ); } addError(name, error) { - return this.setState((state) => ({ + return this.setState(state => ({ errors: { ...state.errors, [name]: error @@ -67,15 +70,15 @@ class ChangeUsernameContainer extends Component { } else if (!validate[name](value)) { addError(name, errorMsj[name]); } else { - const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line + const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line // Removes Error - this.setState((state) => ({...state, errors})); + this.setState(state => ({...state, errors})); } } isCompleted() { const {formData} = this.state; - return !Object.keys(formData).filter((prop) => !formData[prop].length).length; + return !Object.keys(formData).filter(prop => !formData[prop].length).length; } displayErrors(show = true) { @@ -117,19 +120,17 @@ class ChangeUsernameContainer extends Component { } } -const mapStateToProps = (state) => ({ - auth: state.auth.toJS() -}); +const mapStateToProps = ({auth, user}) => ({auth, user}); -const mapDispatchToProps = (dispatch) => ({ - createUsername: (userid, formData) => dispatch(createUsername(userid, formData)), +const mapDispatchToProps = dispatch => ({ + createUsername: (userid, formData) => + dispatch(createUsername(userid, formData)), showCreateUsernameDialog: () => dispatch(showCreateUsernameDialog()), hideCreateUsernameDialog: () => dispatch(hideCreateUsernameDialog()), - invalidForm: (error) => dispatch(invalidForm(error)), + invalidForm: error => dispatch(invalidForm(error)), validForm: () => dispatch(validForm()) }); -export default connect( - mapStateToProps, - mapDispatchToProps -)(ChangeUsernameContainer); +export default connect(mapStateToProps, mapDispatchToProps)( + ChangeUsernameContainer +); diff --git a/client/coral-sign-in/components/CreateUsernameDialog.js b/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js similarity index 100% rename from client/coral-sign-in/components/CreateUsernameDialog.js rename to plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js diff --git a/client/coral-sign-in/components/FakeComment.js b/plugins/coral-plugin-auth/client/components/FakeComment.js similarity index 100% rename from client/coral-sign-in/components/FakeComment.js rename to plugins/coral-plugin-auth/client/components/FakeComment.js diff --git a/client/coral-sign-in/components/ForgotContent.js b/plugins/coral-plugin-auth/client/components/ForgotContent.js similarity index 100% rename from client/coral-sign-in/components/ForgotContent.js rename to plugins/coral-plugin-auth/client/components/ForgotContent.js diff --git a/client/coral-sign-in/components/SignDialog.js b/plugins/coral-plugin-auth/client/components/SignDialog.js similarity index 100% rename from client/coral-sign-in/components/SignDialog.js rename to plugins/coral-plugin-auth/client/components/SignDialog.js diff --git a/plugins/coral-plugin-auth/client/components/SignInButton.js b/plugins/coral-plugin-auth/client/components/SignInButton.js index b5a88431f..b4497bc92 100644 --- a/plugins/coral-plugin-auth/client/components/SignInButton.js +++ b/plugins/coral-plugin-auth/client/components/SignInButton.js @@ -16,7 +16,9 @@ const SignInButton = ({loggedIn, showSignInDialog}) => (
); -const mapStateToProps = ({auth}) => ({loggedIn: auth.loggedIn}); +const mapStateToProps = ({auth}) => ({ + loggedIn: auth.toJS().loggedIn +}); const mapDispatchToProps = dispatch => bindActionCreators({showSignInDialog}, dispatch); diff --git a/plugins/coral-plugin-auth/client/components/SignInContainer.js b/plugins/coral-plugin-auth/client/components/SignInContainer.js index 06a747556..73094920e 100644 --- a/plugins/coral-plugin-auth/client/components/SignInContainer.js +++ b/plugins/coral-plugin-auth/client/components/SignInContainer.js @@ -1,5 +1,6 @@ import React, {Component, PropTypes} from 'react'; import {connect} from 'react-redux'; +import SignDialog from './SignDialog'; import validate from 'coral-framework/helpers/validate'; import errorMsj from 'coral-framework/helpers/error'; import I18n from 'coral-framework/modules/i18n/i18n'; @@ -181,7 +182,16 @@ class SignInContainer extends Component { return (
- This is my login +
); } diff --git a/client/coral-sign-in/components/SignInContent.js b/plugins/coral-plugin-auth/client/components/SignInContent.js similarity index 100% rename from client/coral-sign-in/components/SignInContent.js rename to plugins/coral-plugin-auth/client/components/SignInContent.js diff --git a/client/coral-sign-in/components/SignUpContent.js b/plugins/coral-plugin-auth/client/components/SignUpContent.js similarity index 100% rename from client/coral-sign-in/components/SignUpContent.js rename to plugins/coral-plugin-auth/client/components/SignUpContent.js diff --git a/plugins/coral-plugin-auth/client/components/UserBox.js b/plugins/coral-plugin-auth/client/components/UserBox.js new file mode 100644 index 000000000..a4a0e1c99 --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/UserBox.js @@ -0,0 +1,34 @@ +import React from 'react'; +import styles from './styles.css'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import {showSignInDialog, logout} from 'coral-framework/actions/auth'; +const lang = new I18n(translations); + +const UserBox = ({loggedIn, user, logout, onShowProfile}) => ( +
+ { + loggedIn ? ( +
+ {lang.t('signIn.loggedInAs')} + {user.username}. {lang.t('signIn.notYou')} + logout()}> + {lang.t('signIn.logout')} + +
+ ) : null + } +
+); + +const mapStateToProps = ({auth, user}) => ({ + loggedIn: auth.toJS().loggedIn, + user: user.toJS() +}); + +const mapDispatchToProps = dispatch => + bindActionCreators({logout}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(UserBox); diff --git a/client/coral-sign-in/components/styles.css b/plugins/coral-plugin-auth/client/components/styles.css similarity index 100% rename from client/coral-sign-in/components/styles.css rename to plugins/coral-plugin-auth/client/components/styles.css diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js index 8d2eecc3c..3bf9e58f0 100644 --- a/plugins/coral-plugin-auth/client/index.js +++ b/plugins/coral-plugin-auth/client/index.js @@ -1,9 +1,11 @@ import SignInButton from './components/SignInButton'; import SignInContainer from './components/SignInContainer'; +import UserBox from './components/UserBox'; +import ChangeUserNameContainer from './components/ChangeUserNameContainer'; export default { slots: { - stream: [SignInButton], + stream: [UserBox, SignInButton, ChangeUserNameContainer], login: [SignInContainer] } }; \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/translations.json b/plugins/coral-plugin-auth/client/translations.json index 93d73d3a2..7ae84b2ff 100644 --- a/plugins/coral-plugin-auth/client/translations.json +++ b/plugins/coral-plugin-auth/client/translations.json @@ -1,10 +1,102 @@ { "en": { - "like": "Like", - "liked": "Liked" + "signIn": { + "emailVerifyCTA": "Please verify your email address.", + "requestNewVerifyEmail": "Request another email:", + "verifyEmail": "Thank you for creating an account! We sent an email to the address you provided to verify your account.", + "verifyEmail2": "You must verify your account before engaging with the community.", + "notYou": "Not you?", + "loggedInAs": "Logged in as", + "facebookSignIn": "Sign in with Facebook", + "facebookSignUp": "Sign up with Facebook", + "logout": "Logout", + "signIn": "Sign in to join the conversation", + "or": "Or", + "email": "E-mail Address", + "password": "Password", + "forgotYourPass": "Forgot your password?", + "needAnAccount": "Need an account?", + "register": "Register", + "signUp": "Sign Up", + "confirmPassword": "Confirm Password", + "username": "Username", + "alreadyHaveAnAccount": "Already have an account?", + "recoverPassword": "Recover password", + "emailInUse": "Email address already in use", + "emailORusernameInUse": "Email address or Username already in use", + "requiredField": "This field is required", + "passwordsDontMatch": "Passwords don't match.", + "specialCharacters": "Usernames can contain letters, numbers and _ only", + "checkTheForm": "Invalid Form. Please, check the fields" + }, + "createdisplay": { + "writeyourusername": "Edit your username", + "yourusername": "Your username appears on every comment you post.", + "ifyoudontchangeyourname": "If you don't change your username at this step, your Facebook display name will appear alongside of all your comments.", + "username": "Username", + "continue": "Continue with the same Facebook username", + "save": "Save", + "fakecommentdate": "1 minute ago", + "fakecommentbody": "This is an example comment. Readers can share their thoughts and opinions with newsrooms in the comments section.", + "requiredField": "Required field", + "errorCreate": "Error when changing username", + "checkTheForm": "Invalid Form. Please, check the fields", + "specialCharacters": "Usernames can contain letters, numbers and _ only" + }, + "permalink": { + "permalink": "Link" + }, + "report": "Report", + "like": "Like" }, "es": { - "like": "Me Gusta", - "liked": "Me Gustó" + "signIn": { + "emailVerifyCTA": "Por favor verifique su e-mail.", + "requestNewVerifyEmail": "Enviar otro correo:", + "verifyEmail": "¡Gracias por crear una cuenta! Le enviamos un correo a la dirección que dio para verificar su cuenta.", + "verifyEmail2": "Debe verificarla antes de poder involucrarse en la comunidad.", + "notYou": "¿No eres tu?", + "loggedInAs": "Entraste como", + "facebookSignIn": "Entrar con Facebook", + "facebookSignUp": "Regístrate con Facebook", + "logout": "Salir", + "signIn": "Entrar para Unirte a la Conversación", + "or": "o", + "email": "E-mail", + "password": "Contraseña", + "forgotYourPass": "¿Has olvidado tu contraseña?", + "needAnAccount": "¿Necesitas una cuenta?", + "register": "Regístrate", + "signUp": "Registro", + "confirmPassword": "Confirmar Contraseña", + "username": "Nombre", + "alreadyHaveAnAccount": "¿Ya tienes una cuenta?", + "recoverPassword": "Recuperar contraseña", + "emailInUse": "Este e-mail se encuentra en uso", + "emailORusernameInUse": "Este e-mail ó nombre de usuario se encuentran en uso", + "requiredField": "Este campo es requerido", + "passwordsDontMatch": "Las contraseñas no coinciden", + "specialCharacters": "Los nombres pueden contener letras, números y _", + "checkTheForm": "Formulario Inválido. Por favor, completa los campos" + }, + "createdisplay": { + "writeyourusername": "Edita tu nombre", + "yourusername": "Tu nombre aparece en cada comentario que publiques.", + "ifyoudontchangeyourname": "Si no modificas tu nombre de usuario en este paso, tu nombre de Facebook aparecera al lado de cada comentario que publiques.", + "username": "Nombre", + "continue": "Continuar con nombre de Facebook", + "save": "Guardar", + "fakecommentdate": "hace un minuto", + "fakecommentbody": "Este es un comentario de ejemplo. Las lectoras pueden compartir sus ideas y opiniones con los periodistas en la sección de comentarios.", + "requiredField": "Campo necesario", + "errorCreate": "Hubo un error al cambiar el nombre de usuario", + "checkTheForm": "Formulario Inválido. Por favor, verifica los campos", + "specialCharacters": "Sólo pueden contener letras, números y _" + }, + "permalink": { + "permalink": "Enlace" + }, + "report": "Marcar", + "like": "Me gusta" } } From 1aacb565d4d90b8b1a6562266ba0185e987ec20c Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Fri, 19 May 2017 20:10:49 -0300 Subject: [PATCH 06/47] linting --- client/coral-embed-stream/src/components/Embed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index e6ae3f367..2c22cc8a0 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -36,7 +36,7 @@ export default class Embed extends React.Component { render () { const {activeTab, viewAllComments, commentId} = this.props; const {asset: {totalCommentCount}} = this.props.root; - const {loggedIn, isAdmin, user} = this.props.auth; + const {loggedIn, isAdmin} = this.props.auth; return (
From f364a1e0a12015843f35a4884f5bc1822459b6e9 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 22 May 2017 08:23:58 -0300 Subject: [PATCH 07/47] Refactor --- .../client/components/SignInContainer.js | 140 +++++++++--------- .../client/components/SignInContent.js | 47 +++--- .../client/components/SignUpContent.js | 91 ++++++------ plugins/coral-plugin-auth/client/index.js | 2 +- 4 files changed, 139 insertions(+), 141 deletions(-) diff --git a/plugins/coral-plugin-auth/client/components/SignInContainer.js b/plugins/coral-plugin-auth/client/components/SignInContainer.js index 73094920e..5a8ffa11a 100644 --- a/plugins/coral-plugin-auth/client/components/SignInContainer.js +++ b/plugins/coral-plugin-auth/client/components/SignInContainer.js @@ -1,11 +1,13 @@ -import React, {Component, PropTypes} from 'react'; +import React from 'react'; import {connect} from 'react-redux'; -import SignDialog from './SignDialog'; -import validate from 'coral-framework/helpers/validate'; -import errorMsj from 'coral-framework/helpers/error'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; import {pym} from 'coral-framework'; +import SignDialog from './SignDialog'; +import {bindActionCreators} from 'redux'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import errorMsj from 'coral-framework/helpers/error'; +import validate from 'coral-framework/helpers/validate'; + const lang = new I18n(translations); import { @@ -23,34 +25,22 @@ import { checkLogin } from 'coral-framework/actions/auth'; -class SignInContainer extends Component { - initialState = { - formData: { - email: '', - username: '', - password: '', - confirmPassword: '' - }, - emailToBeResent: '', - errors: {}, - showErrors: false - }; - +class SignInContainer extends React.Component { constructor(props) { super(props); - this.state = this.initialState; - this.addError = this.addError.bind(this); - this.handleAuth = this.handleAuth.bind(this); - this.handleSignUp = this.handleSignUp.bind(this); - this.handleSignIn = this.handleSignIn.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleChangeEmail = this.handleChangeEmail.bind(this); - this.handleResendVerification = this.handleResendVerification.bind(this); - } - static propTypes = { - requireEmailConfirmation: PropTypes.bool.isRequired - }; + this.state = { + formData: { + email: '', + username: '', + password: '', + confirmPassword: '' + }, + emailToBeResent: '', + errors: {}, + showErrors: false + }; + } componentWillMount() { this.props.checkLogin(); @@ -71,8 +61,7 @@ class SignInContainer extends Component { window.removeEventListener('storage', this.handleAuth); } - handleAuth(e) { - + handleAuth = e => { // Listening to FB changes // FB localStorage key is 'auth' const authCallback = this.props.facebookCallback; @@ -81,12 +70,12 @@ class SignInContainer extends Component { const {err, data} = JSON.parse(e.newValue); authCallback(err, data); } - } + }; - handleChange(e) { + handleChange = e => { const {name, value} = e.target; this.setState( - (state) => ({ + state => ({ ...state, formData: { ...state.formData, @@ -97,14 +86,14 @@ class SignInContainer extends Component { this.validation(name, value); } ); - } + }; - handleChangeEmail(e) { + handleChangeEmail = e => { const {value} = e.target; this.setState({emailToBeResent: value}); - } + }; - handleResendVerification(e) { + handleResendVerification = e => { e.preventDefault(); this.props .requestConfirmEmail( @@ -113,23 +102,22 @@ class SignInContainer extends Component { ) .then(() => { setTimeout(() => { - // allow success UI to be shown for a second, and then close the modal - this.props.handleClose(); + this.props.hideSignInDialog(); }, 2500); }); - } + }; - addError(name, error) { - return this.setState((state) => ({ + addError = (name, error) => { + return this.setState(state => ({ errors: { ...state.errors, [name]: error } })); - } + }; - validation(name, value) { + validation = (name, value) => { const {addError} = this; const {formData} = this.state; @@ -145,20 +133,20 @@ class SignInContainer extends Component { } else { const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line // Removes Error - this.setState((state) => ({...state, errors})); + this.setState(state => ({...state, errors})); } - } + }; - isCompleted() { + isCompleted = () => { const {formData} = this.state; - return !Object.keys(formData).filter((prop) => !formData[prop].length).length; - } + return !Object.keys(formData).filter(prop => !formData[prop].length).length; + }; - displayErrors(show = true) { + displayErrors = (show = true) => { this.setState({showErrors: show}); - } + }; - handleSignUp(e) { + handleSignUp = e => { e.preventDefault(); const {errors} = this.state; const {fetchSignUp, validForm, invalidForm} = this.props; @@ -169,12 +157,12 @@ class SignInContainer extends Component { } else { invalidForm(lang.t('signIn.checkTheForm')); } - } + }; - handleSignIn(e) { + handleSignIn = e => { e.preventDefault(); this.props.fetchSignIn(this.state.formData); - } + }; render() { const {auth, requireEmailConfirmation} = this.props; @@ -197,24 +185,28 @@ class SignInContainer extends Component { } } -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ auth: state.auth.toJS() }); -const mapDispatchToProps = (dispatch) => ({ - checkLogin: () => dispatch(checkLogin()), - facebookCallback: (err, data) => dispatch(facebookCallback(err, data)), - fetchSignUp: (formData, url) => dispatch(fetchSignUp(formData, url)), - fetchSignIn: (formData) => dispatch(fetchSignIn(formData)), - fetchSignInFacebook: () => dispatch(fetchSignInFacebook()), - fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()), - fetchForgotPassword: (formData) => dispatch(fetchForgotPassword(formData)), - requestConfirmEmail: (email, url) => - dispatch(requestConfirmEmail(email, url)), - changeView: (view) => dispatch(changeView(view)), - handleClose: () => dispatch(hideSignInDialog()), - invalidForm: (error) => dispatch(invalidForm(error)), - validForm: () => dispatch(validForm()) -}); +const mapDispatchToProps = dispatch => + bindActionCreators( + { + checkLogin, + facebookCallback, + fetchSignUp, + fetchSignUp, + fetchSignIn, + fetchSignInFacebook, + fetchSignUpFacebook, + fetchForgotPassword, + requestConfirmEmail, + changeView, + hideSignInDialog, + invalidForm, + validForm + }, + dispatch + ); export default connect(mapStateToProps, mapDispatchToProps)(SignInContainer); diff --git a/plugins/coral-plugin-auth/client/components/SignInContent.js b/plugins/coral-plugin-auth/client/components/SignInContent.js index 7460efb97..6f0027084 100644 --- a/plugins/coral-plugin-auth/client/components/SignInContent.js +++ b/plugins/coral-plugin-auth/client/components/SignInContent.js @@ -18,17 +18,17 @@ const SignInContent = ({ auth, fetchSignInFacebook }) => { - return (

- {auth.emailVerificationFailure ? lang.t('signIn.emailVerifyCTA') : lang.t('signIn.signIn')} + {auth.emailVerificationFailure + ? lang.t('signIn.emailVerifyCTA') + : lang.t('signIn.signIn')}

- { auth.error && {auth.error} } - { - auth.emailVerificationFailure + {auth.error && {auth.error}} + {auth.emailVerificationFailure ?

{lang.t('signIn.requestNewVerifyEmail')}

- + onChange={handleChangeEmail} + /> + {emailVerificationLoading && } {emailVerificationSuccess && } @@ -70,23 +73,29 @@ const SignInContent = ({ onChange={handleChange} />
- { - !auth.isLoading ? - - : - - } + {!auth.isLoading + ? + : }
-
- } +
}
- changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')} + + changeView('FORGOT')}> + {lang.t('signIn.forgotYourPass')} + + {lang.t('signIn.needAnAccount')} - changeView('SIGNUP')} id='coralRegister'> + changeView('SIGNUP')} id="coralRegister"> {lang.t('signIn.register')} diff --git a/plugins/coral-plugin-auth/client/components/SignUpContent.js b/plugins/coral-plugin-auth/client/components/SignUpContent.js index 921f045be..5f2dd36ea 100644 --- a/plugins/coral-plugin-auth/client/components/SignUpContent.js +++ b/plugins/coral-plugin-auth/client/components/SignUpContent.js @@ -1,39 +1,21 @@ -import React, {PropTypes} from 'react'; -import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; import styles from './styles.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; +import React, {PropTypes} from 'react'; import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; + const lang = new I18n(translations); class SignUpContent extends React.Component { + constructor() { + super(); - constructor (props) { - super(props); - this.successfulSignup = false; + this.state = { + successfulSignup: false + }; } - static propTypes = { - emailVerificationEnabled: PropTypes.bool.isRequired, - fetchSignUpFacebook: PropTypes.func.isRequired, - changeView: PropTypes.func.isRequired, - handleSignUp: PropTypes.func.isRequired, - showErrors: PropTypes.bool, - errors: PropTypes.shape({ - email: PropTypes.string, - username: PropTypes.string, - password: PropTypes.string, - confirmPassword: PropTypes.string, - }), - formData: PropTypes.shape({ - email: PropTypes.string, - username: PropTypes.string, - password: PropTypes.string, - confirmPassword: PropTypes.string - }) - } - - render () { - + render() { const { handleChange, formData, @@ -43,17 +25,20 @@ class SignUpContent extends React.Component { showErrors, changeView, handleSignUp, - fetchSignUpFacebook} = this.props; + fetchSignUpFacebook + } = this.props; const beforeSignup = !auth.isLoading && !auth.successSignUp; const successfulSignup = !auth.isLoading && auth.successSignUp; // the first time we render a successfulSignup, trigger a timer - if ((this.successfulSignup ^ successfulSignup) && !emailVerificationEnabled) { + if (this.successfulSignup ^ successfulSignup && !emailVerificationEnabled) { setTimeout(() => { changeView('SIGNIN'); }, 1000); - this.successfulSignup = true; + this.setState({ + successfulSignup: true + }); } return ( @@ -64,8 +49,8 @@ class SignUpContent extends React.Component {
- { auth.error && {auth.error} } - { beforeSignup && + {auth.error && {auth.error}} + {beforeSignup &&
- { auth.isLoading && } + {auth.isLoading && }
-
- } - { - successfulSignup && +
} + {successfulSignup &&
- { - emailVerificationEnabled && -

{lang.t('signIn.verifyEmail')}

{lang.t('signIn.verifyEmail2')}

- } -
- } + {emailVerificationEnabled && +

+ {lang.t('signIn.verifyEmail')} +
+
+ {lang.t('signIn.verifyEmail2')} +

} +
}
- {lang.t('signIn.alreadyHaveAnAccount')} changeView('SIGNIN')}> + {lang.t('signIn.alreadyHaveAnAccount')} changeView('SIGNIN')} + > {lang.t('signIn.signIn')}
diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js index 3bf9e58f0..867849cac 100644 --- a/plugins/coral-plugin-auth/client/index.js +++ b/plugins/coral-plugin-auth/client/index.js @@ -1,6 +1,6 @@ +import UserBox from './components/UserBox'; import SignInButton from './components/SignInButton'; import SignInContainer from './components/SignInContainer'; -import UserBox from './components/UserBox'; import ChangeUserNameContainer from './components/ChangeUserNameContainer'; export default { From 9d00a27f1de3b5c0a170c885c9adb5b19bba47a6 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 22 May 2017 08:40:45 -0300 Subject: [PATCH 08/47] Auth as a plugin --- ...UsernameContainer.js => ChangeUsername.js} | 82 ++++++------ .../client/components/CreateUsernameDialog.js | 49 ++++--- .../client/components/FakeComment.js | 124 +++++++++--------- .../client/components/ForgotContent.js | 62 +++++---- .../client/components/SignDialog.js | 9 +- .../client/components/SignInButton.js | 8 +- .../client/components/SignInContainer.js | 22 ++-- plugins/coral-plugin-auth/client/index.js | 2 +- 8 files changed, 190 insertions(+), 168 deletions(-) rename plugins/coral-plugin-auth/client/components/{ChangeUsernameContainer.js => ChangeUsername.js} (69%) diff --git a/plugins/coral-plugin-auth/client/components/ChangeUsernameContainer.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js similarity index 69% rename from plugins/coral-plugin-auth/client/components/ChangeUsernameContainer.js rename to plugins/coral-plugin-auth/client/components/ChangeUsername.js index 4f0ad9895..9b0cbb1a6 100644 --- a/plugins/coral-plugin-auth/client/components/ChangeUsernameContainer.js +++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js @@ -1,13 +1,12 @@ -import React, {Component} from 'react'; +import React from 'react'; import {connect} from 'react-redux'; - -import validate from 'coral-framework/helpers/validate'; +import {bindActionCreators} from 'redux'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; import errorMsj from 'coral-framework/helpers/error'; - +import validate from 'coral-framework/helpers/validate'; import CreateUsernameDialog from './CreateUsernameDialog'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; const lang = new I18n(translations); import { @@ -18,26 +17,20 @@ import { createUsername } from 'coral-framework/actions/auth'; -class ChangeUsernameContainer extends Component { - initialState = { - formData: { - username: '' - }, - errors: {}, - showErrors: false - }; - +class ChangeUsernameContainer extends React.Component { constructor(props) { super(props); - this.initialState.formData.username = props.user.username; - this.state = this.initialState; - this.handleChange = this.handleChange.bind(this); - this.handleSubmitUsername = this.handleSubmitUsername.bind(this); - this.handleClose = this.handleClose.bind(this); - this.addError = this.addError.bind(this); + + this.state = { + formData: { + username: props.user.username + }, + errors: {}, + showErrors: false + }; } - handleChange(e) { + handleChange = e => { const {name, value} = e.target; this.setState( state => ({ @@ -51,18 +44,18 @@ class ChangeUsernameContainer extends Component { this.validation(name, value); } ); - } + }; - addError(name, error) { + addError = (name, error) => { return this.setState(state => ({ errors: { ...state.errors, [name]: error } })); - } + }; - validation(name, value) { + validation = (name, value) => { const {addError} = this; if (!value.length) { @@ -74,18 +67,18 @@ class ChangeUsernameContainer extends Component { // Removes Error this.setState(state => ({...state, errors})); } - } + }; - isCompleted() { + isCompleted = () => { const {formData} = this.state; return !Object.keys(formData).filter(prop => !formData[prop].length).length; - } + }; - displayErrors(show = true) { + displayErrors = (show = true) => { this.setState({showErrors: show}); - } + }; - handleSubmitUsername(e) { + handleSubmitUsername = e => { e.preventDefault(); const {errors} = this.state; const {validForm, invalidForm} = this.props; @@ -96,11 +89,11 @@ class ChangeUsernameContainer extends Component { } else { invalidForm(lang.t('createdisplay.checkTheForm')); } - } + }; - handleClose() { + handleClose = () => { this.props.hideCreateUsernameDialog(); - } + }; render() { const {loggedIn, auth} = this.props; @@ -122,14 +115,17 @@ class ChangeUsernameContainer extends Component { const mapStateToProps = ({auth, user}) => ({auth, user}); -const mapDispatchToProps = dispatch => ({ - createUsername: (userid, formData) => - dispatch(createUsername(userid, formData)), - showCreateUsernameDialog: () => dispatch(showCreateUsernameDialog()), - hideCreateUsernameDialog: () => dispatch(hideCreateUsernameDialog()), - invalidForm: error => dispatch(invalidForm(error)), - validForm: () => dispatch(validForm()) -}); +const mapDispatchToProps = dispatch => + bindActionCreators( + { + createUsername, + showCreateUsernameDialog, + hideCreateUsernameDialog, + invalidForm, + validForm + }, + dispatch + ); export default connect(mapStateToProps, mapDispatchToProps)( ChangeUsernameContainer diff --git a/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js b/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js index b94b47c42..c352552ae 100644 --- a/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js +++ b/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js @@ -1,21 +1,26 @@ import React from 'react'; -import TextField from 'coral-ui/components/TextField'; -import Button from 'coral-ui/components/Button'; -import {Dialog, Alert} from 'coral-ui'; -import FakeComment from './FakeComment'; - import styles from './styles.css'; - -import I18n from 'coral-framework/modules/i18n/i18n'; +import {Dialog, Alert, TextField} from 'coral-ui'; +import {FakeComment} from './FakeComment'; import translations from '../translations'; +import Button from 'coral-ui/components/Button'; +import I18n from 'coral-framework/modules/i18n/i18n'; + const lang = new I18n(translations); -const CreateUsernameDialog = ({open, handleClose, formData, handleSubmitUsername, handleChange, ...props}) => { - return ( +const CreateUsernameDialog = ({ + open, + handleClose, + formData, + handleSubmitUsername, + handleChange, + ...props +}) => ( + open={open} + > ×
@@ -24,17 +29,24 @@ const CreateUsernameDialog = ({open, handleClose, formData, handleSubmitUsername
-

{lang.t('createdisplay.yourusername')}

+

+ {lang.t('createdisplay.yourusername')} +

-

{lang.t('createdisplay.ifyoudontchangeyourname')}

- { props.auth.error && {props.auth.error} } +

+ {lang.t('createdisplay.ifyoudontchangeyourname')} +

+ {props.auth.error && {props.auth.error}}
- { props.errors.username && {lang.t('createdisplay.specialCharacters')} } + {props.errors.username && + + {' '}{lang.t('createdisplay.specialCharacters')}{' '} + }
- +
-
+
- ); -}; +); export default CreateUsernameDialog; diff --git a/plugins/coral-plugin-auth/client/components/FakeComment.js b/plugins/coral-plugin-auth/client/components/FakeComment.js index 37630383d..01cc6863e 100644 --- a/plugins/coral-plugin-auth/client/components/FakeComment.js +++ b/plugins/coral-plugin-auth/client/components/FakeComment.js @@ -1,66 +1,72 @@ import React from 'react'; -import styles from 'coral-embed-stream/src/components/Comment.css'; - +import translations from '../translations'; +import {ReplyButton} from 'coral-plugin-replies'; +import PubDate from 'coral-plugin-pubdate/PubDate'; +import I18n from 'coral-framework/modules/i18n/i18n'; 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 I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; +import styles from 'coral-embed-stream/src/components/Comment.css'; const lang = new I18n(translations); -class FakeComment extends React.Component { - constructor (props) { - super(props); - } - - render () { - const {username, created_at, body} = this.props; - - return ( -
-
- - - -
-
- -
- {}} - parentCommentId={'commentID'} - currentUserId={{}} - banned={false} - /> -
-
-
- -
-
- -
-
+export const FakeComment = ({username, created_at, body}) => ( +
+
+ + + +
+
+
- ); - } -} - -export default FakeComment; + {}} + parentCommentId={'commentID'} + currentUserId={{}} + banned={false} + /> +
+
+
+ +
+
+ +
+
+
+); \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/components/ForgotContent.js b/plugins/coral-plugin-auth/client/components/ForgotContent.js index 76246fa00..2099330bc 100644 --- a/plugins/coral-plugin-auth/client/components/ForgotContent.js +++ b/plugins/coral-plugin-auth/client/components/ForgotContent.js @@ -1,22 +1,18 @@ import React from 'react'; import styles from './styles.css'; +import translations from '../translations'; import Button from 'coral-ui/components/Button'; import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; + const lang = new I18n(translations); class ForgotContent extends React.Component { - constructor (props) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit (e) { + handleSubmit = e => { e.preventDefault(); this.props.fetchForgotPassword(this.emailInput.value); - } + }; - render () { + render() { const {changeView, auth} = this.props; const {passwordRequestSuccess, passwordRequestFailure} = auth; @@ -29,29 +25,47 @@ class ForgotContent extends React.Component {
this.emailInput = input} + ref={input => (this.emailInput = input)} type="text" style={{fontSize: 16}} id="email" - name="email" /> + name="email" + />
- - { - passwordRequestSuccess - ?

{passwordRequestSuccess}

- : null - } - { - passwordRequestFailure - ?

{passwordRequestFailure}

- : null - } + {passwordRequestSuccess + ?

+ {passwordRequestSuccess} +

+ : null} + {passwordRequestFailure + ?

+ {passwordRequestFailure} +

+ : null}
- {lang.t('signIn.needAnAccount')} changeView('SIGNUP')}>{lang.t('signIn.register')} - {lang.t('signIn.alreadyHaveAnAccount')} changeView('SIGNIN')}>{lang.t('signIn.signIn')} + + {lang.t('signIn.needAnAccount')} + {' '} + changeView('SIGNUP')}> + {lang.t('signIn.register')} + + + + {lang.t('signIn.alreadyHaveAnAccount')} + {' '} + changeView('SIGNIN')}> + {lang.t('signIn.signIn')} + +
); diff --git a/plugins/coral-plugin-auth/client/components/SignDialog.js b/plugins/coral-plugin-auth/client/components/SignDialog.js index ff2464f7c..7f05889a0 100644 --- a/plugins/coral-plugin-auth/client/components/SignDialog.js +++ b/plugins/coral-plugin-auth/client/components/SignDialog.js @@ -6,12 +6,9 @@ import SignInContent from './SignInContent'; import SignUpContent from './SignUpContent'; import ForgotContent from './ForgotContent'; -const SignDialog = ({open, view, handleClose, ...props}) => ( - - × +const SignDialog = ({open, view, hideSignInDialog, ...props}) => ( + + × {view === 'SIGNIN' && } {view === 'SIGNUP' && } {view === 'FORGOT' && } diff --git a/plugins/coral-plugin-auth/client/components/SignInButton.js b/plugins/coral-plugin-auth/client/components/SignInButton.js index b4497bc92..b54cbfedd 100644 --- a/plugins/coral-plugin-auth/client/components/SignInButton.js +++ b/plugins/coral-plugin-auth/client/components/SignInButton.js @@ -6,13 +6,11 @@ import {showSignInDialog} from 'coral-framework/actions/auth'; const SignInButton = ({loggedIn, showSignInDialog}) => (
- { - !loggedIn ? ( - - ) : null - } + : null}
); diff --git a/plugins/coral-plugin-auth/client/components/SignInContainer.js b/plugins/coral-plugin-auth/client/components/SignInContainer.js index 5a8ffa11a..cbff000ee 100644 --- a/plugins/coral-plugin-auth/client/components/SignInContainer.js +++ b/plugins/coral-plugin-auth/client/components/SignInContainer.js @@ -169,18 +169,16 @@ class SignInContainer extends React.Component { const {emailVerificationLoading, emailVerificationSuccess} = auth; return ( -
- -
+ ); } } diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js index 867849cac..41fa3adbe 100644 --- a/plugins/coral-plugin-auth/client/index.js +++ b/plugins/coral-plugin-auth/client/index.js @@ -1,7 +1,7 @@ import UserBox from './components/UserBox'; import SignInButton from './components/SignInButton'; import SignInContainer from './components/SignInContainer'; -import ChangeUserNameContainer from './components/ChangeUserNameContainer'; +import ChangeUserNameContainer from './components/ChangeUsername'; export default { slots: { From 9936b6ec47c6e1ec4c3e9a8fb95c8263ff3a12a0 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 22 May 2017 08:59:34 -0300 Subject: [PATCH 09/47] Prettier --- .../src/components/Stream.js | 141 +++++++++--------- 1 file changed, 74 insertions(+), 67 deletions(-) diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index a82d7e639..bdf00c46c 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -19,7 +19,7 @@ import ChangeUsernameContainer const lang = new I18n(translations); class Stream extends React.Component { - setActiveReplyBox = (reactKey) => { + setActiveReplyBox = reactKey => { if (!this.props.auth.user) { this.props.showSignInDialog(); } else { @@ -53,7 +53,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,8 +66,12 @@ class Stream extends React.Component { const firstCommentDate = asset.comments[0] ? 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); + const commentIsIgnored = comment => { + return ( + me && + me.ignoredUsers && + me.ignoredUsers.find(u => u.id === comment.user.id) + ); }; return (
@@ -81,38 +88,37 @@ class Stream extends React.Component { content={asset.settings.questionBoxContent} enable={asset.settings.questionBoxEnable} /> - {!banned && temporarilySuspended && + {!banned && + temporarilySuspended && - { - lang.t('temporarilySuspended', - this.props.root.settings.organizationName, - lang.timeago(user.suspension.until), - ) - } - - } + {lang.t( + 'temporarilySuspended', + this.props.root.settings.organizationName, + lang.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 && @@ -126,7 +132,11 @@ class Stream extends React.Component { {loggedIn && user && } - {loggedIn && } + {loggedIn && + } {/* the highlightedComment is isolated after the user followed a permalink */} {highlightedComment @@ -164,41 +174,38 @@ class Stream extends React.Component { setCommentCountCache={this.props.setCommentCountCache} />
- {comments.map( - (comment) => { - return (commentIsIgnored(comment) - ? - : - ); - } - )} + {comments.map(comment => { + return commentIsIgnored(comment) + ? + : ; + })}
Date: Mon, 22 May 2017 09:03:10 -0300 Subject: [PATCH 10/47] linting --- client/coral-embed-stream/src/components/Stream.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index bdf00c46c..ab551e33e 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -19,7 +19,7 @@ import ChangeUsernameContainer const lang = new I18n(translations); class Stream extends React.Component { - setActiveReplyBox = reactKey => { + setActiveReplyBox = (reactKey) => { if (!this.props.auth.user) { this.props.showSignInDialog(); } else { @@ -66,11 +66,11 @@ class Stream extends React.Component { const firstCommentDate = asset.comments[0] ? asset.comments[0].created_at : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); - const commentIsIgnored = comment => { + const commentIsIgnored = (comment) => { return ( me && me.ignoredUsers && - me.ignoredUsers.find(u => u.id === comment.user.id) + me.ignoredUsers.find((u) => u.id === comment.user.id) ); }; return ( @@ -174,7 +174,7 @@ class Stream extends React.Component { setCommentCountCache={this.props.setCommentCountCache} />
- {comments.map(comment => { + {comments.map((comment) => { return commentIsIgnored(comment) ? : Date: Mon, 22 May 2017 09:39:04 -0300 Subject: [PATCH 11/47] Merge Conflicts --- .../coral-embed-stream/src/components/Stream.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index ab551e33e..53f716954 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -13,8 +13,6 @@ import translations from 'coral-framework/translations'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; import QuestionBox from 'coral-plugin-questionbox/QuestionBox'; import IgnoredCommentTombstone from './IgnoredCommentTombstone'; -import ChangeUsernameContainer - from 'coral-sign-in/containers/ChangeUsernameContainer'; const lang = new I18n(translations); @@ -75,9 +73,7 @@ class Stream extends React.Component { }; return (
- - {open ?
}
:

{asset.settings.closedMessage}

} - {!loggedIn && - } - {loggedIn && - user && - } {loggedIn && Date: Tue, 23 May 2017 13:58:02 -0400 Subject: [PATCH 12/47] First pass docs restructure --- README.md | 5 +- docs/{ => diagrams}/architecture.png | Bin docs/{ => diagrams}/architecture.xml | 0 docs/{ => diagrams}/data-model.png | Bin docs/{ => diagrams}/data-model.xml | 0 docs/{ => diagrams}/moderation-flow-post.png | Bin docs/{ => diagrams}/moderation-flow-pre.png | Bin docs/{ => diagrams}/moderation-flow.xml | 0 docs/plugins/CLIENT.md | 272 +++++++++++++ docs/plugins/EXPERIMENTAL.md | 102 +++++ docs/plugins/README.md | 163 ++++++++ docs/plugins/SERVER.md | 382 +++++++++++++++++++ 12 files changed, 923 insertions(+), 1 deletion(-) rename docs/{ => diagrams}/architecture.png (100%) rename docs/{ => diagrams}/architecture.xml (100%) rename docs/{ => diagrams}/data-model.png (100%) rename docs/{ => diagrams}/data-model.xml (100%) rename docs/{ => diagrams}/moderation-flow-post.png (100%) rename docs/{ => diagrams}/moderation-flow-pre.png (100%) rename docs/{ => diagrams}/moderation-flow.xml (100%) create mode 100644 docs/plugins/CLIENT.md create mode 100644 docs/plugins/EXPERIMENTAL.md create mode 100644 docs/plugins/README.md create mode 100644 docs/plugins/SERVER.md diff --git a/README.md b/README.md index 718847d9e..49256d24d 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 diff --git a/docs/architecture.png b/docs/diagrams/architecture.png similarity index 100% rename from docs/architecture.png rename to docs/diagrams/architecture.png diff --git a/docs/architecture.xml b/docs/diagrams/architecture.xml similarity index 100% rename from docs/architecture.xml rename to docs/diagrams/architecture.xml diff --git a/docs/data-model.png b/docs/diagrams/data-model.png similarity index 100% rename from docs/data-model.png rename to docs/diagrams/data-model.png diff --git a/docs/data-model.xml b/docs/diagrams/data-model.xml similarity index 100% rename from docs/data-model.xml rename to docs/diagrams/data-model.xml diff --git a/docs/moderation-flow-post.png b/docs/diagrams/moderation-flow-post.png similarity index 100% rename from docs/moderation-flow-post.png rename to docs/diagrams/moderation-flow-post.png diff --git a/docs/moderation-flow-pre.png b/docs/diagrams/moderation-flow-pre.png similarity index 100% rename from docs/moderation-flow-pre.png rename to docs/diagrams/moderation-flow-pre.png diff --git a/docs/moderation-flow.xml b/docs/diagrams/moderation-flow.xml similarity index 100% rename from docs/moderation-flow.xml rename to docs/diagrams/moderation-flow.xml diff --git a/docs/plugins/CLIENT.md b/docs/plugins/CLIENT.md new file mode 100644 index 000000000..3842fef77 --- /dev/null +++ b/docs/plugins/CLIENT.md @@ -0,0 +1,272 @@ +# Plugins +We can build plugins to extend the client side functionality of Talk. + +The ultimate goal of our client side plugin architecture is to allow developers +to build on existing concepts without needing to understand core code while +providing complete power and flexibility of javascript, css and html. + +* [Plugin Architecture](#plugin-architecture) +* Using our building block components +* [Reactions](#reactions) +* [Styling](#styling-plugins) + +Advanced users will quickly realize that our plugins have complete access to core code. If you would like to write advanced plugins that reach outside of our published API as described in this document, please see [our notes on experimental plugins](/plugins/EXPERIMENTAL.md). + +Under the hood our plugins are powered by *React*, *Redux* and *GraphQL*. We can also build them with simple vanilla javascript. + +## Plugin Architecture + +The plugins live in the `/plugins` folder. Each plugin must have an `index.js` file and two folders `client` and `server`. + +### The Client Folder +The frontend of our plugin lives inside the `client` folder. The `client` folder must have an `index.js` file that exports the configuration of our plugin. + +``` +my-plugin/ + ├── client/ + │ └── index.js <-- index for client side functionality + ├── server/ + └── index.js <-- base plugin index +``` + +For now our base plugin `index.js` file should look like this: + +```js +export default { + // We will add more here later. +}; +``` + +### Creating a Component + +We can add our components (or any other javascript code) within the `client` folder. + +``` +my-plugin/ + ├── client/ + │ ├── MyComponent.js + │ └── index.js + ├── server/ + └── index.js +``` + +Our component could look like this: + +```js +import React, {Component} from 'react'; + +class MyButton extends Component { + render() { + return ; + } +} + +export default MyButton; +``` + +Here we create a component that renders a `button`. Now that we created our component we need to specify where it should get injected within Talk! + +To tell Talk where that Component should get injected we need to specify which *Slots* to insert it into. + +```js +import React from 'react'; +export default = () => ; +``` + +### Slots +In Talk we have defined specific *Slots* where we can inject components. + +Here is how we specify our slots config in `my-plugin/index.js` + +```js +import MyButton from './MyButton'; + +export default { + slots: { + commentDetail: [MyButton] + } +}; +``` + +Here I’m specifying that the MyComponent Component will take place within the `commentDetail` in Talk. + +`commentDetail` it’s a specific slot in the CommentStream. It means that it will be embedded inside de comment detail. + +Slots properties take an`Array` so we can add as many components as we want. + +## Building Blocks (TBD) + +`Note: the concepts in this section are still to be implemented. Code samples are for discussion and may change.` + +In order to allow you to build more complex plugins, we have wrapped some of our functionality in higher order components that expose a simple api. + +## Reactions + +Reactions provide users the ability to 'like', 'respect', etc... comments. + +Note: some server side work will need to accompany this client side component. See the like and respect plugins as examples. + +### Building Reactions + +#### Our `client/index.js` : + +```js +import LoveButton from './LoveButton'; + +export default { + slots: { + commentReactions: [LoveButton] + } +}; + +``` +In this example we add our reaction component to the `commentReaction` Slot + +#### Our Reaction component: + +```js +import React from 'react'; +import {withReaction} from 'coral-plugin-api'; + +class LoveButton extends React.Component { + handleClick = () => { + const { + postReaction, + deleteReaction, + alreadyReacted + } = this.props; + + if (alreadyReacted()) { + deleteReaction(); + } else { + postReaction(); + } + }; + + render() { + const {count} = this.props; + return ( + + ); + } +} + +export default withReaction('love')(LoveButton); +``` + + +This feature introduces `withReaction` HOC. `withReaction` takes, as argument, a reaction string and it allows our component to receive specific props for handling reactions. + + * `postReaction` - Posts the reaction + + * `deleteReaction` - Removes the reaction + + * `alreadyReacted` - A function that returns a boolean. + + * `count` - The reaction count + + +For full reference: Please, check `coral-plugin-love`: [LoveButton.js](https://github.com/coralproject/talk/blob/master/plugins/coral-plugin-love/client/LoveButton.js) + +### Comment Stream + +Comment streams may be created with filtering and ordering in place: + +* filter by user +* filter by tag +* sort by date ascending / descending + +### Comment Commit hooks + +// docs for the pre/post comment submit commit hooks + +### Mod Queues + +Moderation queues can be added via configuration objects passed in through plugins. + +Basic mod queues will resemble the current moderation queues but can be generated from different lists of comments. + +* filter by user tag +* filter by comment tag +* filter by comment status +* Custom queries (paired with back end plugins that provide queries to get the data) + +#### Advanced mod queues + +Advanced mod queues can be created giving plugin authors the power to create the cards that appear in the queue, create actions and custom buttons, etc... + +### Custom Configuration + +Plugins may rely on configuration options that admins/moderators can set in the Configuration section. + +Basic settings can be added via json configuration in a plugin. + +* Setting headline +* Setting description +* Setting input type +* Default value +* Variable name + +#### Advanced Custom Configuration (low prioritiy) + +Users can inject configuration interfaces that they create into the configuration allowing for more advanced configuration. + + +## Styling Plugins +Talk uses CSS Modules. This basically means that you can also add your CSS Module to your plugin without colliding with the rest of Talk! + +##### My Component +```js +import styles from './style.css'; + +class MyCoralButton extends Component { + render() { + return ; + } +} +```` + +Our `style.css` should could look like this. +```css + +.button { + background: coral; + border-radius: 3px; +} +``` + +## Plugin Hooks +The plugins injected in the CommentBox such as `commentInputDetailArea` will inherit through props tools for handling hooks. + +### Available hook types: +`preSubmit` : To perform actions before submitting the comment. +`postSubmit` : To perform actions after submitting the comment. + +### Register Hooks +`registerHook` is a function that takes: the hook type, a hook function and returns the hook data. + +#### Usage: +```js + this.addCommentTagHook = this.props.registerHook('postSubmit', (data) => { + const {comment} = data.createComment; + this.props.addCommentTag({ + id: comment.id, + tag: 'OFF_TOPIC' + }); + }); +``` + +### Unregister Hooks + +`unregisterHook` will remove the hook. + +```js + this.props.unregisterHook(this.addCommentTagHook); +``` + +### The server folder and the index file +Read more about the `/server` and how to extend Talk here. +[talk/PLUGINS.md at master · coralproject/talk · GitHub](https://github.com/coralproject/talk/blob/master/PLUGINS.md) diff --git a/docs/plugins/EXPERIMENTAL.md b/docs/plugins/EXPERIMENTAL.md new file mode 100644 index 000000000..895be5900 --- /dev/null +++ b/docs/plugins/EXPERIMENTAL.md @@ -0,0 +1,102 @@ +# Experimental plugins + +Talk plugins are, in essence, small programs that hook into the core application in a variety of ways. Ultimately, this code can do anything that javascript is capable of. In addition, plugins can import any core code to hook into talk at any level. + +If you want to write plugins that integrate with core code beyond the api described in [PLUGINS.md](PLUGINS.md), please keep the following things in mind: + +* core code may change and break your plugin +* you may introduce inefficiencies with your plugin that could hurt performance/crash Talk +* you may cause bugs in other areas of Talk + +If you'd like to build a supported plugin but don't have the hooks you need, please file an issue on this repo and we can discuss deepening the supported plugin api! + +With that said, here's some of the prime experimental integration points: + +## Reducers and Actions : Redux + +Talk is powered by Redux and our plugins can too! Our plugins can have their own reducers and actions. + +```js +import MyButton from './MyButton'; +import reducer from './reducer'; + +export default { + slots: { + commentDetail: [MyButton], + }, + reducer +}; +``` + +## Import Actions from Talk +We can easily trigger `Talk` actions in our plugin Components. + +```js +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {addTag, removeTag} from 'coral-plugin-commentbox/actions'; + +class MyButton extends Component { + render() { + return ; + } +} + +const mapStateToProps = ({commentBox}) => ({commentBox}); + +const mapDispatchToProps = dispatch => + bindActionCreators({addTag, removeTag}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(OffTopicCheckbox); +``` + +## ESlint and Babel +In talk we use `eslint:recommended` and Babel with the latest ECMAScript Features. But you can use your own! +While building your plugin you need to specify a `.eslintrc.json` file and a`.babelrc` file. + +#### `.eslintrc.json` +```json +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} +```` + + +#### `. babelrc ` +```json +{ + "presets": [ + "es2015" + ], + "plugins": [ + "add-module-exports", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-react-jsx" + ] +} +```` diff --git a/docs/plugins/README.md b/docs/plugins/README.md new file mode 100644 index 000000000..ff869b3b0 --- /dev/null +++ b/docs/plugins/README.md @@ -0,0 +1,163 @@ +# Plugins + +The talk platform ships with a plugin architecture featuring that allows +developers to: + +* extend or replace server side graph, rest and auth functionality +* inject functionality into front end embeds and the admin application +* create new front end build targets for embedding +* build plugins from local folders or published via npm/yarn +* deploy plugins throughout the application lifecycle + +## Basic Concepts + +All plugin code lives in the `/plugin` directory. + +Each plugin is provided in a single folder named after the plugin. + +### Naming a Plugin + +Each plugin has a name which must be globally unique. Plugins with name +collisions will not be able to be run together in an instance of Talk. + +If you are creating a plugin for the open source community, we recommend the +following naming convention: + +``` +coral-talk-plugin-[name] +``` + +If you are creating a variant of a plugin for an organization, we recommend +adding the organization's name: + +``` +coral-talk-plugin-[name]-[organization] +``` + +## Plugin Registration + +In order for a Plugin to be active it must be _registered_. + +The parsing order for the plugin registration is as follows: + +- `TALK_PLUGINS_JSON` environment variable +- `plugins.json` file +- `plugins.default.json` file + +If you need to "disable all plugins", you can simply provide `{}` as the +contents of `process.env.TALK_PLUGINS_JSON` or the `plugins.json`. + +### Local Plugins + +The format for plugins.json looks like this: + +```json +{ + "server": [ + "coral-plugin-respect", + "coral-plugin-facebook-auth" + ], + "client": [ + "coral-plugin-respect" + ] +} +``` + +The `server` array specifies which plugins will be loaded when the server +starts. The `client` array specifies which plugins will be built into the +front end bundles. + +Where we have a `server` key with an array of plugins that match the folder +name in the `plugins/` folder. For example, the above config would +require a plugin from `plugins/coral-plugin-respect` and +`plugins/coral-plugin-facebook-auth`. + +### Published Plugins + +If the package is external (available on NPM) you can specify the string for +the version by using an object instead, for example: + +```json +{ + "server": [ + {"people": "^1.2.0"} + ] +} +``` + +### Resolving Plugins + +External plugins can be _resolved_ by running: + +```bash +./bin/cli plugins reconcile +``` + +This achieves two things: + +1. It will traverse into local plugin folders and install their dependencies. + _Note that if the plugin is already installed and available in the node_modules folder, it will not be + fetched again unless there is a version mismatch._ This will result in the + project `package.json` and `yarn.lock` files to be modified, this is normal as + this ensures that repeated deployments (with the same config) will have the + same config, these changes should not be committed to source control. +2. It will seek out dependencies that are listed in the object notation and try + to install them from npm. + +## Plugin Dependencies + +You may also include additional external dependencies in your local packages by +specifying a `package.json` at your plugin root which will result in a +`node_modules` folder being generated at the plugin root with your specific +dependencies. + +## Deployment Solutions + +Plugins can be deployed with a production instance of Talk. + +### Source + +Source deployments can just modify the `plugins.json` file and include any +local plugins into the `plugins/` directory. After including the config, you +need to reconcile the plugins and build the static assets: + +```bash +# get plugin dependancies and remote plugins +./bin/cli plugins reconcile + +# build staic assets (including enabled client side plugins) +yarn build +``` + +Then the application can be started as is. + +If you are working on a plugin, our changes to the plugins will be picked up +naturally by our development scripts: + +```bash +# Watch for changes to client files and rebuild +yarn build-watch +``` + +```bash +# Watch for changes to server files and restart +yarn dev-start +``` + + +### Docker + +If you deploy using Docker, you can extend from the `*-onbuild` image, an +example `Dockerfile` for your project could be: + +```Dockerfile +FROM coralproject/talk:latest-onbuild +``` + +Where the directory for your instance would contain a `plugins.json` file +describing the plugin requirements and a `plugins` directory containing any +other local plugins that should be included. + +Onbuild triggers will execute when the image is building with your custom +configuration and will ensure that the image is ready to use by building all +assets inside the image as well. diff --git a/docs/plugins/SERVER.md b/docs/plugins/SERVER.md new file mode 100644 index 000000000..cb39096be --- /dev/null +++ b/docs/plugins/SERVER.md @@ -0,0 +1,382 @@ +# Server Plugins + +### The Client Folder +The frontend of our plugin lives inside the `client` folder. The `client` folder must have an `index.js` file that exports the configuration of our plugin. + +``` +my-plugin/ + ├── client/ + │ └── ... <-- client side plugin files + ├── server/ + │ └── index.js <-- index for server side functionality + └── index.js <-- base plugin index +``` + +## Specification + +Each plugin should export a single object with all hooks available on it. + +_**Note: You will have access to the whole core and other plugin's typeDefs, +context, loaders, mutators, resolvers, hooks. This is intentional, as it +encourages composing plugins to merge functionality, like a Slack plugin which +provides a Slack notify context function as well as having the loader for +comments.**_ + +The following are the hooks available: + +### GraphQL hooks + +#### Field: `typeDefs` + +```graphql +enum COLOUR { + RED + BLUE +} + +type Person { + name: String! + colour: COLOUR! +} + +type RootMutation { + createPerson(name: String!): Person +} + +type RootQuery { + people: [Person!] +} + +type Subscription { + leader: Person +} +``` + +Thanks to [gql-merge](https://www.npmjs.com/package/gql-merge) the contents of +`typeDefs` should be a string that will be _merged_ with the existing type +definitions. `enum`'s will be appended to, types will be appended, and new types +will be added. + +#### Field: `context` + +```js +{ + Slack: (context) => ({ + notify: (message) => { + // return a promise after we're done sending notifications. + } + }) +} +``` + +Any property provided here will be added to the context parameter available +inside all resolvers, loaders, mutators, and of course, other context based +plugins. + +The top level item must accept a context for the request which it should use to +configure the context plugin before it would be mounted at `context.plugins`. +This plugin above would mount at: `context.plugins.Slack`, or, if you're using +[object destructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), `{plugins: {Slack}}`. + +#### Field: `loaders` + +```js +(context) => ({ + People: { + load: () => db.people.find({user: context.user}) + } +}) +``` + +Loaders should be provided as a function which returns a map which is used in +the resolvers function. These must return a promise or a value. + +#### Field: `mutators` + +```js +(context) => ({ + People: { + create: (name) => { + return db.people.insert({user: context.user, name}); + } + } +}) +``` + +Mutators should be provided as a function which returns a map which is used in +the resolvers function. These must return a promise or a value. + +#### Field: `resolvers` + +```js +{ + Person: { + name(obj, args, context) { + return obj.name; + }, + colour(obj, args, context) { + // Bill likes the colour red, everyone else likes blue. + return obj.name === 'bill' ? 'RED' : 'BLUE'; + } + }, + RootQuery: { + people(obj, args, {loaders: {People}}) { + return People.load(); + } + }, + RootMutation: { + createPerson(obj, {name}, {mutators: {People}}) { + return People.create(name); + } + } +} +``` + +Should return a resolver map as described in the +[Apollo Docs](http://dev.apollodata.com/tools/graphql-tools/resolvers.html#Resolver-map). + +This will merge with the existing resolvers in core and from previous plugins. + +#### Field: `hooks` + +```js +{ + RootMutation: { + createPerson: { + post: async (obj, args, {plugins: {Slack}}, info, person) { + if (!person) { + return person; + } + + await Slack.notify(`A new person just was created with name ${person.name}`); + + return person; + } + } + } +} +``` + +Hooks here are pretty special, for each resolver field, you can specify a +pre/post hook that will execute pre and post field resolution. + +If your post function accepts four parameters, then it can modify the field +result. It is *required* that the function resolves a promise (or returns) with +the modified value or simply the original if you didn't modify it. + +#### Field: `setupFunctions` + +```js +setupFunctions: { + leader: (options, args) => ({ + leader: { + filter: (person) => person.place === 1 + }, + }), +} +``` + +Setup functions allow you to create filters that control which pubsub.publish() events +send data to the client. If the type in question contains args, clients may subscribe using those arguments to further filter their subscription. + +For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions). + +### Routes + +#### Field: `router` + +```js +(router) => { + router.get('/api/v1/people', (req, res) => { + res.json({people: [{name: 'Bob'}]}); + }); +} +``` + +The Router hook allows you to create a function that accepts the base express +router where you can mount any amount of middleware/routes to do any form of +action needed by external applications. + +### Authorization middleware + +The following example creates the requisite callback route and passport +strategy needed to enable Facebook Authorization: + +```js +const authorization = require('middleware/authorization'); + +module.exports = { + router(router) { + router.get('/api/v1/people', authorization.needed('ADMIN'), (req, res) => { + res.json({people: [{name: 'SECRET PEOPLE'}]}); + }); + } +} +``` + +#### Field: `passport` + +```js +const FacebookStrategy = require('passport-facebook').Strategy; +const UsersService = require('services/users'); +const {ValidateUserLogin, HandleAuthPopupCallback} = require('services/passport'); + +module.exports = { + passport(passport) { + 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`, + passReqToCallback: true, + profileFields: ['id', 'displayName', 'picture.type(large)'] + }, async (req, accessToken, refreshToken, profile, done) => { + + let user; + try { + user = await UsersService.findOrCreateExternalUser(profile); + } catch (err) { + return done(err); + } + + return ValidateUserLogin(profile, user, done); + })); + }, + router(router) { + + // Note that we have to import the passport instance here, it is + // instantiated after all the strategies have been mounted. + const {passport} = require('services/passport'); + + /** + * Facebook auth endpoint, this will redirect the user immediatly to facebook + * for authorization. + */ + router.get('/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']})); + + /** + * Facebook callback endpoint, this will send the user a html page designed to + * send back the user credentials upon sucesfull login. + */ + router.get('/facebook/callback', (req, res, next) => { + + // Perform the facebook login flow and pass the data back through the opener. + passport.authenticate('facebook', HandleAuthPopupCallback(req, res, next))(req, res, next); + }); + } +}; +``` + +## Full Example + +Contents of `plugins.json`: + +```json +{ + "server": [ + "people" + ] +} +``` + +Located in `plugins/people/index.js`: + +```js +module.exports = { + typeDefs: ` + enum COLOUR { + RED + BLUE + } + + type Person { + name: String! + colour: COLOUR! + } + + type RootMutation { + createPerson(name: String!): Person + } + + type RootQuery { + people: [Person!] + } + + type Subscription { + leader: Person + } + `, + context: { + Slack: () => ({ + notify: (message) => { + // return a promise after we're done sending notifications. + } + }) + }, + loaders: ({user}) => ({ + People: { + load: () => db.people.find({user}) + } + }), + mutators: ({user}) => ({ + People: { + create: (name) => { + return db.people.insert({user, name}); + } + } + }), + resolvers: { + Person: { + name(obj, args, context) { + return obj.name; + }, + colour(obj, args, context) { + // Bill likes the colour red, everyone else likes blue. + return obj.name === 'bill' ? 'RED' : 'BLUE'; + } + }, + RootQuery: { + people(obj, args, {loaders: {People}}) { + return People.load(); + } + }, + RootMutation: { + createPerson(obj, {name}, {mutators: {People}}) { + return People.create(name); + } + } + }, + hooks: { + RootMutation: { + createPerson: { + post: async (obj, args, {plugins: {Slack}}, info, person) => { + if (!person) { + return person; + } + + await Slack.notify(`A new person just was created with name ${person.name}`); + + return person; + } + } + } + }, + setupFunctions: { + leader: (options, args) => ({ + leader: { + filter: (person) => person.place === 1 + } + } + } +}; + +``` + +## API + +You can access any API available inside the talk directory in a plugin by simply +importing the file relative to the talk project root. An example would be if you +wanted to import the `MetadataService`, you would simply write: + +```javascript +const MetadataService = require('services/metadata'); +``` From bd21719ea50dddcd8da60602cd6023a5f5e899d7 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 24 May 2017 17:20:23 -0300 Subject: [PATCH 13/47] Handling non safari browsers --- client/coral-admin/src/actions/auth.js | 42 +++++++++++++++------- client/coral-framework/actions/auth.js | 24 ++++++++----- client/coral-framework/helpers/response.js | 27 ++++++++------ package.json | 1 + yarn.lock | 4 +++ 5 files changed, 67 insertions(+), 31 deletions(-) diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 29971e294..00ab46910 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,28 +1,44 @@ +import browser from 'detect-browser'; import * as actions from '../constants/auth'; -import * as Storage from 'coral-framework/helpers/storage'; import coralApi from 'coral-framework/helpers/response'; +import * as Storage from 'coral-framework/helpers/storage'; import {handleAuthToken} from 'coral-framework/actions/auth'; //============================================================================== // SIGN IN //============================================================================== -export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => { +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 (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } + dispatch(handleAuthToken(token)); dispatch(checkLoginSuccess(user)); }) - .catch((error) => { + .catch(error => { if (error.translation_key === 'LOGIN_MAXIMUM_EXCEEDED') { dispatch({ type: actions.LOGIN_MAXIMUM_EXCEEDED, @@ -50,11 +66,11 @@ const forgotPassowordFailure = () => ({ type: actions.FETCH_FORGOT_PASSWORD_FAILURE }); -export const requestPasswordReset = (email) => (dispatch) => { +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))); + .catch(error => dispatch(forgotPassowordFailure(error))); }; //============================================================================== @@ -71,23 +87,25 @@ const checkLoginSuccess = (user, isAdmin) => ({ isAdmin }); -const checkLoginFailure = (error) => ({ +const checkLoginFailure = error => ({ type: actions.CHECK_LOGIN_FAILURE, error }); -export const checkLogin = () => (dispatch) => { +export const checkLogin = () => dispatch => { dispatch(checkLoginRequest()); return coralApi('/auth') .then(({user}) => { if (!user) { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } dispatch(checkLoginSuccess(user)); }) - .catch((error) => { + .catch(error => { console.error(error); dispatch(checkLoginFailure(`${error.translation_key}`)); }); diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 6b71fa9dd..071235ce3 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -1,14 +1,14 @@ -import {pym} from 'coral-framework'; -import * as Storage from '../helpers/storage'; -import * as actions from '../constants/auth'; -import coralApi, {base} from '../helpers/response'; import jwtDecode from 'jwt-decode'; +import {pym} from 'coral-framework'; +import browser from 'detect-browser'; +import * as actions from '../constants/auth'; +import * as Storage from '../helpers/storage'; +import coralApi, {base} from '../helpers/response'; const lang = new I18n(translations); import translations from './../translations'; import I18n from '../../coral-framework/modules/i18n/i18n'; -// Dialog Actions export const showSignInDialog = () => (dispatch) => { const signInPopUp = window.open( '/embed/stream/login', @@ -112,8 +112,10 @@ const signInFailure = (error) => ({ //============================================================================== export const handleAuthToken = (token) => (dispatch) => { - Storage.setItem('exp', jwtDecode(token).exp); - Storage.setItem('token', token); + if (!browser || browser.name !== 'safari') { + Storage.setItem('exp', jwtDecode(token).exp); + Storage.setItem('token', token); + } dispatch({type: 'HANDLE_AUTH_TOKEN'}); }; @@ -269,7 +271,9 @@ export const fetchForgotPassword = (email) => (dispatch) => { export const logout = () => (dispatch) => { return coralApi('/auth', {method: 'DELETE'}).then(() => { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } dispatch({type: actions.LOGOUT}); }); }; @@ -292,7 +296,9 @@ export const checkLogin = () => (dispatch) => { coralApi('/auth') .then((result) => { if (!result.user) { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } throw new Error('Not logged in'); } diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index 794a73e30..bad2e52f7 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -1,22 +1,29 @@ -import * as Storage from './storage'; +import browser from 'detect-browser'; const buildOptions = (inputOptions = {}) => { const defaultOptions = { method: 'GET', headers: { Accept: 'application/json', - Authorization: `Bearer ${Storage.getItem('token')}`, 'Content-Type': 'application/json' }, credentials: 'same-origin' }; - let options = Object.assign({}, defaultOptions, inputOptions); - options.headers = Object.assign( - {}, - defaultOptions.headers, - inputOptions.headers - ); + let options = { + defaultOptions, + ...inputOptions + }; + + if (!browser || browser.name !== 'safari') { + let authorization = localStorage.getItem('token'); + + if (authorization) { + options.headers = { + Authorization: `Bearer ${authorization}` + }; + } + } if (options.method.toLowerCase() !== 'get') { options.body = JSON.stringify(options.body); @@ -25,9 +32,9 @@ const buildOptions = (inputOptions = {}) => { return options; }; -const handleResp = (res) => { +const handleResp = res => { if (res.status > 399) { - return res.json().then((err) => { + return res.json().then(err => { let message = err.message || res.status; const error = new Error(); diff --git a/package.json b/package.json index 84d0fdf52..cbf514cf5 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "csurf": "^1.9.0", "dataloader": "^1.3.0", "debug": "^2.6.3", + "detect-browser": "^1.7.0", "dotenv": "^4.0.0", "ejs": "^2.5.6", "env-rewrite": "^1.0.2", diff --git a/yarn.lock b/yarn.lock index 728687248..3fd71e907 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2390,6 +2390,10 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +detect-browser@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-1.7.0.tgz#11758cd6aa07d76c25784036d19154ae0392c3b3" + detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" From 69b19d2c91098bfbb867f7bcffb0035d5c81a40c Mon Sep 17 00:00:00 2001 From: David Erwin Date: Wed, 24 May 2017 16:48:00 -0400 Subject: [PATCH 14/47] Jekyll Initial Commit --- {docs => docs-tmp}/diagrams/architecture.png | Bin {docs => docs-tmp}/diagrams/architecture.xml | 0 {docs => docs-tmp}/diagrams/data-model.png | Bin {docs => docs-tmp}/diagrams/data-model.xml | 0 .../diagrams/moderation-flow-post.png | Bin .../diagrams/moderation-flow-pre.png | Bin .../diagrams/moderation-flow.xml | 0 {docs => docs-tmp}/frontend/DEBUG.md | 0 {docs => docs-tmp}/frontend/HOOKS.md | 0 {docs => docs-tmp}/frontend/IMMUTABLEJS.md | 0 .../frontend/PLUGINS-experimental.md | 0 {docs => docs-tmp}/frontend/PLUGINS.md | 0 {docs => docs-tmp}/frontend/README.md | 0 {docs => docs-tmp}/frontend/REDUX.md | 0 {docs => docs-tmp}/frontend/TEST.md | 0 {docs => docs-tmp}/plugins/CLIENT.md | 0 {docs => docs-tmp}/plugins/EXPERIMENTAL.md | 0 {docs => docs-tmp}/plugins/README.md | 0 {docs => docs-tmp}/plugins/SERVER.md | 0 {docs => docs-tmp}/swagger.yaml | 0 docs/.gitignore | 6 + docs/.vscode/settings.json | 6 + docs/404.md | 6 + docs/Dockerfile | 26 + docs/Gemfile | 4 + docs/Gemfile.lock | 199 + docs/_config.yml | 102 + docs/_data/alerts.yml | 15 + docs/_data/definitions.yml | 0 docs/_data/sidebars/talk_sidebar.yml | 42 + docs/_data/tags.yml | 4 + docs/_data/terms.yml | 0 docs/_data/topnav.yml | 15 + docs/_includes/archive.html | 15 + docs/_includes/callout.html | 1 + docs/_includes/custom/sidebarconfigs.html | 1 + docs/_includes/disqus.html | 16 + docs/_includes/feedback.html | 13 + docs/_includes/footer.html | 9 + docs/_includes/google_analytics.html | 6 + docs/_includes/head.html | 36 + docs/_includes/head_print.html | 33 + docs/_includes/image.html | 1 + docs/_includes/important.html | 1 + docs/_includes/initialize_shuffle.html | 130 + docs/_includes/inline_image.html | 1 + docs/_includes/links.html | 44 + docs/_includes/note.html | 1 + docs/_includes/sidebar.html | 55 + docs/_includes/taglogic.html | 32 + docs/_includes/tip.html | 1 + docs/_includes/toc.html | 21 + docs/_includes/topnav.html | 50 + docs/_includes/warning.html | 1 + docs/_layouts/default.html | 106 + docs/_layouts/default_print.html | 25 + docs/_layouts/page.html | 66 + docs/_layouts/page_print.html | 15 + docs/_layouts/post.html | 39 + docs/configuration.md | 32 + docs/css/bootstrap.min.css | 5 + docs/css/customstyles.css | 1179 ++++ docs/css/font-awesome.min.css | 4 + docs/css/fonts/FontAwesome.otf | Bin 0 -> 93888 bytes docs/css/fonts/fontawesome-webfont.eot | Bin 0 -> 60767 bytes docs/css/fonts/fontawesome-webfont.svg | 565 ++ docs/css/fonts/fontawesome-webfont.ttf | Bin 0 -> 122092 bytes docs/css/fonts/fontawesome-webfont.woff | Bin 0 -> 71508 bytes docs/css/fonts/fontawesome-webfont.woff2 | Bin 0 -> 56780 bytes docs/css/lavish-bootstrap.css | 5898 +++++++++++++++++ docs/css/modern-business.css | 93 + docs/css/printstyles.css | 160 + docs/css/syntax.css | 60 + docs/css/theme-blue.css | 103 + docs/css/theme-green.css | 99 + docs/feed.xml | 32 + docs/fonts/FontAwesome.otf | Bin 0 -> 85908 bytes docs/fonts/fontawesome-webfont.eot | Bin 0 -> 56006 bytes docs/fonts/fontawesome-webfont.svg | 520 ++ docs/fonts/fontawesome-webfont.ttf | Bin 0 -> 112160 bytes docs/fonts/fontawesome-webfont.woff | Bin 0 -> 65452 bytes docs/fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes docs/fonts/glyphicons-halflings-regular.svg | 288 + docs/fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes docs/fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes docs/fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes docs/images/communicorn.jpg | Bin 0 -> 119335 bytes docs/images/company_logo.png | Bin 0 -> 13330 bytes docs/images/favicon.ico | Bin 0 -> 177352 bytes docs/index.md | 66 + docs/install-docker.md | 147 + docs/install-setup.md | 38 + docs/install-source.md | 80 + docs/js/customscripts.js | 55 + docs/js/jekyll-search.js | 1 + docs/js/jquery.ba-throttle-debounce.min.js | 9 + docs/js/jquery.localScroll.min.js | 7 + docs/js/jquery.navgoco.min.js | 8 + docs/js/jquery.scrollTo.min.js | 7 + docs/js/jquery.shuffle.min.js | 1588 +++++ docs/js/mydoc_scroll.html | 240 + docs/js/toc.js | 82 + docs/licenses/LICENSE | 21 + docs/licenses/LICENSE-BSD-NAVGOCO.txt | 27 + docs/pages/plugins/client.md | 278 + docs/plugins-client.md | 327 + docs/plugins-experimental.md | 63 + docs/plugins-server.md | 390 ++ docs/sitemap.xml | 24 + docs/update.sh | 4 + 110 files changed, 13644 insertions(+) rename {docs => docs-tmp}/diagrams/architecture.png (100%) rename {docs => docs-tmp}/diagrams/architecture.xml (100%) rename {docs => docs-tmp}/diagrams/data-model.png (100%) rename {docs => docs-tmp}/diagrams/data-model.xml (100%) rename {docs => docs-tmp}/diagrams/moderation-flow-post.png (100%) rename {docs => docs-tmp}/diagrams/moderation-flow-pre.png (100%) rename {docs => docs-tmp}/diagrams/moderation-flow.xml (100%) rename {docs => docs-tmp}/frontend/DEBUG.md (100%) rename {docs => docs-tmp}/frontend/HOOKS.md (100%) rename {docs => docs-tmp}/frontend/IMMUTABLEJS.md (100%) rename {docs => docs-tmp}/frontend/PLUGINS-experimental.md (100%) rename {docs => docs-tmp}/frontend/PLUGINS.md (100%) rename {docs => docs-tmp}/frontend/README.md (100%) rename {docs => docs-tmp}/frontend/REDUX.md (100%) rename {docs => docs-tmp}/frontend/TEST.md (100%) rename {docs => docs-tmp}/plugins/CLIENT.md (100%) rename {docs => docs-tmp}/plugins/EXPERIMENTAL.md (100%) rename {docs => docs-tmp}/plugins/README.md (100%) rename {docs => docs-tmp}/plugins/SERVER.md (100%) rename {docs => docs-tmp}/swagger.yaml (100%) create mode 100644 docs/.gitignore create mode 100644 docs/.vscode/settings.json create mode 100644 docs/404.md create mode 100644 docs/Dockerfile create mode 100644 docs/Gemfile create mode 100644 docs/Gemfile.lock create mode 100644 docs/_config.yml create mode 100644 docs/_data/alerts.yml create mode 100644 docs/_data/definitions.yml create mode 100644 docs/_data/sidebars/talk_sidebar.yml create mode 100644 docs/_data/tags.yml create mode 100644 docs/_data/terms.yml create mode 100644 docs/_data/topnav.yml create mode 100644 docs/_includes/archive.html create mode 100644 docs/_includes/callout.html create mode 100644 docs/_includes/custom/sidebarconfigs.html create mode 100644 docs/_includes/disqus.html create mode 100644 docs/_includes/feedback.html create mode 100755 docs/_includes/footer.html create mode 100644 docs/_includes/google_analytics.html create mode 100644 docs/_includes/head.html create mode 100644 docs/_includes/head_print.html create mode 100644 docs/_includes/image.html create mode 100644 docs/_includes/important.html create mode 100644 docs/_includes/initialize_shuffle.html create mode 100644 docs/_includes/inline_image.html create mode 100644 docs/_includes/links.html create mode 100644 docs/_includes/note.html create mode 100644 docs/_includes/sidebar.html create mode 100644 docs/_includes/taglogic.html create mode 100644 docs/_includes/tip.html create mode 100644 docs/_includes/toc.html create mode 100644 docs/_includes/topnav.html create mode 100644 docs/_includes/warning.html create mode 100644 docs/_layouts/default.html create mode 100644 docs/_layouts/default_print.html create mode 100644 docs/_layouts/page.html create mode 100644 docs/_layouts/page_print.html create mode 100644 docs/_layouts/post.html create mode 100644 docs/configuration.md create mode 100755 docs/css/bootstrap.min.css create mode 100644 docs/css/customstyles.css create mode 100644 docs/css/font-awesome.min.css create mode 100644 docs/css/fonts/FontAwesome.otf create mode 100644 docs/css/fonts/fontawesome-webfont.eot create mode 100644 docs/css/fonts/fontawesome-webfont.svg create mode 100644 docs/css/fonts/fontawesome-webfont.ttf create mode 100644 docs/css/fonts/fontawesome-webfont.woff create mode 100644 docs/css/fonts/fontawesome-webfont.woff2 create mode 100644 docs/css/lavish-bootstrap.css create mode 100755 docs/css/modern-business.css create mode 100644 docs/css/printstyles.css create mode 100644 docs/css/syntax.css create mode 100644 docs/css/theme-blue.css create mode 100644 docs/css/theme-green.css create mode 100644 docs/feed.xml create mode 100644 docs/fonts/FontAwesome.otf create mode 100644 docs/fonts/fontawesome-webfont.eot create mode 100644 docs/fonts/fontawesome-webfont.svg create mode 100644 docs/fonts/fontawesome-webfont.ttf create mode 100644 docs/fonts/fontawesome-webfont.woff create mode 100644 docs/fonts/glyphicons-halflings-regular.eot create mode 100644 docs/fonts/glyphicons-halflings-regular.svg create mode 100644 docs/fonts/glyphicons-halflings-regular.ttf create mode 100644 docs/fonts/glyphicons-halflings-regular.woff create mode 100644 docs/fonts/glyphicons-halflings-regular.woff2 create mode 100644 docs/images/communicorn.jpg create mode 100644 docs/images/company_logo.png create mode 100644 docs/images/favicon.ico create mode 100644 docs/index.md create mode 100644 docs/install-docker.md create mode 100644 docs/install-setup.md create mode 100644 docs/install-source.md create mode 100644 docs/js/customscripts.js create mode 100644 docs/js/jekyll-search.js create mode 100644 docs/js/jquery.ba-throttle-debounce.min.js create mode 100644 docs/js/jquery.localScroll.min.js create mode 100755 docs/js/jquery.navgoco.min.js create mode 100644 docs/js/jquery.scrollTo.min.js create mode 100644 docs/js/jquery.shuffle.min.js create mode 100644 docs/js/mydoc_scroll.html create mode 100644 docs/js/toc.js create mode 100644 docs/licenses/LICENSE create mode 100644 docs/licenses/LICENSE-BSD-NAVGOCO.txt create mode 100644 docs/pages/plugins/client.md create mode 100644 docs/plugins-client.md create mode 100644 docs/plugins-experimental.md create mode 100644 docs/plugins-server.md create mode 100644 docs/sitemap.xml create mode 100755 docs/update.sh diff --git a/docs/diagrams/architecture.png b/docs-tmp/diagrams/architecture.png similarity index 100% rename from docs/diagrams/architecture.png rename to docs-tmp/diagrams/architecture.png diff --git a/docs/diagrams/architecture.xml b/docs-tmp/diagrams/architecture.xml similarity index 100% rename from docs/diagrams/architecture.xml rename to docs-tmp/diagrams/architecture.xml diff --git a/docs/diagrams/data-model.png b/docs-tmp/diagrams/data-model.png similarity index 100% rename from docs/diagrams/data-model.png rename to docs-tmp/diagrams/data-model.png diff --git a/docs/diagrams/data-model.xml b/docs-tmp/diagrams/data-model.xml similarity index 100% rename from docs/diagrams/data-model.xml rename to docs-tmp/diagrams/data-model.xml diff --git a/docs/diagrams/moderation-flow-post.png b/docs-tmp/diagrams/moderation-flow-post.png similarity index 100% rename from docs/diagrams/moderation-flow-post.png rename to docs-tmp/diagrams/moderation-flow-post.png diff --git a/docs/diagrams/moderation-flow-pre.png b/docs-tmp/diagrams/moderation-flow-pre.png similarity index 100% rename from docs/diagrams/moderation-flow-pre.png rename to docs-tmp/diagrams/moderation-flow-pre.png diff --git a/docs/diagrams/moderation-flow.xml b/docs-tmp/diagrams/moderation-flow.xml similarity index 100% rename from docs/diagrams/moderation-flow.xml rename to docs-tmp/diagrams/moderation-flow.xml diff --git a/docs/frontend/DEBUG.md b/docs-tmp/frontend/DEBUG.md similarity index 100% rename from docs/frontend/DEBUG.md rename to docs-tmp/frontend/DEBUG.md diff --git a/docs/frontend/HOOKS.md b/docs-tmp/frontend/HOOKS.md similarity index 100% rename from docs/frontend/HOOKS.md rename to docs-tmp/frontend/HOOKS.md diff --git a/docs/frontend/IMMUTABLEJS.md b/docs-tmp/frontend/IMMUTABLEJS.md similarity index 100% rename from docs/frontend/IMMUTABLEJS.md rename to docs-tmp/frontend/IMMUTABLEJS.md diff --git a/docs/frontend/PLUGINS-experimental.md b/docs-tmp/frontend/PLUGINS-experimental.md similarity index 100% rename from docs/frontend/PLUGINS-experimental.md rename to docs-tmp/frontend/PLUGINS-experimental.md diff --git a/docs/frontend/PLUGINS.md b/docs-tmp/frontend/PLUGINS.md similarity index 100% rename from docs/frontend/PLUGINS.md rename to docs-tmp/frontend/PLUGINS.md diff --git a/docs/frontend/README.md b/docs-tmp/frontend/README.md similarity index 100% rename from docs/frontend/README.md rename to docs-tmp/frontend/README.md diff --git a/docs/frontend/REDUX.md b/docs-tmp/frontend/REDUX.md similarity index 100% rename from docs/frontend/REDUX.md rename to docs-tmp/frontend/REDUX.md diff --git a/docs/frontend/TEST.md b/docs-tmp/frontend/TEST.md similarity index 100% rename from docs/frontend/TEST.md rename to docs-tmp/frontend/TEST.md diff --git a/docs/plugins/CLIENT.md b/docs-tmp/plugins/CLIENT.md similarity index 100% rename from docs/plugins/CLIENT.md rename to docs-tmp/plugins/CLIENT.md diff --git a/docs/plugins/EXPERIMENTAL.md b/docs-tmp/plugins/EXPERIMENTAL.md similarity index 100% rename from docs/plugins/EXPERIMENTAL.md rename to docs-tmp/plugins/EXPERIMENTAL.md diff --git a/docs/plugins/README.md b/docs-tmp/plugins/README.md similarity index 100% rename from docs/plugins/README.md rename to docs-tmp/plugins/README.md diff --git a/docs/plugins/SERVER.md b/docs-tmp/plugins/SERVER.md similarity index 100% rename from docs/plugins/SERVER.md rename to docs-tmp/plugins/SERVER.md diff --git a/docs/swagger.yaml b/docs-tmp/swagger.yaml similarity index 100% rename from docs/swagger.yaml rename to docs-tmp/swagger.yaml 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: '