Merge pull request #296 from guillegiu/master

fix bug #288 and add validatios
This commit is contained in:
Ivan Diaz 2018-09-11 18:09:20 -03:00 committed by GitHub
commit 3b5d23b78b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 226 additions and 128 deletions

View File

@ -290,7 +290,7 @@ class StaffEditor extends React.Component {
API.call({
path: '/staff/edit',
data: {
staffId: this.props.staffId,
staffId: (!this.props.myAccount) ? this.props.staffId : null,
sendEmailOnNewTicket: (eventType === 'SEND_EMAIL_ON_NEW_TICKET') ? form.sendEmailOnNewTicket * 1 : null,
email: (eventType === 'EMAIL') ? form.email : null,
password: (eventType === 'PASSWORD') ? form.password : null,
@ -331,7 +331,7 @@ class StaffEditor extends React.Component {
path: '/staff/edit',
dataAsForm: true,
data: {
staffId: this.props.staffId,
staffId: (!this.props.myAcount) ? this.props.staffId : null,
file: event.target.files[0]
}
}).then(() => {

View File

@ -32,7 +32,7 @@ class AdminPanelMyTickets extends React.Component {
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()}/>}
<div style={{textAlign: 'right', marginTop: 10}}>
<Button onClick={this.onCreateTicket.bind(this)} type="secondary" size="medium">
<Icon size="sm" name="plus"/> Create ticket
<Icon size="sm" name="plus"/> {i18n('CREATE_TICKET')}
</Button>
</div>
</div>
@ -55,7 +55,7 @@ class AdminPanelMyTickets extends React.Component {
<div>
<CreateTicketForm onSuccess={this.onCreateTicketSuccess.bind(this)} />
<div style={{textAlign: 'center'}}>
<Button onClick={ModalContainer.closeModal} type="link">Close</Button>
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
</div>
</div>
);

View File

@ -9,6 +9,9 @@ import Table from 'core-components/table';
import SearchBox from 'core-components/search-box';
import Button from 'core-components/button';
import Message from 'core-components/message';
import Icon from 'core-components/icon';
import ModalContainer from 'app-components/modal-container';
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
class AdminPanelListUsers extends React.Component {
@ -37,6 +40,11 @@ class AdminPanelListUsers extends React.Component {
<Header title={i18n('LIST_USERS')} description={i18n('LIST_USERS_DESCRIPTION')} />
<SearchBox className="admin-panel-list-users__search-box" placeholder={i18n('SEARCH_USERS')} onSearch={this.onSearch.bind(this)} />
{(this.state.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_USERS')}</Message> : <Table {...this.getTableProps()}/>}
<div style={{textAlign: 'right', marginTop: 10}}>
<Button onClick={this.onCreateUser.bind(this)} type="secondary" size="medium">
<Icon size="sm" name="plus"/> {i18n('ADD_USER')}
</Button>
</div>
</div>
);
}
@ -149,6 +157,20 @@ class AdminPanelListUsers extends React.Component {
}).then(this.onUsersRetrieved.bind(this)).catch(this.onUsersRejected.bind(this));
}
onCreateUser(user) {
ModalContainer.openModal(
<div className="admin-panel-list-users__add-user-form">
<MainSignUpWidget onSuccess={this.onCreateUserSuccess.bind(this)} />
<div style={{textAlign: 'center'}}>
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
</div>
</div>
);
}
onCreateUserSuccess() {
ModalContainer.closeModal();
}
onUsersRetrieved(result) {
this.setState({
page: result.data.page * 1,

View File

@ -21,4 +21,8 @@
display: inline-block;
text-align: center;
}
}
&__add-user-form {
max-width: 500px;
}
}

View File

@ -1,112 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
import i18n from 'lib-app/i18n';
import API from 'lib-app/api-call';
import Captcha from 'app/main/captcha';
import SubmitButton from 'core-components/submit-button';
import Message from 'core-components/message';
import Form from 'core-components/form';
import FormField from 'core-components/form-field';
import Widget from 'core-components/widget';
import Header from 'core-components/header';
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
class MainSignUpPageWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
email: null
};
}
class MainSignUpPage extends React.Component {
render() {
return (
<div className="main-signup-page">
<Widget className="signup-widget col-md-6 col-md-offset-3">
<Header title={i18n('SIGN_UP')} description={i18n('SIGN_UP_VIEW_DESCRIPTION')} />
<Form {...this.getFormProps()}>
<div className="signup-widget__inputs">
<FormField {...this.getInputProps()} label={i18n('FULL_NAME')} name="name" validation="NAME" required/>
<FormField {...this.getInputProps()} label={i18n('EMAIL')} name="email" validation="EMAIL" required/>
<FormField {...this.getInputProps(true)} label={i18n('PASSWORD')} name="password" validation="PASSWORD" required/>
<FormField {...this.getInputProps(true)} label={i18n('REPEAT_PASSWORD')} name="repeated-password" validation="REPEAT_PASSWORD" required/>
</div>
<div className="signup-widget__captcha">
<Captcha ref="captcha"/>
</div>
<SubmitButton type="primary">{i18n('SIGN_UP')}</SubmitButton>
</Form>
{this.renderMessage()}
</Widget>
<MainSignUpWidget {...this.props} className="col-md-6 col-md-offset-3" />
</div>
);
}
renderMessage() {
switch (this.state.message) {
case 'success':
return <Message type="success">{i18n('SIGNUP_SUCCESS')}</Message>;
case 'fail':
return <Message type="error">{i18n('EMAIL_EXISTS')}</Message>;
default:
return null;
}
}
getFormProps() {
return {
loading: this.state.loading,
className: 'signup-widget__form',
onSubmit: this.onSignupFormSubmit.bind(this)
};
}
getInputProps(password) {
return {
className: 'signup-widget__input',
fieldProps: {
size: 'medium',
password: password
}
};
}
onSignupFormSubmit(formState) {
const captcha = this.refs.captcha.getWrappedInstance();
if (!captcha.getValue()) {
captcha.focus();
} else {
this.setState({
loading: true
});
API.call({
path: '/user/signup',
data: _.extend({captcha: captcha.getValue()}, formState)
}).then(this.onSignupSuccess.bind(this)).catch(this.onSignupFail.bind(this));
}
}
onSignupSuccess() {
this.setState({
loading: false,
message: 'success'
});
}
onSignupFail() {
this.setState({
loading: false,
message: 'fail'
});
}
}
export default MainSignUpPageWidget;
export default MainSignUpPage;

View File

@ -1,23 +1,3 @@
.main-signup-page {
min-height: 669px;
.signup-widget {
padding: 30px;
text-align: center;
&__form {
margin-bottom: 20px;
}
&__inputs {
display: inline-block;
margin: 0 auto;
}
&__captcha {
margin: 10px auto 20px;
height: 78px;
width: 304px;
}
}
}
}

View File

@ -0,0 +1,123 @@
import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
import classNames from 'classnames';
import i18n from 'lib-app/i18n';
import API from 'lib-app/api-call';
import Captcha from 'app/main/captcha';
import SubmitButton from 'core-components/submit-button';
import Message from 'core-components/message';
import Form from 'core-components/form';
import FormField from 'core-components/form-field';
import Widget from 'core-components/widget';
import Header from 'core-components/header';
class MainSignUpWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
email: null
};
}
static propTypes = {
onSuccess: React.PropTypes.func,
className: React.PropTypes.string
};
render() {
return (
<Widget className={this.getClass()}>
<Header title={i18n('SIGN_UP')} description={i18n('SIGN_UP_VIEW_DESCRIPTION')} />
<Form {...this.getFormProps()}>
<div className="signup-widget__inputs">
<FormField {...this.getInputProps()} label={i18n('FULL_NAME')} name="name" validation="NAME" required/>
<FormField {...this.getInputProps()} label={i18n('EMAIL')} name="email" validation="EMAIL" required/>
<FormField {...this.getInputProps(true)} label={i18n('PASSWORD')} name="password" validation="PASSWORD" required/>
<FormField {...this.getInputProps(true)} label={i18n('REPEAT_PASSWORD')} name="repeated-password" validation="REPEAT_PASSWORD" required/>
</div>
<div className="signup-widget__captcha">
<Captcha ref="captcha"/>
</div>
<SubmitButton type="primary">{i18n('SIGN_UP')}</SubmitButton>
</Form>
{this.renderMessage()}
</Widget>
);
}
renderMessage() {
switch (this.state.message) {
case 'success':
return <Message type="success">{i18n('SIGNUP_SUCCESS')}</Message>;
case 'fail':
return <Message type="error">{i18n('EMAIL_EXISTS')}</Message>;
default:
return null;
}
}
getClass() {
let classes = {
'signup-widget': true,
[this.props.className]: this.props.className
};
return classNames(classes);
}
getFormProps() {
return {
loading: this.state.loading,
className: 'signup-widget__form',
onSubmit: this.onSignupFormSubmit.bind(this)
};
}
getInputProps(password) {
return {
className: 'signup-widget__input',
fieldProps: {
size: 'medium',
password: password
}
};
}
onSignupFormSubmit(formState) {
const captcha = this.refs.captcha.getWrappedInstance();
if (!captcha.getValue()) {
captcha.focus();
} else {
this.setState({
loading: true
});
API.call({
path: '/user/signup',
data: _.extend({captcha: captcha.getValue()}, formState)
}).then(this.onSignupSuccess.bind(this)).catch(this.onSignupFail.bind(this));
}
}
onSignupSuccess() {
this.setState({
loading: false,
message: 'success'
});
}
onSignupFail() {
this.setState({
loading: false,
message: 'fail'
});
}
}
export default MainSignUpWidget;

