Merge branch 'master' into master
This commit is contained in:
commit
b9e4a55b91
|
@ -23,6 +23,7 @@ class ActivityRow extends React.Component {
|
|||
|
||||
'EDIT_SETTINGS',
|
||||
'SIGNUP',
|
||||
'INVITE',
|
||||
'ADD_TOPIC',
|
||||
'ADD_ARTICLE',
|
||||
'DELETE_TOPIC',
|
||||
|
@ -65,9 +66,7 @@ class ActivityRow extends React.Component {
|
|||
<div className="activity-row">
|
||||
<Icon {...this.getIconProps()} className="activity-row__icon"/>
|
||||
<span>
|
||||
<Link className="activity-row__name-link" to={this.getNameLinkDestination()}>
|
||||
{this.props.author.name}
|
||||
</Link>
|
||||
{this.renderAuthorName()}
|
||||
</span>
|
||||
<span className="activity-row__message"> {i18n('ACTIVITY_' + this.props.type)} </span>
|
||||
{_.includes(ticketRelatedTypes, this.props.type) ? this.renderTicketNumber() : this.props.to}
|
||||
|
@ -76,6 +75,18 @@ class ActivityRow extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderAuthorName() {
|
||||
let name = this.props.author.name;
|
||||
|
||||
if (this.props.author.id) {
|
||||
name = <Link className="activity-row__name-link" to={this.getNameLinkDestination()}>
|
||||
{this.props.author.name}
|
||||
</Link>;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
renderTicketNumber() {
|
||||
let ticketNumber = (this.props.mode === 'staff') ? this.props.ticketNumber : this.props.to;
|
||||
|
||||
|
@ -106,6 +117,7 @@ class ActivityRow extends React.Component {
|
|||
|
||||
'EDIT_SETTINGS': 'wrench',
|
||||
'SIGNUP': 'user-plus',
|
||||
'INVITE': 'user-plus',
|
||||
'ADD_TOPIC': 'book',
|
||||
'ADD_ARTICLE': 'book',
|
||||
'DELETE_TOPIC': 'book',
|
||||
|
|
|
@ -107,18 +107,18 @@ class AdminPanelMenu extends React.Component {
|
|||
getRoutes() {
|
||||
const customLists = this.getCustomlists();
|
||||
|
||||
return this.getItemsByFilteredByLevel([
|
||||
return this.getItemsByFilteredByLevel(_.without([
|
||||
{
|
||||
groupName: i18n('DASHBOARD'),
|
||||
path: '/admin/panel',
|
||||
icon: 'tachometer',
|
||||
level: 1,
|
||||
items: this.getItemsByFilteredByLevel([
|
||||
{
|
||||
/*{
|
||||
name: i18n('STATISTICS'),
|
||||
path: '/admin/panel/stats',
|
||||
level: 1
|
||||
},
|
||||
},*/
|
||||
{
|
||||
name: i18n('LAST_ACTIVITY'),
|
||||
path: '/admin/panel/activity',
|
||||
|
@ -155,7 +155,7 @@ class AdminPanelMenu extends React.Component {
|
|||
...customLists
|
||||
])
|
||||
},
|
||||
{
|
||||
this.props.config['user-system-enabled'] ? {
|
||||
groupName: i18n('USERS'),
|
||||
path: '/admin/panel/users',
|
||||
icon: 'user',
|
||||
|
@ -177,7 +177,7 @@ class AdminPanelMenu extends React.Component {
|
|||
level: 1
|
||||
}
|
||||
])
|
||||
},
|
||||
} : null,
|
||||
{
|
||||
groupName: i18n('ARTICLES'),
|
||||
path: '/admin/panel/articles',
|
||||
|
@ -192,7 +192,6 @@ class AdminPanelMenu extends React.Component {
|
|||
])
|
||||
},
|
||||
{
|
||||
|
||||
groupName: i18n('STAFF'),
|
||||
path: '/admin/panel/staff',
|
||||
icon: 'users',
|
||||
|
@ -239,7 +238,7 @@ class AdminPanelMenu extends React.Component {
|
|||
}
|
||||
])
|
||||
}
|
||||
]);
|
||||
], null));
|
||||
}
|
||||
|
||||
getItemsByFilteredByLevel(items) {
|
||||
|
@ -249,6 +248,7 @@ class AdminPanelMenu extends React.Component {
|
|||
|
||||
export default connect((store) => {
|
||||
return {
|
||||
level: store.session.userLevel
|
||||
level: store.session.userLevel,
|
||||
config: store.config
|
||||
};
|
||||
})(AdminPanelMenu);
|
||||
|
|
|
@ -67,7 +67,7 @@ class AddStaffModal extends React.Component {
|
|||
return SessionStore.getDepartments().map(department => {
|
||||
if(department.private*1){
|
||||
return <spam>{department.name} <Icon name='user-secret'/> </spam>
|
||||
}else {
|
||||
} else {
|
||||
return department.name;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import SessionStore from 'lib-app/session-store';
|
|||
import PeopleList from 'app-components/people-list';
|
||||
import ModalContainer from 'app-components/modal-container';
|
||||
|
||||
import AddStaffModal from 'app/admin/panel/staff/add-staff-modal';
|
||||
import InviteStaffModal from 'app/admin/panel/staff/invite-staff-modal';
|
||||
|
||||
import Header from 'core-components/header';
|
||||
import DropDown from 'core-components/drop-down';
|
||||
|
@ -47,8 +47,8 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
<Header title={i18n('STAFF_MEMBERS')} description={i18n('STAFF_MEMBERS_DESCRIPTION')} />
|
||||
<div className="admin-panel-staff-members__wrapper">
|
||||
<DepartmentDropdown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
||||
<Button onClick={this.onAddNewStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||
<Icon name="user-plus" className=""/> {i18n('ADD_NEW_STAFF')}
|
||||
<Button onClick={this.onInviteStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||
<Icon name="user-plus" className=""/> {i18n('INVITE_STAFF')}
|
||||
</Button>
|
||||
</div>
|
||||
{(this.props.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />}
|
||||
|
@ -56,8 +56,8 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
onAddNewStaff() {
|
||||
ModalContainer.openModal(<AddStaffModal onSuccess={this.retrieveStaffMembers.bind(this)} />);
|
||||
onInviteStaff() {
|
||||
ModalContainer.openModal(<InviteStaffModal onSuccess={this.retrieveStaffMembers.bind(this)} />);
|
||||
}
|
||||
|
||||
getDepartmentDropdownProps() {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
import SessionStore from 'lib-app/session-store';
|
||||
|
||||
import Header from 'core-components/header'
|
||||
import Form from 'core-components/form';
|
||||
import FormField from 'core-components/form-field';
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
import Button from 'core-components/button';
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class InviteStaffModal extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
closeModal: React.PropTypes.func
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
onSuccess: React.PropTypes.func
|
||||
};
|
||||
|
||||
state = {
|
||||
loading: false,
|
||||
errors: {},
|
||||
error: null
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="invite-staff-modal">
|
||||
<Header title={i18n('INVITE_STAFF')} description={i18n('INVITE_STAFF_DESCRIPTION')} />
|
||||
<Form onSubmit={this.onSubmit.bind(this)} errors={this.getErrors()} onValidateErrors={errors => this.setState({errors})} loading={this.state.loading}>
|
||||
<div className="row">
|
||||
<div className="col-md-7">
|
||||
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} validation="NAME" required />
|
||||
<FormField name="email" label={i18n('EMAIL')} fieldProps={{size: 'large'}} validation="EMAIL" required />
|
||||
<div className="invite-staff-modal__level-selector">
|
||||
<FormField name="level" label={i18n('LEVEL')} field="select" fieldProps={{
|
||||
items: [{content: i18n('LEVEL_1')}, {content: i18n('LEVEL_2')}, {content: i18n('LEVEL_3')}],
|
||||
size: 'large'
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<div className="invite-staff-modal__departments">
|
||||
<div className="invite-staff-modal__departments-title">{i18n('Departments')}</div>
|
||||
<FormField name="departments" field="checkbox-group" fieldProps={{items: this.getDepartments()}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SubmitButton type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
<Button type="clean" onClick={this.onCancelClick.bind(this)}>
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getDepartments() {
|
||||
return SessionStore.getDepartments().map(department => {
|
||||
if(department.private*1){
|
||||
return <span>{department.name} <Icon name='user-secret'/> </span>
|
||||
} else {
|
||||
return department.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(form) {
|
||||
let departments = _.filter(SessionStore.getDepartments(), (department, index) => {
|
||||
return _.includes(form.departments, index);
|
||||
}).map(department => department.id);
|
||||
|
||||
this.setState({loading: true});
|
||||
|
||||
API.call({
|
||||
path: '/staff/invite',
|
||||
data: {
|
||||
name: form.name,
|
||||
email: form.email,
|
||||
level: form.level + 1,
|
||||
departments: JSON.stringify(departments)
|
||||
}
|
||||
}).then(() => {
|
||||
this.context.closeModal();
|
||||
|
||||
if(this.props.onSuccess) {
|
||||
this.props.onSuccess();
|
||||
}
|
||||
}).catch((result) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: result.message
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onCancelClick(event) {
|
||||
event.preventDefault();
|
||||
this.context.closeModal();
|
||||
}
|
||||
|
||||
getErrors() {
|
||||
let errors = _.extend({}, this.state.errors);
|
||||
|
||||
if (this.state.error === 'ALREADY_A_STAFF') {
|
||||
errors.email = i18n('EMAIL_EXISTS');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
export default InviteStaffModal;
|
|
@ -0,0 +1,23 @@
|
|||
@import "../../../../scss/vars";
|
||||
|
||||
.invite-staff-modal {
|
||||
width: 700px;
|
||||
|
||||
&__level-selector {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__departments {
|
||||
@include scrollbars();
|
||||
|
||||
border: 1px solid $grey;
|
||||
padding: 20px;
|
||||
height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__departments-title {
|
||||
font-size: $font-size--md;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
|
@ -12,10 +13,9 @@ 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';
|
||||
import InviteUserWidget from 'app/admin/panel/users/invite-user-widget';
|
||||
|
||||
class AdminPanelListUsers extends React.Component {
|
||||
|
||||
state = {
|
||||
loading: true,
|
||||
users: [],
|
||||
|
@ -39,11 +39,19 @@ class AdminPanelListUsers extends React.Component {
|
|||
return (
|
||||
<div className="admin-panel-list-users">
|
||||
<Header title={i18n('LIST_USERS')} description={i18n('LIST_USERS_DESCRIPTION')} />
|
||||
{(this.state.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_USERS')}</Message> : this.renderTableAndInviteButton()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTableAndInviteButton() {
|
||||
return (
|
||||
<div>
|
||||
<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()}/>}
|
||||
<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 onClick={this.onInviteUser.bind(this)} type="secondary" size="medium">
|
||||
<Icon size="sm" name="plus"/> {i18n('INVITE_USER')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,17 +175,17 @@ class AdminPanelListUsers extends React.Component {
|
|||
}).catch(this.onUsersRejected.bind(this)).then(this.onUsersRetrieved.bind(this));
|
||||
}
|
||||
|
||||
onCreateUser(user) {
|
||||
onInviteUser(user) {
|
||||
ModalContainer.openModal(
|
||||
<div className="admin-panel-list-users__add-user-form">
|
||||
<MainSignUpWidget onSuccess={this.onCreateUserSuccess.bind(this)} />
|
||||
<div className="admin-panel-list-users__invite-user-form">
|
||||
<InviteUserWidget onSuccess={this.onInviteUserSuccess.bind(this)} />
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
onCreateUserSuccess() {
|
||||
onInviteUserSuccess() {
|
||||
ModalContainer.closeModal();
|
||||
}
|
||||
|
||||
|
@ -201,4 +209,8 @@ class AdminPanelListUsers extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default AdminPanelListUsers;
|
||||
export default connect((store) => {
|
||||
return {
|
||||
config: store.config
|
||||
};
|
||||
})(AdminPanelListUsers);
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
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 InviteUserWidget extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
onSuccess: React.PropTypes.func,
|
||||
className: React.PropTypes.string
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
email: null,
|
||||
customFields: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
API.call({
|
||||
path: '/system/get-custom-fields',
|
||||
data: {}
|
||||
})
|
||||
.then(result => this.setState({customFields: result.data}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Widget className={this.getClass()}>
|
||||
<Header title={i18n('INVITE_USER')} description={i18n('INVITE_USER_VIEW_DESCRIPTION')} />
|
||||
<Form {...this.getFormProps()}>
|
||||
<div className="invite-user-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/>
|
||||
{this.state.customFields.map(this.renderCustomField.bind(this))}
|
||||
</div>
|
||||
<div className="invite-user-widget__captcha">
|
||||
<Captcha ref="captcha"/>
|
||||
</div>
|
||||
<SubmitButton type="primary">{i18n('INVITE_USER')}</SubmitButton>
|
||||
</Form>
|
||||
|
||||
{this.renderMessage()}
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
renderCustomField(customField, key) {
|
||||
if(customField.type === 'text') {
|
||||
return (
|
||||
<FormField {...this.getInputProps()}
|
||||
name={`customfield_${customField.name}`}
|
||||
key={key}
|
||||
label={customField.name}
|
||||
infoMessage={customField.description}
|
||||
field="input"/>
|
||||
);
|
||||
} else {
|
||||
const items = customField.options.map(option => ({content: option.name, value: option.name}));
|
||||
|
||||
return (
|
||||
<FormField
|
||||
name={`customfield_${customField.name}`}
|
||||
key={key}
|
||||
label={customField.name}
|
||||
infoMessage={customField.description}
|
||||
field="select"
|
||||
fieldProps={{size:'medium', items}}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderMessage() {
|
||||
switch (this.state.message) {
|
||||
case 'success':
|
||||
return <Message type="success">{i18n('INVITE_USER_SUCCESS')}</Message>;
|
||||
case 'fail':
|
||||
return <Message type="error">{i18n('EMAIL_EXISTS')}</Message>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getClass() {
|
||||
let classes = {
|
||||
'invite-user-widget': true,
|
||||
[this.props.className]: this.props.className
|
||||
};
|
||||
return classNames(classes);
|
||||
}
|
||||
|
||||
getFormProps() {
|
||||
return {
|
||||
loading: this.state.loading,
|
||||
className: 'invite-user-widget__form',
|
||||
onSubmit: this.onInviteUserFormSubmit.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
getInputProps(password) {
|
||||
return {
|
||||
className: 'invite-user-widget__input',
|
||||
fieldProps: {
|
||||
size: 'medium',
|
||||
password: password
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onInviteUserFormSubmit(formState) {
|
||||
const captcha = this.refs.captcha.getWrappedInstance();
|
||||
|
||||
if (!captcha.getValue()) {
|
||||
captcha.focus();
|
||||
} else {
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
|
||||
const form = _.clone(formState);
|
||||
|
||||
this.state.customFields.forEach(customField => {
|
||||
if(customField.type === 'select') {
|
||||
form[`customfield_${customField.name}`] = customField.options[form[`customfield_${customField.name}`]].name;
|
||||
}
|
||||
})
|
||||
|
||||
API.call({
|
||||
path: '/user/invite',
|
||||
data: _.extend({captcha: captcha.getValue()}, form)
|
||||
}).then(this.onInviteUserSuccess.bind(this)).catch(this.onInviteUserFail.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
onInviteUserSuccess() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: 'success'
|
||||
});
|
||||
}
|
||||
|
||||
onInviteUserFail() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: 'fail'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default InviteUserWidget;
|
|
@ -0,0 +1,19 @@
|
|||
.invite-user-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;
|
||||
}
|
||||
}
|
|
@ -146,7 +146,7 @@ class CreateTicketForm extends React.Component {
|
|||
message: 'success'
|
||||
}, () => {
|
||||
if(this.props.onSuccess) {
|
||||
this.props.onSuccess();
|
||||
this.props.onSuccess(result, email);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class DashboardCreateTicketPage extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
onCreateTicketSuccess() {
|
||||
onCreateTicketSuccess(result, email) {
|
||||
if((this.props.location.pathname !== '/create-ticket')) {
|
||||
setTimeout(() => {history.push('/dashboard')}, 2000);
|
||||
} else {
|
||||
|
|
|
@ -30,7 +30,7 @@ class MainRecoverPasswordPage extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="main-recover-password-page">
|
||||
<Widget title={i18n('RECOVER_PASSWORD')} className="col-md-4 col-md-offset-4">
|
||||
<Widget title={this.props.location.query.invited ? i18n('SET_UP_PASSWORD') : i18n('RECOVER_PASSWORD')} className="col-md-4 col-md-offset-4">
|
||||
<Form className="recover-password__form" onSubmit={this.onRecoverPasswordSubmit.bind(this)} loading={this.state.loading}>
|
||||
<div className="recover-password__inputs">
|
||||
<FormField placeholder={i18n('NEW_PASSWORD')} name="password" className="recover-password__input" validation="PASSWORD" fieldProps={{password: true}} required/>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Widget from 'core-components/widget';
|
||||
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
|
||||
|
||||
class MainSignUpPage extends React.Component {
|
||||
|
@ -9,70 +7,10 @@ class MainSignUpPage extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="main-signup-page">
|
||||
<MainSignUpWidget {...this.props} className="col-md-6 col-md-offset-3" />
|
||||
<MainSignUpWidget 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 MainSignUpPage;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
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 history from 'lib-app/history';
|
||||
|
||||
import Captcha from 'app/main/captcha';
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
|
@ -17,7 +17,6 @@ import Header from 'core-components/header';
|
|||
class MainSignUpWidget extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
onSuccess: React.PropTypes.func,
|
||||
className: React.PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -153,6 +152,8 @@ class MainSignUpWidget extends React.Component {
|
|||
this.setState({
|
||||
loading: false,
|
||||
message: 'success'
|
||||
}, () => {
|
||||
setTimeout(() => {history.push('/check-ticket')}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -300,7 +300,7 @@ export default {
|
|||
'DELETE_USER_DESCRIPTION': 'O usuário não será capaz de entrar no sistema e todos os seus chamados serão apagados. Além disso, o e-mail não poderá mais ser usado.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Ao excluir o tópico, todos os artigos dele serão apagados.',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Aqui você pode alterar o nome, o ícone ea cor do ícone do tópico.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {categoria}.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Esta é uma lista de artigos que inclui informações sobre nossos serviços.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Aqui você pode adicionar um tópico que funciona como uma categoria para artigos.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.',
|
||||
|
|
|
@ -272,7 +272,7 @@ export default {
|
|||
'INSTALLATION_COMPLETED': 'Installation abgeschlossen.',
|
||||
'INSTALLATION_COMPLETED_DESCRIPTION': 'Die Installation von OpenSupports ist abgeschlossen. Umleitung zum Admin-Panel...',
|
||||
|
||||
'STEP_TITLE': 'Schritt {aktuell} von {total} - {title}',
|
||||
'STEP_TITLE': 'Schritt {current} von {total} - {title}',
|
||||
'STEP_1_DESCRIPTION': 'Wählen Sie Ihre bevorzugte Sprache für den Installationsassistenten aus.',
|
||||
'STEP_2_DESCRIPTION': 'Hier sind die Voraussetzungen für das Ausführen von OpenSupports aufgelistet. Bitte stellen Sie sicher, dass alle Anforderungen erfüllt sind.',
|
||||
'STEP_3_DESCRIPTION': 'Bitte füllen Sie die MySQL-Datenbankkonfiguration aus.',
|
||||
|
|
|
@ -11,6 +11,7 @@ export default {
|
|||
'SIGN_UP': 'Sign up',
|
||||
'FORGOT_PASSWORD': 'Forgot your password?',
|
||||
'RECOVER_PASSWORD': 'Recover Password',
|
||||
'SET_UP_PASSWORD': 'Set up your password',
|
||||
'RECOVER_SENT': 'An email with recover instructions has been sent.',
|
||||
'NEW_EMAIL': 'New email',
|
||||
'FULL_NAME': 'Full name',
|
||||
|
@ -194,6 +195,8 @@ export default {
|
|||
'NEVER': 'Never',
|
||||
'HIMSELF': 'himself',
|
||||
'ADD_USER': 'Add user',
|
||||
'INVITE_USER': 'Invite user',
|
||||
'INVITE_STAFF': 'Invite staff',
|
||||
'UPLOAD_FILE': 'Upload file',
|
||||
'PRIVATE': 'Private',
|
||||
'ENABLE_USER': 'Enable User',
|
||||
|
@ -233,6 +236,7 @@ export default {
|
|||
|
||||
'ACTIVITY_EDIT_SETTINGS': 'edited settings',
|
||||
'ACTIVITY_SIGNUP': 'signed up',
|
||||
'ACTIVITY_INVITE': 'invited user',
|
||||
'ACTIVITY_ADD_TOPIC': 'added topic',
|
||||
'ACTIVITY_ADD_ARTICLE': 'added article',
|
||||
'ACTIVITY_DELETE_TOPIC': 'deleted topic',
|
||||
|
@ -338,6 +342,8 @@ export default {
|
|||
'IMAP_POLLING_DESCRIPTION': 'Inbox checking will not be done automatically by OpenSupports. You have to make POST requests periodically to this url to process the emails: {url}',
|
||||
'NEW_CUSTOM_FIELD_DESCRIPTION': 'Here you can create a custom field for an user, it can be a blank text box or a fixed set of options.',
|
||||
'CUSTOM_FIELDS_DESCRIPTION': 'Custom fields are defined additional fields the users are able to fill to provide more information about them.',
|
||||
'INVITE_USER_VIEW_DESCRIPTION': 'Here you can invite an user to join our support system, he will just need to provide his password to create a new user.',
|
||||
'INVITE_STAFF_DESCRIPTION': 'Here you can invite staff members to your teams.',
|
||||
|
||||
//ERRORS
|
||||
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
||||
|
@ -374,6 +380,7 @@ export default {
|
|||
|
||||
//MESSAGES
|
||||
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',
|
||||
'INVITE_USER_SUCCESS': 'You have invited a new user successfully in our support system',
|
||||
'TICKET_SENT': 'Ticket has been created successfully.',
|
||||
'VALID_RECOVER': 'Password recovered successfully',
|
||||
'EMAIL_EXISTS': 'Email already exists',
|
||||
|
|
|
@ -206,7 +206,7 @@ export default {
|
|||
'TYPE': 'Tipo',
|
||||
'SELECT_INPUT': 'Seleccionar entrada',
|
||||
'TEXT_INPUT': 'Entrada de texto',
|
||||
'OPTION': 'Opción {índice}',
|
||||
'OPTION': 'Opción {index}',
|
||||
'OPTIONS': 'Opciones',
|
||||
'FIELD_DESCRIPTION': 'Descripción del campo (opcional)',
|
||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puedes agregar una nueva etiqueta personalizada',
|
||||
|
|
|
@ -62,7 +62,7 @@ export default {
|
|||
'HIGH': 'Haute',
|
||||
'MEDIUM': 'Moyenne',
|
||||
'LOW': 'Faible',
|
||||
'TITLE': 'Titre',
|
||||
'TITLE': 'Objet',
|
||||
'CONTENT': 'Contenu',
|
||||
'SAVE': 'Enregistrer',
|
||||
'DISCARD_CHANGES': 'Annuler les modifications',
|
||||
|
@ -343,7 +343,7 @@ export default {
|
|||
'ERROR_EMPTY': 'Valeur invalide',
|
||||
'ERROR_PASSWORD': 'Mot de passe incorrect',
|
||||
'ERROR_NAME': 'Nom incorrect',
|
||||
'ERROR_TITLE': 'Titre incorrect',
|
||||
'ERROR_TITLE': 'Objet incorrect',
|
||||
'ERROR_EMAIL': 'Email invalide',
|
||||
'ERROR_CONTENT_SHORT': 'Contenu trop court',
|
||||
'PASSWORD_NOT_MATCH': 'Le mot de passe ne correspond pas',
|
||||
|
|
|
@ -300,7 +300,7 @@
|
|||
'DELETE_USER_DESCRIPTION': 'Ο χρήστης δεν θα μπορέσει να συνδεθεί με τη γήρανση και όλα τα εισιτήρια του θα διαγραφούν. Επίσης, το ηλεκτρονικό ταχυδρομείο δεν μπορεί πλέον να χρησιμοποιηθεί.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Διαγράφοντας το θέμα, όλα τα άρθρα σε αυτό θα διαγραφούν.',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να αλλάξετε το όνομα, το εικονίδιο και το χρώμα του εικονιδίου του θέματος.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {κατηγορία}.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Αυτή είναι μια λίστα με άρθρα που περιλαμβάνουν πληροφορίες σχετικά με τις υπηρεσίες μας.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα θέμα που λειτουργεί ως κατηγορία για άρθρα.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Πρόκειται να διαγράψετε αυτό το άρθρο για πάντα.',
|
||||
|
|
|
@ -206,7 +206,7 @@ export default {
|
|||
'TYPE': 'genere',
|
||||
'SELECT_INPUT': 'Seleziona input',
|
||||
'TEXT_INPUT': 'L\'immissione di testo',
|
||||
'OPTION': 'Opzione {indice}',
|
||||
'OPTION': 'Opzione {index}',
|
||||
'OPTIONS': 'Opzioni',
|
||||
'FIELD_DESCRIPTION': 'Descrizione del campo (facoltativo)',
|
||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'qui puoi aggiungere un nuovo tag personalizzato',
|
||||
|
|
|
@ -300,7 +300,7 @@ export default {
|
|||
'DELETE_USER_DESCRIPTION': 'O usuário não será capaz de entrar no envelhecimento e todos os seus bilhetes serão apagados. Além disso, o e-mail não pode mais ser usado.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Ao excluir o tópico, todos os artigos dele serão apagados.',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Aqui você pode alterar o nome, o ícone ea cor do ícone do tópico.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {categoria}.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Esta é uma lista de artigos que inclui informações sobre nossos serviços.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Aqui você pode adicionar um tópico que funciona como uma categoria para artigos.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.',
|
||||
|
|
|
@ -9,7 +9,7 @@ $systemControllerGroup->addController(new GetTicketStaffController);
|
|||
$systemControllerGroup->addController(new GetNewTicketsStaffController);
|
||||
$systemControllerGroup->addController(new GetAllTicketsStaffController);
|
||||
$systemControllerGroup->addController(new SearchTicketStaffController);
|
||||
$systemControllerGroup->addController(new AddStaffController);
|
||||
$systemControllerGroup->addController(new InviteStaffController);
|
||||
$systemControllerGroup->addController(new GetAllStaffController);
|
||||
$systemControllerGroup->addController(new DeleteStaffController);
|
||||
$systemControllerGroup->addController(new EditStaffController);
|
||||
|
|
|
@ -11,7 +11,7 @@ use Respect\Validation\Validator as DataValidator;
|
|||
*
|
||||
* @apiDescription This path retrieves information about all the staff member.
|
||||
*
|
||||
* @apiPermission staff3
|
||||
* @apiPermission staff1
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
*
|
||||
|
|
|
@ -3,20 +3,19 @@ use Respect\Validation\Validator as DataValidator;
|
|||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
/**
|
||||
* @api {post} /staff/add Add staff
|
||||
* @api {post} /staff/invite Invite staff
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Add staff
|
||||
* @apiName Invite staff
|
||||
*
|
||||
* @apiGroup Staff
|
||||
*
|
||||
* @apiDescription This path adds a new staff member.
|
||||
* @apiDescription This path invites a new staff member.
|
||||
*
|
||||
* @apiPermission staff3
|
||||
*
|
||||
* @apiParam {String} name The name of the new staff member.
|
||||
* @apiParam {String} email The email of the new staff member.
|
||||
* @apiParam {String} password The password of the new staff member.
|
||||
* @apiParam {Number} level The level of the new staff member.
|
||||
* @apiParam {String} profilePic The profile pic of the new staff member.
|
||||
* @apiParam {Number[]} departments The departments that will have assigned the new staff member.
|
||||
|
@ -33,18 +32,16 @@ DataValidator::with('CustomValidations', true);
|
|||
*
|
||||
*/
|
||||
|
||||
class AddStaffController extends Controller {
|
||||
const PATH = '/add';
|
||||
class InviteStaffController extends Controller {
|
||||
const PATH = '/invite';
|
||||
const METHOD = 'POST';
|
||||
|
||||
private $name;
|
||||
private $email;
|
||||
private $password;
|
||||
private $profilePic;
|
||||
private $level;
|
||||
private $departments;
|
||||
|
||||
|
||||
public function validations() {
|
||||
return [
|
||||
'permission' => 'staff_3',
|
||||
|
@ -57,53 +54,55 @@ class AddStaffController extends Controller {
|
|||
'validation' => DataValidator::email(),
|
||||
'error' => ERRORS::INVALID_EMAIL
|
||||
],
|
||||
'password' => [
|
||||
'validation' => DataValidator::length(5, 200),
|
||||
'error' => ERRORS::INVALID_PASSWORD
|
||||
],
|
||||
'level' => [
|
||||
'validation' => DataValidator::between(1, 3, true),
|
||||
'error' => ERRORS::INVALID_LEVEL
|
||||
]
|
||||
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
$this->storeRequestData();
|
||||
|
||||
$staffRow = Staff::getDataStore($this->email, 'email');
|
||||
|
||||
if(!$staffRow->isNull()) throw new RequestException(ERRORS::ALREADY_A_STAFF);
|
||||
|
||||
$staff = new Staff();
|
||||
$staff->setProperties([
|
||||
'name'=> $this->name,
|
||||
'email' => $this->email,
|
||||
'password'=> Hashing::hashPassword(Hashing::generateRandomToken()),
|
||||
'profilePic' => $this->profilePic,
|
||||
'level' => $this->level,
|
||||
'sharedDepartmentList' => $this->getDepartmentList()
|
||||
]);
|
||||
|
||||
$staffRow = Staff::getDataStore($this->email,'email');
|
||||
$this->addOwner();
|
||||
|
||||
if($staffRow->isNull()) {
|
||||
$staff->setProperties([
|
||||
'name'=> $this->name,
|
||||
'email' => $this->email,
|
||||
'password'=> Hashing::hashPassword($this->password),
|
||||
'profilePic' => $this->profilePic,
|
||||
'level' => $this->level,
|
||||
'sharedDepartmentList' => $this->getDepartmentList()
|
||||
$this->token = Hashing::generateRandomToken();
|
||||
|
||||
]);
|
||||
|
||||
$this->addOwner();
|
||||
|
||||
Log::createLog('ADD_STAFF', $this->name);
|
||||
|
||||
Response::respondSuccess([
|
||||
'id' => $staff->store()
|
||||
]);
|
||||
return;
|
||||
}
|
||||
$recoverPassword = new RecoverPassword();
|
||||
$recoverPassword->setProperties(array(
|
||||
'email' => $this->email,
|
||||
'token' => $this->token,
|
||||
'staff' => true
|
||||
));
|
||||
$recoverPassword->store();
|
||||
|
||||
throw new RequestException(ERRORS::ALREADY_A_STAFF);
|
||||
$this->sendInvitationMail();
|
||||
|
||||
Response::respondSuccess([
|
||||
'id' => $staff->store()
|
||||
]);
|
||||
|
||||
Log::createLog('INVITE', $this->name);
|
||||
}
|
||||
|
||||
public function storeRequestData() {
|
||||
$this->name = Controller::request('name');
|
||||
$this->email = Controller::request('email');
|
||||
$this->password = Controller::request('password');
|
||||
$this->profilePic = Controller::request('profilePic');
|
||||
$this->level = Controller::request('level');
|
||||
$this->departments = Controller::request('departments');
|
||||
|
@ -120,6 +119,7 @@ class AddStaffController extends Controller {
|
|||
|
||||
return $listDepartments;
|
||||
}
|
||||
|
||||
public function addOwner() {
|
||||
$departmentIds = json_decode($this->departments);
|
||||
|
||||
|
@ -129,4 +129,17 @@ class AddStaffController extends Controller {
|
|||
$departmentRow->store();
|
||||
}
|
||||
}
|
||||
|
||||
public function sendInvitationMail() {
|
||||
$mailSender = MailSender::getInstance();
|
||||
|
||||
$mailSender->setTemplate(MailTemplate::USER_INVITE, [
|
||||
'to' => $this->email,
|
||||
'name' => $this->name,
|
||||
'url' => Setting::getSetting('url')->getValue(),
|
||||
'token' => $this->token
|
||||
]);
|
||||
|
||||
$mailSender->send();
|
||||
}
|
||||
}
|
|
@ -37,9 +37,10 @@ class CommentController extends Controller {
|
|||
|
||||
private $ticket;
|
||||
private $content;
|
||||
private $session;
|
||||
|
||||
public function validations() {
|
||||
$session = Session::getInstance();
|
||||
$this->session = Session::getInstance();
|
||||
|
||||
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
|
||||
return [
|
||||
|
@ -64,11 +65,11 @@ class CommentController extends Controller {
|
|||
'error' => ERRORS::INVALID_CONTENT
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::equals($session->getTicketNumber()),
|
||||
'validation' => DataValidator::equals($this->session->getTicketNumber()),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
],
|
||||
'csrf_token' => [
|
||||
'validation' => DataValidator::equals($session->getToken()),
|
||||
'validation' => DataValidator::equals($this->session->getToken()),
|
||||
'error' => ERRORS::INVALID_TOKEN
|
||||
]
|
||||
]
|
||||
|
@ -79,28 +80,29 @@ class CommentController extends Controller {
|
|||
public function handler() {
|
||||
$this->requestData();
|
||||
$ticketAuthor = $this->ticket->authorToArray();
|
||||
$isAuthor = $this->ticket->isAuthor(Controller::getLoggedUser()) || Session::getInstance()->isTicketSession();
|
||||
$isOwner = $this->ticket->isOwner(Controller::getLoggedUser());
|
||||
$user = Controller::getLoggedUser();
|
||||
$isAuthor = $this->session->isTicketSession() || $this->ticket->isAuthor($this->user);
|
||||
$isOwner = $this->ticket->isOwner($this->user);
|
||||
$private = Controller::request('private');
|
||||
|
||||
if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() && !$isAuthor){
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
if(!$user->canManageTicket($this->ticket)) {
|
||||
if(!$this->session->isTicketSession() && !$this->user->canManageTicket($this->ticket)) {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
$this->storeComment();
|
||||
|
||||
if($isAuthor && $this->ticket->owner) {
|
||||
if(!$isAuthor && !$private) {
|
||||
$this->sendMail($ticketAuthor);
|
||||
}
|
||||
if($this->ticket->owner && !$isOwner) {
|
||||
$this->sendMail([
|
||||
'email' => $this->ticket->owner->email,
|
||||
'name' => $this->ticket->owner->name,
|
||||
'staff' => true
|
||||
]);
|
||||
} else if($isOwner) {
|
||||
!Controller::request('private') ? $this->sendMail($ticketAuthor) : null;
|
||||
}
|
||||
|
||||
Log::createLog('COMMENT', $this->ticket->ticketNumber);
|
||||
|
@ -112,6 +114,7 @@ class CommentController extends Controller {
|
|||
$ticketNumber = Controller::request('ticketNumber');
|
||||
$this->ticket = Ticket::getByTicketNumber($ticketNumber);
|
||||
$this->content = Controller::request('content', true);
|
||||
$this->user = Controller::getLoggedUser();
|
||||
}
|
||||
|
||||
private function storeComment() {
|
||||
|
@ -129,12 +132,14 @@ class CommentController extends Controller {
|
|||
));
|
||||
|
||||
if(Controller::isStaffLogged()) {
|
||||
$this->ticket->unread = !$this->ticket->isAuthor(Controller::getLoggedUser());
|
||||
$this->ticket->unreadStaff = !$this->ticket->isOwner(Controller::getLoggedUser());
|
||||
$comment->authorStaff = Controller::getLoggedUser();
|
||||
$this->ticket->unread = !$this->ticket->isAuthor($this->user);
|
||||
$this->ticket->unreadStaff = !$this->ticket->isOwner($this->user);
|
||||
$comment->authorStaff = $this->user;
|
||||
} else if(Controller::isUserSystemEnabled()) {
|
||||
$this->ticket->unreadStaff = true;
|
||||
$comment->authorUser = Controller::getLoggedUser();
|
||||
$comment->authorUser = $this->user;
|
||||
} else {
|
||||
$this->ticket->unreadStaff = true;
|
||||
}
|
||||
|
||||
$this->ticket->addEvent($comment);
|
||||
|
|
|
@ -115,10 +115,16 @@ class CreateController extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
Log::createLog('CREATE_TICKET', $this->ticketNumber);
|
||||
Response::respondSuccess([
|
||||
'ticketNumber' => $this->ticketNumber
|
||||
]);
|
||||
|
||||
if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
|
||||
$session = Session::getInstance();
|
||||
$session->createTicketSession($this->ticketNumber);
|
||||
}
|
||||
|
||||
Log::createLog('CREATE_TICKET', $this->ticketNumber);
|
||||
}
|
||||
|
||||
private function storeTicket() {
|
||||
|
|
|
@ -4,6 +4,7 @@ $userControllers->setGroupPath('/user');
|
|||
|
||||
$userControllers->addController(new LoginController);
|
||||
$userControllers->addController(new SignUpController);
|
||||
$userControllers->addController(new InviteUserController);
|
||||
$userControllers->addController(new LogoutController);
|
||||
$userControllers->addController(new CheckSessionController);
|
||||
$userControllers->addController(new SendRecoverPasswordController);
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
use Respect\Validation\Validator as DataValidator;
|
||||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
/**
|
||||
* @api {post} /user/invite Invite
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Invite
|
||||
*
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiDescription This path invites an user on the system.
|
||||
*
|
||||
* @apiPermission staff1
|
||||
*
|
||||
* @apiParam {String} name The name of the invited user.
|
||||
* @apiParam {String} email The email of the invited user.
|
||||
* @apiParam {String} customfield_ Custom field values for this user.
|
||||
*
|
||||
* @apiUse INVALID_NAME
|
||||
* @apiUse INVALID_EMAIL
|
||||
* @apiUse INVALID_CAPTCHA
|
||||
* @apiUse USER_EXISTS
|
||||
* @apiUse ALREADY_BANNED
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_CUSTOM_FIELD_OPTION
|
||||
*
|
||||
* @apiSuccess {Object} data Information about invited user
|
||||
* @apiSuccess {Number} data.userId Id of the invited user
|
||||
* @apiSuccess {String} data.userEmail Email of the invited user
|
||||
*
|
||||
*/
|
||||
|
||||
class InviteUserController extends Controller {
|
||||
const PATH = '/invite';
|
||||
const METHOD = 'POST';
|
||||
|
||||
private $userEmail;
|
||||
private $userName;
|
||||
|
||||
public function validations() {
|
||||
$validations = [
|
||||
'permission' => 'staff_1',
|
||||
'requestData' => [
|
||||
'name' => [
|
||||
'validation' => DataValidator::length(2, 55),
|
||||
'error' => ERRORS::INVALID_NAME
|
||||
],
|
||||
'email' => [
|
||||
'validation' => DataValidator::email(),
|
||||
'error' => ERRORS::INVALID_EMAIL
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$validations['requestData']['captcha'] = [
|
||||
'validation' => DataValidator::captcha(),
|
||||
'error' => ERRORS::INVALID_CAPTCHA
|
||||
];
|
||||
|
||||
return $validations;
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
if (!Controller::isUserSystemEnabled()) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
$this->storeRequestData();
|
||||
|
||||
$existentUser = User::getUser($this->userEmail, 'email');
|
||||
|
||||
if (!$existentUser->isNull()) {
|
||||
throw new RequestException(ERRORS::USER_EXISTS);
|
||||
}
|
||||
|
||||
$banRow = Ban::getDataStore($this->userEmail, 'email');
|
||||
|
||||
if (!$banRow->isNull()) {
|
||||
throw new RequestException(ERRORS::ALREADY_BANNED);
|
||||
}
|
||||
|
||||
$userId = $this->createNewUserAndRetrieveId();
|
||||
|
||||
$this->token = Hashing::generateRandomToken();
|
||||
|
||||
$recoverPassword = new RecoverPassword();
|
||||
$recoverPassword->setProperties(array(
|
||||
'email' => $this->userEmail,
|
||||
'token' => $this->token,
|
||||
'staff' => false
|
||||
));
|
||||
$recoverPassword->store();
|
||||
|
||||
$this->sendInvitationMail();
|
||||
|
||||
Response::respondSuccess([
|
||||
'userId' => $userId,
|
||||
'userEmail' => $this->userEmail
|
||||
]);
|
||||
|
||||
Log::createLog('INVITE', $this->userName);
|
||||
}
|
||||
|
||||
public function storeRequestData() {
|
||||
$this->userName = Controller::request('name');
|
||||
$this->userEmail = Controller::request('email');
|
||||
}
|
||||
|
||||
public function createNewUserAndRetrieveId() {
|
||||
$userInstance = new User();
|
||||
|
||||
$userInstance->setProperties([
|
||||
'name' => $this->userName,
|
||||
'signupDate' => Date::getCurrentDate(),
|
||||
'tickets' => 0,
|
||||
'email' => $this->userEmail,
|
||||
'password' => Hashing::hashPassword(Hashing::generateRandomToken()),
|
||||
'verificationToken' => null,
|
||||
'xownCustomfieldvalueList' => $this->getCustomFieldValues()
|
||||
]);
|
||||
|
||||
return $userInstance->store();
|
||||
}
|
||||
|
||||
public function sendInvitationMail() {
|
||||
$mailSender = MailSender::getInstance();
|
||||
|
||||
$mailSender->setTemplate(MailTemplate::USER_INVITE, [
|
||||
'to' => $this->userEmail,
|
||||
'name' => $this->userName,
|
||||
'url' => Setting::getSetting('url')->getValue(),
|
||||
'token' => $this->token
|
||||
]);
|
||||
|
||||
$mailSender->send();
|
||||
}
|
||||
}
|
|
@ -56,10 +56,6 @@ class RecoverPasswordController extends Controller {
|
|||
}
|
||||
|
||||
public function handler() {
|
||||
if(!Controller::isUserSystemEnabled()) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
$this->requestData();
|
||||
$this->changePassword();
|
||||
}
|
||||
|
@ -69,30 +65,38 @@ class RecoverPasswordController extends Controller {
|
|||
$this->token = Controller::request('token');
|
||||
$this->password = Controller::request('password');
|
||||
}
|
||||
|
||||
public function changePassword() {
|
||||
$recoverPassword = RecoverPassword::getDataStore($this->token, 'token');
|
||||
|
||||
if($recoverPassword->isNull() || $recoverPassword->email !== $this->email) {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
if(!Controller::isUserSystemEnabled() && !$recoverPassword->staff) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
if($recoverPassword->staff) {
|
||||
$this->user = Staff::getDataStore($this->email, 'email');
|
||||
}else {
|
||||
} else {
|
||||
$this->user = User::getDataStore($this->email, 'email');
|
||||
}
|
||||
|
||||
if (!$recoverPassword->isNull() && !$this->user->isNull()) {
|
||||
$recoverPassword->delete();
|
||||
if($this->user->isNull()) throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
|
||||
$this->user->setProperties([
|
||||
'password' => Hashing::hashPassword($this->password)
|
||||
]);
|
||||
$recoverPassword->delete();
|
||||
|
||||
$this->user->store();
|
||||
$this->user->setProperties([
|
||||
'password' => Hashing::hashPassword($this->password)
|
||||
]);
|
||||
|
||||
$this->sendMail();
|
||||
Response::respondSuccess(['staff' => $recoverPassword->staff]);
|
||||
} else {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
$this->user->store();
|
||||
|
||||
$this->sendMail();
|
||||
Response::respondSuccess(['staff' => $recoverPassword->staff]);
|
||||
}
|
||||
|
||||
public function sendMail() {
|
||||
$mailSender = MailSender::getInstance();
|
||||
|
||||
|
|
|
@ -49,17 +49,18 @@ class SendRecoverPasswordController extends Controller {
|
|||
}
|
||||
|
||||
public function handler() {
|
||||
if(!Controller::isUserSystemEnabled()) {
|
||||
$this->staff = Controller::request('staff');
|
||||
|
||||
if(!Controller::isUserSystemEnabled() && !$this->staff) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
$this->staff = Controller::request('staff');
|
||||
$email = Controller::request('email');
|
||||
|
||||
if($this->staff){
|
||||
$this->user = Staff::getUser($email,'email');
|
||||
}else {
|
||||
$this->user = User::getUser($email,'email');
|
||||
$this->user = Staff::getUser($email, 'email');
|
||||
} else {
|
||||
$this->user = User::getUser($email, 'email');
|
||||
}
|
||||
|
||||
if(!$this->user->isNull()) {
|
||||
|
|
|
@ -18,7 +18,7 @@ DataValidator::with('CustomValidations', true);
|
|||
* @apiParam {String} name The name of the new user.
|
||||
* @apiParam {String} email The email of the new user.
|
||||
* @apiParam {String} password The password of the new user.
|
||||
* @apiParam {String} apiKey APIKey to sign up an user if the user system is disabled.
|
||||
* @apiParam {String} apiKey APIKey to sign up an user if the registration system is disabled.
|
||||
* @apiParam {String} customfield_ Custom field values for this user.
|
||||
*
|
||||
* @apiUse INVALID_NAME
|
||||
|
|
|
@ -25,6 +25,12 @@ class MailTexts {
|
|||
'Hi, {{name}}. You have requested to recover your password.',
|
||||
'Use this code in {{url}}/recover-password?email={{to}}&token={{token}} or click the button below.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'You have been invited - OpenSupports',
|
||||
'You have been invited',
|
||||
'Hi, {{name}}. You have been invited to join our support center.',
|
||||
'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Access system changed - OpenSupports',
|
||||
'Access system changed',
|
||||
|
@ -85,6 +91,12 @@ class MailTexts {
|
|||
'喂 {{name}}。 您已要求恢复密码。',
|
||||
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}} 或单击下面的按钮.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'您已受邀 - OpenSupports',
|
||||
'您已受邀',
|
||||
'你好, {{name}}. 邀请您加入我们的支持中心.',
|
||||
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}}&invited=true 或单击下面的按钮来设置密码.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'访问系统更改 - OpenSupports',
|
||||
'访问系统更改',
|
||||
|
@ -145,6 +157,12 @@ class MailTexts {
|
|||
'Hallo, {{name}}. Sie haben aufgefordert, Ihr Passwort wiederherzustellen.',
|
||||
'Verwenden Sie diesen Code in {{url}}/recover-password?email={{to}}&token={{token}} oder klicken Sie auf die Schaltfläche unten.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Du bist eingeladen - OpenSupports',
|
||||
'Du bist eingeladen',
|
||||
'Hallo, {{name}}. Sie wurden zu unserem Support-Center eingeladen.',
|
||||
'Verwenden Sie diesen Code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true oder klicken Sie auf die Schaltfläche unten, um Ihr Passwort einzurichten.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Access system changed - OpenSupports',
|
||||
'Zugriffssystem geändert',
|
||||
|
@ -205,6 +223,12 @@ class MailTexts {
|
|||
'Hola, {{name}}. Has requerido recuperar tu contraseña.',
|
||||
'Usá este codigo en {{url}}/recover-password?email={{to}}&token={{token}} o hacé click en el botón de abajo.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Haz sido invitado - OpenSupports',
|
||||
'Haz sido invitado',
|
||||
'Hola, {{name}}. Haz sido invitado a unirte a nuestro sistema de soporte.',
|
||||
'Usa este código en {{url}}/recover-password?email={{to}}&token={{token}}&invited=true o haz click en el botón de abajo para establecer tu contraseña.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Sistema de acceso cambiado - OpenSupports',
|
||||
'Sistema de acceso cambiado',
|
||||
|
@ -265,6 +289,12 @@ class MailTexts {
|
|||
'Salut, {{name}}. Vous avez demandé à récupérer votre mot de passe.',
|
||||
'Utilisez ce code dans {{url}}/recover-password?email={{to}}&token={{token}} ou cliquez sur le bouton ci-dessous.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'You have been invited - OpenSupports',
|
||||
'You have been invited',
|
||||
'Hi, {{name}}. You have been invited to join our support center.',
|
||||
'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Système d\'accès modifié - OpenSupports',
|
||||
'Système d\'accès modifié',
|
||||
|
@ -325,6 +355,12 @@ class MailTexts {
|
|||
'नमस्ते {{name}}. आपने अपना पासवर्ड पुनर्प्राप्त करने का अनुरोध किया है',
|
||||
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}} या नीचे दिए गए बटन पर क्लिक करें.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'आपको आमंत्रित किया गया है - OpenSupports',
|
||||
'आपको आमंत्रित किया गया है',
|
||||
'नमस्ते, {{name}}. आपको हमारे सहायता केंद्र से जुड़ने के लिए आमंत्रित किया गया है.',
|
||||
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}}&invited=true या अपना पासवर्ड सेट करने के लिए नीचे दिए गए बटन पर क्लिक करें.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'sistem akses berubah - OpenSupports',
|
||||
'एक्सेस सिस्टम बदल गया',
|
||||
|
@ -385,6 +421,12 @@ class MailTexts {
|
|||
'Ciao, {{name}}. Hai richiesto di recuperare la tua password.',
|
||||
'Clicca sul link {{url}}/recover-password?email={{to}}&token={{token}} o clicca sul pulsante qui sotto.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Sei stato invitato - OpenSupports',
|
||||
'Sei stato invitato',
|
||||
'Ciao, {{name}}. Sei stato invitato a far parte del nostro centro di supporto.',
|
||||
'Usa questo codice in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true oppure fai clic sul pulsante in basso per impostare la password.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Il sistema di accesso è cambiato - OpenSupports',
|
||||
'Modifica sistema di accesso',
|
||||
|
@ -445,6 +487,12 @@ class MailTexts {
|
|||
'こんにちは、{{name}}。 パスワードの回復を要求しました。',
|
||||
'でこのコードを使用 {{url}}/recover-password?email={{to}}&token={{token}} 下のボタンをクリックしてください.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'招待されました - OpenSupports',
|
||||
'招待されました',
|
||||
'こんにちは, {{name}}. サポートセンターに招待されました.',
|
||||
'このコードを {{url}}/recover-password?email={{to}}&token={{token}}&invited=true または、下のボタンをクリックしてパスワードを設定します.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'アクセスシステムが変更されました - OpenSupports',
|
||||
'アクセスシステムが変更されました',
|
||||
|
@ -505,6 +553,12 @@ class MailTexts {
|
|||
'Olá, {{name}}. Você solicitou a recuperação da sua senha.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}} ou clique no botão abaixo.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Você foi convidado - OpenSupports',
|
||||
'Você foi convidado',
|
||||
'Oi, {{name}}. Você foi convidado a participar do nosso centro de suporte.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ou clique no botão abaixo para configurar sua senha.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Sistema de acesso alterado - OpenSupports',
|
||||
'Sistema de acesso alterado',
|
||||
|
@ -565,6 +619,12 @@ class MailTexts {
|
|||
'Здравствуй, {{name}}. Вы запросили восстановить пароль.',
|
||||
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}} или нажмите кнопку ниже.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Вы были приглашены - OpenSupports',
|
||||
'Вы были приглашены',
|
||||
'Здравствуй, {{name}}. Вас пригласили присоединиться к нашему центру поддержки.',
|
||||
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}}&invited=true или нажмите кнопку ниже, чтобы установить пароль.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Система доступа изменена - OpenSupports',
|
||||
'Система доступа изменена',
|
||||
|
@ -625,6 +685,12 @@ class MailTexts {
|
|||
'Merhaba, {{name}}. Şifrenizi geri yüklemenizi istediniz.',
|
||||
'Bu kodu şu adreste kullanın {{url}}/recover-password?email={{to}}&token={{token}} veya aşağıdaki butona tıklayın.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Davet edildin - OpenSupports',
|
||||
'Davet edildin',
|
||||
'Merhaba, {{name}}. Destek merkezimize katılmaya davet edildiniz.',
|
||||
'Bu kodu {{url}}/recover-password?email={{to}}&token={{token}}&invited=true veya şifrenizi ayarlamak için aşağıdaki butona tıklayın.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Erişim sistemi değiştirildi - OpenSupports',
|
||||
'Erişim sistemi değiştirildi',
|
||||
|
@ -685,6 +751,12 @@ class MailTexts {
|
|||
'Olá, {{name}}. Você solicitou a recuperação da sua senha.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}} ou clique no botão abaixo.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Você foi convidado - OpenSupports',
|
||||
'Você foi convidado',
|
||||
'Oi, {{name}}. Você foi convidado a participar do nosso centro de suporte.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ou clique no botão abaixo para configurar sua senha.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Sistema de acesso alterado - OpenSupports',
|
||||
'Sistema de acesso alterado',
|
||||
|
@ -745,6 +817,12 @@ class MailTexts {
|
|||
'Γεια σου, {{name}}. Ζητήσατε να ανακτήσετε τον κωδικό πρόσβασής σας.',
|
||||
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}} / recover-password? Email = {{to}} & token = {{token}} ή κάντε κλικ στο παρακάτω κουμπί.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Έχετε προσκληθεί - OpenSupports',
|
||||
'Έχετε προσκληθεί',
|
||||
'Γεια σου, {{name}}. Έχετε προσκληθεί να συμμετάσχετε στο κέντρο υποστήριξής μας.',
|
||||
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ή κάντε κλικ στο παρακάτω κουμπί για να ρυθμίσετε τον κωδικό πρόσβασής σας.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Το σύστημα πρόσβασης άλλαξε - OpenSupports',
|
||||
'Το σύστημα πρόσβασης άλλαξε',
|
||||
|
@ -805,6 +883,12 @@ class MailTexts {
|
|||
'Hallo, {{name}}. U heeft een verzoek gedaan om uw wachtwoord te resetten.',
|
||||
'Gebruik deze code {{url}}/recover-password?email={{to}}&token={{token}} of klik op de knop hieronder.'
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Je bent uitgenodigd - OpenSupports',
|
||||
'Je bent uitgenodigd',
|
||||
'Hallo, {{name}}. U bent uitgenodigd om lid te worden van ons ondersteuningscentrum.',
|
||||
'Gebruik deze code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true of klik op de onderstaande knop om uw wachtwoord in te stellen.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Toegangssysteem gewijzigd - OpenSupports',
|
||||
'Toegang tot incidenten is gewijzigd',
|
||||
|
@ -865,6 +949,12 @@ class MailTexts {
|
|||
'Hej, {{name}}. Zażądałeś odzyskania hasła.',
|
||||
'Użyj tego linka {{url}}/recover-password?email={{to}}&token={{token}} lub kliknij przycisk poniżej.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Zostałeś zaproszony - OpenSupports',
|
||||
'Zostałeś zaproszony',
|
||||
'Hej, {{name}}. Zaproszono Cię do dołączenia do naszego centrum wsparcia.',
|
||||
'Użyj tego kodu w {{url}}/recover-password?email={{to}}&token={{token}}&invited=true lub kliknij przycisk poniżej, aby ustawić hasło.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Zmieniono dostęp do systemu - OpenSupports',
|
||||
'Zmieniono dostęp do systemu',
|
||||
|
|
|
@ -0,0 +1,384 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Support Center</title>
|
||||
|
||||
<style type="text/css">
|
||||
/* Take care of image borders and formatting, client hacks */
|
||||
img { max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic;}
|
||||
a img { border: none; }
|
||||
table { border-collapse: collapse !important;}
|
||||
#outlook a { padding:0; }
|
||||
.ReadMsgBody { width: 100%; }
|
||||
.ExternalClass { width: 100%; }
|
||||
.backgroundTable { margin: 0 auto; padding: 0; width: 100% !important; }
|
||||
table td { border-collapse: collapse; }
|
||||
.ExternalClass * { line-height: 115%; }
|
||||
.container-for-gmail-android { min-width: 600px; }
|
||||
|
||||
|
||||
/* General styling */
|
||||
* {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100% !important;
|
||||
margin: 0 !important;
|
||||
height: 100%;
|
||||
color: #676767;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #777777;
|
||||
text-align: center;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #676767;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.header-lg,
|
||||
.header-md,
|
||||
.header-sm {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
padding: 35px 0 0;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.header-md {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.header-sm {
|
||||
padding: 5px 0;
|
||||
font-size: 18px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.content-padding {
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
.mobile-header-padding-right {
|
||||
width: 290px;
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.mobile-header-padding-left {
|
||||
width: 290px;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.free-text {
|
||||
width: 100% !important;
|
||||
padding: 10px 60px 0px;
|
||||
}
|
||||
|
||||
.block-rounded {
|
||||
border-radius: 5px;
|
||||
border: 1px solid #e5e5e5;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 55px 0 0;
|
||||
}
|
||||
|
||||
.info-block {
|
||||
padding: 0 20px;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.mini-block-container {
|
||||
padding: 30px 50px;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.mini-block {
|
||||
background-color: #ffffff;
|
||||
width: 498px;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
padding: 60px 75px;
|
||||
}
|
||||
|
||||
.block-rounded {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.info-img {
|
||||
width: 258px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.force-width-img {
|
||||
width: 480px;
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.force-width-full {
|
||||
width: 600px;
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.user-img img {
|
||||
width: 82px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.user-img {
|
||||
width: 92px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-msg {
|
||||
width: 236px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
padding: 10px 0;
|
||||
border: 1px solid #cccccc;
|
||||
color: #4d4d4d;
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.force-width-gmail {
|
||||
min-width:600px;
|
||||
height: 0px !important;
|
||||
line-height: 1px !important;
|
||||
font-size: 1px !important;
|
||||
}
|
||||
|
||||
.button-width {
|
||||
width: 228px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style type="text/css" media="screen">
|
||||
@import url(http://fonts.googleapis.com/css?family=Oxygen:400,700);
|
||||
</style>
|
||||
|
||||
<style type="text/css" media="screen">
|
||||
@media screen {
|
||||
/* Thanks Outlook 2013! */
|
||||
* {
|
||||
font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style type="text/css" media="only screen and (max-width: 480px)">
|
||||
/* Mobile styles */
|
||||
@media only screen and (max-width: 480px) {
|
||||
|
||||
table[class*="container-for-gmail-android"] {
|
||||
min-width: 290px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class="w320"] {
|
||||
width: 320px !important;
|
||||
}
|
||||
|
||||
img[class="force-width-gmail"] {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
a[class="button-width"],
|
||||
a[class="button-mobile"] {
|
||||
width: 248px !important;
|
||||
}
|
||||
|
||||
td[class*="mobile-header-padding-left"] {
|
||||
width: 160px !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
td[class*="mobile-header-padding-right"] {
|
||||
width: 160px !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
td[class="header-lg"] {
|
||||
font-size: 24px !important;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
td[class="header-md"] {
|
||||
font-size: 18px !important;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
td[class="content-padding"] {
|
||||
padding: 5px 0 30px !important;
|
||||
}
|
||||
|
||||
td[class="button"] {
|
||||
padding: 15px 0 5px !important;
|
||||
}
|
||||
|
||||
td[class*="free-text"] {
|
||||
padding: 10px 18px 30px !important;
|
||||
}
|
||||
|
||||
img[class="force-width-img"],
|
||||
img[class="force-width-full"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
td[class="info-block"] {
|
||||
display: block !important;
|
||||
width: 280px !important;
|
||||
padding-bottom: 40px !important;
|
||||
}
|
||||
|
||||
td[class="info-img"],
|
||||
img[class="info-img"] {
|
||||
width: 278px !important;
|
||||
}
|
||||
|
||||
td[class="mini-block-container"] {
|
||||
padding: 8px 20px !important;
|
||||
width: 280px !important;
|
||||
}
|
||||
|
||||
td[class="mini-block"] {
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
||||
td[class="user-img"] {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
width: 100% !important;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
td[class="user-msg"] {
|
||||
display: block !important;
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#f7f7f7">
|
||||
<table align="center" cellpadding="0" cellspacing="0" class="container-for-gmail-android" width="100%">
|
||||
<tr>
|
||||
<td align="left" valign="top" width="100%" style="background-color: #ffffff;">
|
||||
<center>
|
||||
<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#ffffff" style="border-bottom: 1px solid #cccccc">
|
||||
<tr>
|
||||
<td width="100%" height="80" valign="top" style="text-align: center; vertical-align:middle;">
|
||||
<center>
|
||||
<table cellpadding="0" cellspacing="0" width="600" class="w320">
|
||||
<tr>
|
||||
<td style="vertical-align: middle;padding: 15px 0;">
|
||||
<img src="{{IMAGE_HEADER_URL}}" alt="logo">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="100%" style="background-color: #f7f7f7;" class="content-padding">
|
||||
<center>
|
||||
<table cellspacing="0" cellpadding="0" width="600" class="w320">
|
||||
<tr>
|
||||
<td class="header-lg">
|
||||
{{USER_INVITE_MATCH_1}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="free-text">
|
||||
{{USER_INVITE_MATCH_2}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mini-block-container">
|
||||
<table cellspacing="0" cellpadding="0" width="100%" style="border-collapse:separate !important;">
|
||||
<tr>
|
||||
<td class="mini-block">
|
||||
<table cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding-bottom: 30px;">
|
||||
{{USER_INVITE_MATCH_3}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="code-block">
|
||||
{{token}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="button">
|
||||
<div><a class="button-mobile" target="_blank" href="{{url}}/recover-password?email={{to}}&token={{token}}&invited=true"
|
||||
style="background-color:#ff6f6f;border-radius:5px;color:#ffffff;display:inline-block;font-family:'Cabin', Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;line-height:45px;text-align:center;text-decoration:none;width:155px;-webkit-text-size-adjust:none;mso-hide:all;">Set up your password</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="100%" style="background-color: #ffffff; border-top: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5; height: 100px;">
|
||||
<center>
|
||||
<table cellspacing="0" cellpadding="0" width="600" class="w320">
|
||||
<tr>
|
||||
<td style="padding: 25px 0 25px">
|
||||
<strong>OpenSupports</strong><br />
|
||||
Open source ticket system<br />
|
||||
www.opensupports.com<br /><br />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -22,21 +22,36 @@ class Log extends DataStore {
|
|||
'authorUser',
|
||||
'authorStaff',
|
||||
'to',
|
||||
'date'
|
||||
'date',
|
||||
'authorName'
|
||||
];
|
||||
}
|
||||
|
||||
public static function createLog($type,$to, $author = null) {
|
||||
public static function createLog($type, $to, $author = null) {
|
||||
$session = Session::getInstance();
|
||||
$authorName = '';
|
||||
|
||||
if($session->isTicketSession()) {
|
||||
$ticketNumber = $session->getTicketNumber();
|
||||
$ticket = Ticket::getByTicketNumber($ticketNumber);
|
||||
$authorName = $ticket->authorToArray()['name'];
|
||||
}
|
||||
|
||||
if($author === null) {
|
||||
$author = Controller::getLoggedUser();
|
||||
}
|
||||
|
||||
if(!$author->isNull()) {
|
||||
$authorName = $author->name;
|
||||
}
|
||||
|
||||
$log = new Log();
|
||||
|
||||
$log->setProperties(array(
|
||||
'type' => $type,
|
||||
'to' => $to,
|
||||
'date' => Date::getCurrentDate()
|
||||
'date' => Date::getCurrentDate(),
|
||||
'authorName' => $authorName
|
||||
));
|
||||
|
||||
if($author instanceof User) {
|
||||
|
@ -55,8 +70,8 @@ class Log extends DataStore {
|
|||
'type' => $this->type,
|
||||
'to' => $this->to,
|
||||
'author' => [
|
||||
'name' => $author->name,
|
||||
'id' => $author->id,
|
||||
'name' => $this->authorName,
|
||||
'id' => ($author && !$author->isNull()) ? $author->id : null,
|
||||
'staff' => $author instanceof Staff
|
||||
],
|
||||
'date' => $this->date
|
||||
|
|
|
@ -19,6 +19,7 @@ class MailTemplate extends DataStore {
|
|||
const USER_SIGNUP = 'USER_SIGNUP';
|
||||
const USER_PASSWORD = 'USER_PASSWORD';
|
||||
const PASSWORD_FORGOT = 'PASSWORD_FORGOT';
|
||||
const USER_INVITE = 'USER_INVITE';
|
||||
const USER_SYSTEM_DISABLED = 'USER_SYSTEM_DISABLED';
|
||||
const USER_SYSTEM_ENABLED = 'USER_SYSTEM_ENABLED';
|
||||
const TICKET_CREATED = 'TICKET_CREATED';
|
||||
|
@ -32,6 +33,7 @@ class MailTemplate extends DataStore {
|
|||
'USER_PASSWORD' => 'data/mail-templates/user-edit-password.html',
|
||||
'USER_EMAIL' => 'data/mail-templates/user-edit-email.html',
|
||||
'PASSWORD_FORGOT' => 'data/mail-templates/user-password-forgot.html',
|
||||
'USER_INVITE' => 'data/mail-templates/user-invite.html',
|
||||
'USER_SYSTEM_DISABLED' => 'data/mail-templates/user-system-disabled.html',
|
||||
'USER_SYSTEM_ENABLED' => 'data/mail-templates/user-system-enabled.html',
|
||||
'TICKET_CREATED' => 'data/mail-templates/ticket-created.html',
|
||||
|
|
|
@ -83,15 +83,16 @@ class Ticketevent extends DataStore {
|
|||
|
||||
public function toArray() {
|
||||
$user = ($this->authorStaff) ? $this->authorStaff : $this->authorUser;
|
||||
$author = $this->ticket->authorToArray();
|
||||
|
||||
return [
|
||||
'type' => $this->type,
|
||||
'ticketNumber' => $this->ticket->ticketNumber,
|
||||
'author' => [
|
||||
'name' => $user ? $user->name : null,
|
||||
'name' => $user ? $user->name : $author['name'],
|
||||
'staff' => $user instanceOf Staff,
|
||||
'id' => $user ? $user->id : null,
|
||||
'customfields' => $user->xownCustomfieldvalueList ? $user->xownCustomfieldvalueList->toArray() : [],
|
||||
'customfields' => ($user && $user->xownCustomfieldvalueList) ? $user->xownCustomfieldvalueList->toArray() : [],
|
||||
],
|
||||
'edited' => $this->editedContent
|
||||
];
|
||||
|
|
|
@ -33,7 +33,7 @@ require './ticket/change-department.rb'
|
|||
require './ticket/close.rb'
|
||||
require './ticket/re-open.rb'
|
||||
require './ticket/delete.rb'
|
||||
require './staff/add.rb'
|
||||
require './staff/invite.rb'
|
||||
require './staff/get.rb'
|
||||
require './staff/edit.rb'
|
||||
require './staff/delete.rb'
|
||||
|
|
|
@ -16,25 +16,33 @@ class Scripts
|
|||
})
|
||||
end
|
||||
|
||||
def self.createStaff(email, password, name, level='1')
|
||||
def self.createStaff(email, password, name, level='1') # WARNING: NOT USED ANYWHERE
|
||||
departments = request('/system/get-settings', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})['data']['departments']
|
||||
departments = departments.collect { |x| x.id }
|
||||
|
||||
response = request('/staff/add', {
|
||||
response = request('/staff/invite', {
|
||||
:name => name,
|
||||
:email => email,
|
||||
:password => password,
|
||||
:level => level,
|
||||
:departments => departments.to_string
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', email, 'email')
|
||||
|
||||
response = request('/user/recover-password', {
|
||||
email: email,
|
||||
password: password,
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
if response['status'] === 'fail'
|
||||
raise response['message']
|
||||
end
|
||||
end
|
||||
|
||||
def self.deleteStaff(staffId)
|
||||
response = request('/staff/delete', {
|
||||
staffId: staffId,
|
||||
|
@ -106,6 +114,7 @@ class Scripts
|
|||
description: description
|
||||
})
|
||||
end
|
||||
|
||||
def self.createTag(name, color)
|
||||
request('/ticket/create-tag', {
|
||||
csrf_userid: $csrf_userid,
|
||||
|
@ -114,6 +123,7 @@ class Scripts
|
|||
color: color
|
||||
})
|
||||
end
|
||||
|
||||
def self.assignTicket(ticketnumber)
|
||||
request('/staff/assign-ticket', {
|
||||
ticketNumber: ticketnumber,
|
||||
|
@ -121,6 +131,7 @@ class Scripts
|
|||
csrf_token: $csrf_token
|
||||
})
|
||||
end
|
||||
|
||||
def self.commentTicket(ticketnumber,content)
|
||||
request('/ticket/comment', {
|
||||
content: content,
|
||||
|
|
|
@ -32,17 +32,24 @@ describe'/staff/edit' do
|
|||
end
|
||||
|
||||
it 'should edit own data staff' do
|
||||
request('/staff/add', {
|
||||
request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Arya Stark',
|
||||
password: 'starkpassword',
|
||||
email: 'arya@opensupports.com',
|
||||
level: 1,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'arya@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'arya@opensupports.com',
|
||||
password: 'starkpassword',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
row = $database.getRow('staff', 'arya@opensupports.com', 'email')
|
||||
|
||||
result = request('/staff/edit', {
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
describe'/staff/add' do
|
||||
describe'/staff/invite' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
|
||||
it 'should add staff member' do
|
||||
result= request('/staff/add', {
|
||||
|
||||
result = request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Tyrion Lannister',
|
||||
email: 'tyrion@opensupports.com',
|
||||
password: 'testpassword',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'tyrion@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'tyrion@opensupports.com',
|
||||
password: 'testpassword',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
row = $database.getRow('staff', result['data']['id'], 'id')
|
||||
|
||||
(row['name']).should.equal('Tyrion Lannister')
|
||||
|
@ -27,16 +34,15 @@ describe'/staff/add' do
|
|||
(row['owners']).should.equal('4')
|
||||
|
||||
lastLog = $database.getLastRow('log')
|
||||
(lastLog['type']).should.equal('ADD_STAFF')
|
||||
(lastLog['type']).should.equal('INVITE')
|
||||
|
||||
end
|
||||
it 'should fail if staff member is alrady a staff' do
|
||||
result= request('/staff/add', {
|
||||
result = request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Tyrion Lannister',
|
||||
email: 'tyrion@opensupports.com',
|
||||
password: 'testpassword',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
|
@ -65,6 +65,35 @@ describe'system/disable-user-system' do
|
|||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should be able to comment on ticket as a non-logged user' do
|
||||
result = request('/ticket/create', {
|
||||
title: 'Doubt about Russian language',
|
||||
content: 'Stariy means old in Russian?',
|
||||
departmentId: 1,
|
||||
language: 'en',
|
||||
name: 'Abraham Einstein',
|
||||
email: 'abrahameinstein@opensupports.com'
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
ticketNumber = result['data']['ticketNumber']
|
||||
|
||||
result = request('/ticket/check', {
|
||||
ticketNumber: ticketNumber,
|
||||
email: 'abrahameinstein@opensupports.com',
|
||||
captcha: 'valid'
|
||||
})
|
||||
token = result['data']['token']
|
||||
(result['status']).should.equal('success');
|
||||
|
||||
result = request('/ticket/comment', {
|
||||
content: 'I actually think it is not like that, but anyways, thanks',
|
||||
ticketNumber: ticketNumber,
|
||||
csrf_token: token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should be able to assign and respond tickets' do
|
||||
Scripts.login($staff[:email], $staff[:password], true);
|
||||
ticket = $database.getLastRow('ticket');
|
||||
|
@ -84,6 +113,26 @@ describe'system/disable-user-system' do
|
|||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should be able to get the latest events as admin' do
|
||||
result = request('/staff/last-events', {
|
||||
page: 1,
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data'].size).should.equal(10)
|
||||
end
|
||||
|
||||
it 'should be able to get system logs as admin' do
|
||||
result = request('/system/get-logs', {
|
||||
page: 1,
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data'].size).should.equal(10)
|
||||
end
|
||||
|
||||
it 'should be be able to create a ticket as an admin' do
|
||||
result = request('/ticket/create', {
|
||||
title: 'created by staff with user system disabled',
|
||||
|
@ -113,7 +162,36 @@ describe'system/disable-user-system' do
|
|||
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_DISABLED')
|
||||
end
|
||||
|
||||
it 'should allow staff members to recover their passwords' do
|
||||
request('/user/logout')
|
||||
result = request('/user/send-recover-password', {
|
||||
email: 'jorah@opensupports.com',
|
||||
staff: true
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
token = $database.getLastRow('recoverpassword')['token'];
|
||||
|
||||
result = request('/user/recover-password', {
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 's3cur3p455w0rd',
|
||||
token: token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data']['staff']).should.equal('1')
|
||||
|
||||
result = request('/user/login', {
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 's3cur3p455w0rd',
|
||||
staff: true
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data']['userEmail']).should.equal('jorah@opensupports.com')
|
||||
end
|
||||
|
||||
it 'should enable the user system' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
result = request('/system/enable-user-system', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
|
@ -127,8 +205,7 @@ describe'system/disable-user-system' do
|
|||
|
||||
numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" )
|
||||
|
||||
(numberOftickets.num_rows).should.equal(52)
|
||||
|
||||
(numberOftickets.num_rows).should.equal(53)
|
||||
end
|
||||
|
||||
it 'should not enable the user system' do
|
||||
|
@ -140,6 +217,5 @@ describe'system/disable-user-system' do
|
|||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_ENABLED')
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -182,18 +182,28 @@ describe '/ticket/comment/' do
|
|||
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
request('/staff/add', {
|
||||
|
||||
result = request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Jorah mormont',
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 'testpassword',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
(result['status'].should.equal('success'))
|
||||
|
||||
request('/user/logout')
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'jorah@opensupports.com', 'email')
|
||||
request('/user/recover-password', {
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 'testpassword',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
Scripts.login('jorah@opensupports.com', 'testpassword', true)
|
||||
result = request('/ticket/comment', {
|
||||
content: 'some comment content',
|
||||
|
|
|
@ -6,17 +6,24 @@ describe '/ticket/delete' do
|
|||
Scripts.createTicket('ticket_to_delete')
|
||||
ticket = $database.getRow('ticket', 'ticket_to_delete', 'title')
|
||||
|
||||
request('/staff/add', {
|
||||
request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Ned Stark',
|
||||
password: 'headless',
|
||||
email: 'ned@opensupports.com',
|
||||
level: 3,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'ned@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'ned@opensupports.com',
|
||||
password: 'headless',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.login('ned@opensupports.com', 'headless', true)
|
||||
|
||||
|
@ -80,16 +87,24 @@ describe '/ticket/delete' do
|
|||
|
||||
ticket = $database.getRow('ticket', 'ticket_to_delete_4', 'title');
|
||||
|
||||
request('/staff/add', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Joan Chris',
|
||||
password: 'theyaregonnafireme',
|
||||
email: 'uselessstaff@opensupports.com',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Joan Chris',
|
||||
email: 'uselessstaff@opensupports.com',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'uselessstaff@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'uselessstaff@opensupports.com',
|
||||
password: 'theyaregonnafireme',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
request('/user/logout')
|
||||
|
||||
Scripts.login('uselessstaff@opensupports.com', 'theyaregonnafireme',true)
|
||||
|
|
Loading…
Reference in New Issue