Merge pull request #348 from coralproject/whizard-whitelist

Permitted Domains for Talk Install
This commit is contained in:
Belén Curcio
2017-02-28 21:06:16 -03:00
committed by GitHub
14 changed files with 209 additions and 77 deletions
+32 -22
View File
@@ -20,13 +20,17 @@ const validation = (formData, dispatch, next) => {
return dispatch(hasError());
}
const validKeys = Object.keys(formData)
.filter(name => name !== 'domains');
// Required Validation
const empty = Object.keys(formData).filter(name => {
const empty = validKeys
.filter(name => {
const cond = !formData[name].length;
if (cond) {
// Adding Error
// Adding Error
dispatch(addError(name, 'This field is required.'));
} else {
dispatch(addError(name, ''));
@@ -40,18 +44,19 @@ const validation = (formData, dispatch, next) => {
}
// RegExp Validation
const validation = Object.keys(formData).filter(name => {
const cond = !validate[name](formData[name]);
if (cond) {
const validation = validKeys
.filter(name => {
const cond = !validate[name](formData[name]);
if (cond) {
// Adding Error
dispatch(addError(name, errorMsj[name]));
} else {
dispatch(addError(name, ''));
}
dispatch(addError(name, errorMsj[name]));
} else {
dispatch(addError(name, ''));
}
return cond;
});
return cond;
});
if (validation.length) {
return dispatch(hasError());
@@ -71,22 +76,27 @@ export const submitSettings = () => (dispatch, getState) => {
export const submitUser = () => (dispatch, getState) => {
const userFormData = getState().install.toJS().data.user;
validation(userFormData, dispatch, function() {
const data = getState().install.toJS().data;
dispatch(installRequest());
coralApi('/setup', {method: 'POST', body: data})
.then(result => {
dispatch(installSuccess(result));
dispatch(nextStep());
})
.catch(error => {
console.error(error);
dispatch(installFailure(`${error.translation_key}`));
});
dispatch(nextStep());
});
};
export const finishInstall = () => (dispatch, getState) => {
const data = getState().install.toJS().data;
dispatch(installRequest());
return coralApi('/setup', {method: 'POST', body: data})
.then(() => {
dispatch(installSuccess());
dispatch(nextStep());
})
.catch(error => {
console.error(error);
dispatch(installFailure(`${error.translation_key}`));
});
};
export const updateSettingsFormData = (name, value) => ({type: actions.UPDATE_FORMDATA_SETTINGS, name, value});
export const updateUserFormData = (name, value) => ({type: actions.UPDATE_FORMDATA_USER, name, value});
export const updatePermittedDomains = (value) => ({type: actions.UPDATE_PERMITTED_DOMAINS_SETTINGS, value});
const checkInstallRequest = () => ({type: actions.CHECK_INSTALL_REQUEST});
const checkInstallSuccess = installed => ({type: actions.CHECK_INSTALL_SUCCESS, installed});
@@ -10,6 +10,7 @@ export const INSTALL_SUCCESS = 'INSTALL_SUCCESS';
export const INSTALL_FAILURE = 'INSTALL_FAILURE';
export const UPDATE_FORMDATA_USER = 'UPDATE_FORMDATA_USER';
export const UPDATE_FORMDATA_SETTINGS = 'UPDATE_FORMDATA_SETTINGS';
export const UPDATE_PERMITTED_DOMAINS_SETTINGS = 'UPDATE_PERMITTED_DOMAINS_SETTINGS';
export const CHECK_INSTALL_REQUEST = 'CHECK_INSTALL_REQUEST';
export const CHECK_INSTALL_SUCCESS = 'CHECK_INSTALL_SUCCESS';
@@ -1,26 +1,28 @@
import React from 'react';
import {Card} from 'coral-ui';
import styles from './Configure.css';
import TagsInput from 'react-tagsinput';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
import TagsInput from 'react-tagsinput';
import styles from './Configure.css';
import {Card} from 'coral-ui';
const lang = new I18n(translations);
const Domainlist = ({domains, onChangeDomainlist}) => (
<div>
<h3>{lang.t('configure.domain-list-title')}</h3>
<Card id={styles.domainlist}>
<p className={styles.domainlistDesc}>{lang.t('configure.domain-list-text')}</p>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
addOnPaste={true}
pasteSplit={data => data.split(',').map(d => d.trim())}
onChange={tags => onChangeDomainlist('whitelist', tags)}
/>
</Card>
</div>
);
const Domainlist = ({domains, onChangeDomainlist}) => {
return (
<div>
<h3>{lang.t('configure.domain-list-title')}</h3>
<Card id={styles.domainlist}>
<p className={styles.domainlistDesc}>{lang.t('configure.domain-list-text')}</p>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
addOnPaste={true}
pasteSplit={data => data.split(',').map(d => d.trim())}
onChange={tags => onChangeDomainlist('whitelist', tags)}
/>
</Card>
</div>
);
};
export default Domainlist;
const lang = new I18n(translations);
@@ -5,19 +5,22 @@ import {Wizard, WizardNav} from 'coral-ui';
import {Layout} from '../../components/ui/Layout';
import {
nextStep,
previousStep,
goToStep,
nextStep,
submitUser,
checkInstall,
previousStep,
finishInstall,
submitSettings,
updateUserFormData,
updateSettingsFormData,
submitSettings,
submitUser,
checkInstall
updatePermittedDomains
} from '../../actions/install';
import InitialStep from './components/Steps/InitialStep';
import AddOrganizationName from './components/Steps/AddOrganizationName';
import CreateYourAccount from './components/Steps/CreateYourAccount';
import PermittedDomainsStep from './components/Steps/PermittedDomainsStep';
import FinalStep from './components/Steps/FinalStep';
class InstallContainer extends Component {
@@ -43,6 +46,7 @@ class InstallContainer extends Component {
<InitialStep/>
<AddOrganizationName/>
<CreateYourAccount/>
<PermittedDomainsStep/>
<FinalStep/>
</Wizard>
</div>
@@ -65,10 +69,14 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => ({
checkInstall: next => dispatch(checkInstall(next)),
nextStep: () => dispatch(nextStep()),
previousStep: () => dispatch(previousStep()),
goToStep: step => dispatch(goToStep(step)),
previousStep: () => dispatch(previousStep()),
finishInstall: () => dispatch(finishInstall()),
checkInstall: next => dispatch(checkInstall(next)),
handleDomainsChange: value => {
dispatch(updatePermittedDomains(value));
},
handleSettingsChange: e => {
const {name, value} = e.currentTarget;
dispatch(updateSettingsFormData(name, value));
@@ -2,25 +2,26 @@ import React from 'react';
import styles from './style.css';
import {TextField, Button} from 'coral-ui';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const AddOrganizationName = props => {
const {handleSettingsChange, handleSettingsSubmit, install} = props;
return (
<div className={styles.step}>
<p>
Please tell us the name of your organization. This will appear in emails when
inviting new team members
</p>
<p>{lang.t('ADD_ORGANIZATION.DESCRIPTION')}</p>
<div className={styles.form}>
<form onSubmit={handleSettingsSubmit}>
<TextField
className={styles.TextField}
id="organizationName"
type="text"
label='Organization name'
label={lang.t('ADD_ORGANIZATION.LABEL')}
onChange={handleSettingsChange}
showErrors={install.showErrors}
errorMsg={install.errors.organizationName} />
<Button type="submit" cStyle='black' full>Save</Button>
<Button type="submit" cStyle='black' full>{lang.t('ADD_ORGANIZATION.SAVE')}</Button>
</form>
</div>
</div>
@@ -2,6 +2,10 @@ import React from 'react';
import styles from './style.css';
import {TextField, Button, Spinner} from 'coral-ui';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const InitialStep = props => {
const {handleUserChange, handleUserSubmit, install} = props;
return (
@@ -12,7 +16,7 @@ const InitialStep = props => {
className={styles.textField}
id="email"
type="email"
label='Email address'
label={lang.t('CREATE.EMAIL')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.email}
@@ -23,7 +27,7 @@ const InitialStep = props => {
className={styles.textField}
id="username"
type="text"
label='Username'
label={lang.t('CREATE.USERNAME')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.username}
@@ -33,7 +37,7 @@ const InitialStep = props => {
className={styles.textField}
id="password"
type="password"
label='Password'
label={lang.t('CREATE.PASSWORD')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.password}
@@ -43,7 +47,7 @@ const InitialStep = props => {
className={styles.textField}
id="confirmPassword"
type="password"
label='Confirm Password'
label={lang.t('CREATE.CONFIRM_PASSWORD')}
onChange={handleUserChange}
showErrors={install.showErrors}
errorMsg={install.errors.confirmPassword}
@@ -51,7 +55,7 @@ const InitialStep = props => {
{
!props.install.isLoading ?
<Button cStyle='black' type="submit" full>Save</Button>
<Button cStyle='black' type="submit" full>{lang.t('CREATE.SAVE')}</Button>
:
<Spinner />
}
@@ -3,16 +3,16 @@ import styles from './style.css';
import {Button} from 'coral-ui';
import {Link} from 'react-router';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const InitialStep = () => {
return (
<div className={`${styles.step} ${styles.finalStep}`}>
<p>
Thanks for installing Talk! We sent an email to verify your email
address. While you finish setting the account, you can start engaging
with your readers now.
</p>
<Button raised><Link to='/admin'>Launch Talk</Link></Button>
<Button cStyle='black' raised><a href="http://coralproject.net">Close this Installer</a></Button>
<p>{lang.t('FINAL.DESCRIPTION')}</p>
<Button raised><Link to='/admin'>{lang.t('FINAL.LAUNCH')}</Link></Button>
<Button cStyle='black' raised><a href="http://coralproject.net">{lang.t('FINAL.CLOSE')}</a></Button>
</div>
);
};
@@ -2,16 +2,16 @@ import React from 'react';
import styles from './style.css';
import {Button} from 'coral-ui';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const InitialStep = props => {
const {nextStep} = props;
return (
<div className={styles.step}>
<p>
The remainder of the Talk installation will take about ten minutes.
Once you complete the following two steps, you will have a free
installation and provision of Mongo and Redis.
</p>
<Button cStyle='green' onClick={nextStep} raised>Get Started</Button>
<p>{lang.t('INITIAL.DESCRIPTION')}</p>
<Button cStyle='green' onClick={nextStep} raised>{lang.t('INITIAL.SUBMIT')}</Button>
</div>
);
};
@@ -0,0 +1,31 @@
import React from 'react';
import styles from './style.css';
import {Button, Card} from 'coral-ui';
import TagsInput from 'react-tagsinput';
const lang = new I18n(translations);
import translations from '../../translations.json';
import I18n from 'coral-framework/modules/i18n/i18n';
const PermittedDomainsStep = props => {
const {finishInstall, install, handleDomainsChange} = props;
const domains = install.data.settings.domains.whitelist;
return (
<div className={styles.step}>
<h3>{lang.t('PERMITTED_DOMAINS.TITLE')}</h3>
<Card className={styles.card}>
<p>{lang.t('PERMITTED_DOMAINS.DESCRIPTION')}</p>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
addOnPaste={true}
pasteSplit={data => data.split(',').map(d => d.trim())}
onChange={tags => handleDomainsChange(tags)}
/>
</Card>
<Button cStyle='green' onClick={finishInstall} raised>{lang.t('PERMITTED_DOMAINS.SUBMIT')}</Button>
</div>
);
};
export default PermittedDomainsStep;
@@ -59,6 +59,12 @@
}
}
}
.card {
max-width: 500px;
margin: 20px auto;
text-align: left;
}
}
.finalStep {
@@ -0,0 +1,58 @@
{
"en": {
"INITIAL" : {
"DESCRIPTION": "Let's set up your Talk community in just a few short steps.",
"SUBMIT": "Get Started"
},
"ADD_ORGANIZATION": {
"DESCRIPTION": "Please tell us the name of your organization. This will appear in emails when inviting new team members.",
"LABEL": "Organization Name",
"SAVE": "Save"
},
"CREATE": {
"EMAIL": "Email address",
"USERNAME": "Username",
"PASSWORD": "Password",
"CONFIRM_PASSWORD": "Confirm Password",
"SAVE": "Save"
},
"PERMITTED_DOMAINS": {
"TITLE": "Permitted domains",
"DESCRIPTION": "Enter the domains you would like to permit for Talk, e.g. your local, staging and production environments (ex. localhost:3000, staging.domain.com, domain.com).",
"SUBMIT": "Finish install"
},
"FINAL": {
"DESCRIPTION": "Thanks for installing Talk! We sent an email to verify your email address. While you finish setting up the account, you can start engaging with your readers now.",
"LAUNCH": "Launch Talk",
"CLOSE": "Close this Installer"
}
},
"es": {
"INITIAL" : {
"DESCRIPTION": "Configuremos tu comunidad de Talk en sólo algunos pasos.",
"SUBMIT": "Empezá!"
},
"ADD_ORGANIZATION": {
"DESCRIPTION": "Por favor, dinos el nombre de tu organización. Este aparecerá en los emails cuando invites nuevos miembros.",
"LABEL": "Nombre de la Organización",
"SAVE": "Guardar"
},
"CREATE": {
"EMAIL": "Dirección de E-Mail",
"USERNAME": "Usuario",
"PASSWORD": "Contraseña",
"CONFIRM_PASSWORD": "Confirmar contraseña",
"SAVE": "Guardar"
},
"PERMITTED_DOMAINS": {
"TITLE": "Dominios Permitidos",
"DESCRIPTION": "Agrega dominios permitidos a Talk, e.g. tu localhost, staging y ambientes de production (ex. localhost:3000, staging.domain.com, domain.com).",
"SUBMIT": "Finalizar instalación"
},
"FINAL": {
"DESCRIPTION": "Gracias por instalar Talk! Te enviamos un email para verificar tu identidad. Mientras se termina de configurar la cuenta, ya puedes empezar a interactuar con tus lectores",
"LAUNCH": "Lanzar Talk",
"CLOSE": "Cerrar este instalador"
}
}
}
+12 -2
View File
@@ -1,4 +1,4 @@
import {Map} from 'immutable';
import {Map, List} from 'immutable';
import * as actions from '../constants/install';
@@ -6,7 +6,10 @@ const initialState = Map({
isLoading: false,
data: Map({
settings: Map({
organizationName: ''
organizationName: '',
domains: Map({
whitelist: List()
})
}),
user: Map({
username: '',
@@ -33,6 +36,10 @@ const initialState = Map({
{
text: '2. Create your account',
step: 2
},
{
text: '3. Domain Whitelist',
step: 3
}],
installRequest: null,
installRequestError: null,
@@ -50,6 +57,9 @@ export default function install (state = initialState, action) {
case actions.GO_TO_STEP:
return state
.set('step', action.step);
case actions.UPDATE_PERMITTED_DOMAINS_SETTINGS:
return state
.setIn(['data', 'settings', 'domains', 'whitelist'], action.value);
case actions.UPDATE_FORMDATA_SETTINGS:
return state
.setIn(['data', 'settings', action.name], action.value);
+2 -2
View File
@@ -86,7 +86,7 @@
"comment-count-text-post": " characters.",
"comment-count-error": "Please enter a valid number.",
"domain-list-title": "Domain Whitelist",
"domain-list-text": "Some instructions on how to type the urls."
"domain-list-text": "Enter the domains you would like to permit for Talk, e.g. your local, staging and production environments (ex. localhost:3000, staging.domain.com, domain.com)."
},
"bandialog": {
"ban_user": "Ban User?",
@@ -207,7 +207,7 @@
"comment-count-text-post": " caracteres",
"comment-count-error": "Por favor escribe un número válido.",
"domain-list-title": "Lista de Dominios Permitidos",
"domain-list-text": "Instrucciones de como ingresar las URLs."
"domain-list-text": "Agrega dominios permitidos a Talk, e.g. tu localhost, staging y ambientes de production (ex. localhost:3000, staging.domain.com, domain.com)."
},
"embedlink": {
"copy": "Copiar"
+1
View File
@@ -13,6 +13,7 @@
position: relative;
padding-left: 40px;
border-radius: 1px;
min-height: 24px;
&:hover {
cursor: pointer;