View File

@ -0,0 +1,19 @@
.signup-widget {
padding: 30px;
text-align: center;
&__form {
margin-bottom: 20px;
}
&__inputs {
display: inline-block;
margin: 0 auto;
}
&__captcha {
margin: 10px auto 20px;
height: 78px;
width: 304px;
}
}

View File

@ -3,6 +3,8 @@ import React from 'react';
import Button from 'core-components/button';
import Icon from 'core-components/icon';
import i18n from 'lib-app/i18n';
class FileUploader extends React.Component {
static propTypes = {
text: React.PropTypes.string,
@ -11,7 +13,7 @@ class FileUploader extends React.Component {
};
static defaultProps = {
text: 'Upload file'
text: i18n('UPLOAD_FILE')
};
render() {
@ -37,4 +39,4 @@ class FileUploader extends React.Component {
}
}
export default FileUploader;
export default FileUploader;

View File

@ -181,6 +181,8 @@ export default {
'UPDATE': 'Atualizar',
'NEVER': 'Nunca',
'HIMSELF': 'ele mesmo',
'ADD_USER': 'Adicionar usuário',
'UPLOAD_FILE': 'Subir arquivo',
'CHART_CREATE_TICKET': 'Chamados criados',
'CHART_CLOSE': 'Chamados fechados',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': '更新',
'NEVER': '从来没有',
'HIMSELF': '他自己',
'ADD_USER': '添加用户',
'UPLOAD_FILE': '上传文件',
'CHART_CREATE_TICKET': '已創建門票',
'CHART_CLOSE': '門票已關閉',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Aktualisierung',
'NEVER': 'Niemals',
'HIMSELF': 'selbst',
'ADD_USER': 'Benutzer hinzufügen',
'UPLOAD_FILE': 'Datei hochladen',
'CHART_CREATE_TICKET': 'Tickets erstellt',
'CHART_CLOSE': 'Tickets geschlossen',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Update',
'NEVER': 'Never',
'HIMSELF': 'himself',
'ADD_USER': 'Add user',
'UPLOAD_FILE': 'Upload file',
'CHART_CREATE_TICKET': 'Tickets created',
'CHART_CLOSE': 'Tickets closed',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Actualizar',
'NEVER': 'Nunca',
'HIMSELF': 'si mismo',
'ADD_USER': 'Añadir un usuario',
'UPLOAD_FILE': 'Subir archivo',
'CHART_CREATE_TICKET': 'Tickets creados',
'CHART_CLOSE': 'Tickets cerrados',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Mettre à jour',
'NEVER': 'Jamais',
'HIMSELF': 'lui-même',
'ADD_USER': 'Ajouter un utilisateur',
'UPLOAD_FILE': 'Téléverser un fichier',
'CHART_CREATE_TICKET': 'Tickets créés',
'CHART_CLOSE': 'Tickets fermés',

View File

@ -182,6 +182,8 @@
'UPDATE': 'Ενημέρωση',
'NEVER': 'Ποτέ',
'HIMSELF': 'ο ίδιος',
'ADD_USER': 'Πρόσθεσε χρήστη',
'UPLOAD_FILE': 'Ανέβασμα αρχείου',
'CHART_CREATE_TICKET': 'Τα εισιτήρια δημιουργήθηκαν',
'CHART_CLOSE': 'Τα εισιτήρια κλείσανε',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'अद्यतन',
'NEVER': 'कभी नहीँ',
'HIMSELF': 'स्वयं',
'ADD_USER': 'उपयोगकर्ता जोड़ें',
'UPLOAD_FILE': 'दस्तावेज अपलोड करें',
'CHART_CREATE_TICKET': 'टिकट बनाया',
'CHART_CLOSE': 'टिकट बंद कर दिया',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Aggiornare',
'NEVER': 'Mai',
'HIMSELF': 'lui stesso',
'ADD_USER': 'Aggiungi utente',
'UPLOAD_FILE': 'Caricare un file',
'CHART_CREATE_TICKET': 'Tickets creato',
'CHART_CLOSE': 'Tickets chiuso',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': '更新',
'NEVER': '決して',
'HIMSELF': '彼自身',
'ADD_USER': 'ユーザーを追加する',
'UPLOAD_FILE': 'ファイルをアップロードする',
'CHART_CREATE_TICKET': '作成されたチケット',
'CHART_CLOSE': 'チケットが閉じられました',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Update',
'NEVER': 'Nooit',
'HIMSELF': 'zichzelf',
'ADD_USER': 'Voeg gebruiker toe',
'UPLOAD_FILE': 'Upload bestand',
'CHART_CREATE_TICKET': 'Aangemaakte incidenten',
'CHART_CLOSE': 'Gesloten incidenten',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Actualizar',
'NEVER': 'Nunca',
'HIMSELF': 'ele mesmo',
'ADD_USER': 'Adicionar usuário',
'UPLOAD_FILE': 'Subir arquivo',
'CHART_CREATE_TICKET': 'Ingressos criados',
'CHART_CLOSE': 'Ingressos fechados',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Обновить',
'NEVER': 'Никогда',
'HIMSELF': 'сам',
'ADD_USER': 'Добавить пользователя',
'UPLOAD_FILE': 'Загрузить файл',
'CHART_CREATE_TICKET': 'Билеты создано',
'CHART_CLOSE': ' Билеты закрыты',

View File

@ -182,6 +182,8 @@ export default {
'UPDATE': 'Güncelleştirme',
'NEVER': 'Asla',
'HIMSELF': 'kendisi',
'ADD_USER': 'Kullanıcı Ekle',
'UPLOAD_FILE': 'Dosya yükleme',
'CHART_CREATE_TICKET': 'Biletler oluşturuldu',
'CHART_CLOSE': 'Biletler kapandı',

View File

@ -36,7 +36,21 @@ class EditStaffController extends Controller {
public function validations() {
return [
'permission' => 'staff_1',
'requestData' => []
'requestData' => [
'email' => [
'validation' => DataValidator::oneOf(DataValidator::email(), DataValidator::falseVal()),
'error' => ERRORS::INVALID_EMAIL
],
'password' => [
'validation' => DataValidator::oneOf(DataValidator::length(5, 200), DataValidator::falseVal()),
'error' => ERRORS::INVALID_PASSWORD
],
'level' => [
'validation' => DataValidator::oneOf(DataValidator::between(1, 3, true), DataValidator::falseVal()),
'error' => ERRORS::INVALID_LEVEL
]
]
];
}

View File

@ -97,7 +97,7 @@ class SignUpController extends Controller {
throw new Exception(ERRORS::ALREADY_BANNED);
}
if (!Setting::getSetting('registration')->value && $apiKey->isNull() && !$this->csvImported) {
if (!Setting::getSetting('registration')->value && $apiKey->isNull() && !Controller::isStaffLogged(2) && !$this->csvImported) {
throw new Exception(ERRORS::NO_PERMISSION);
}