mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-31 01:35:15 +02:00
Merge branch 'master' into master
This commit is contained in:
commit
b9e4a55b91
@ -23,6 +23,7 @@ class ActivityRow extends React.Component {
|
|||||||
|
|
||||||
'EDIT_SETTINGS',
|
'EDIT_SETTINGS',
|
||||||
'SIGNUP',
|
'SIGNUP',
|
||||||
|
'INVITE',
|
||||||
'ADD_TOPIC',
|
'ADD_TOPIC',
|
||||||
'ADD_ARTICLE',
|
'ADD_ARTICLE',
|
||||||
'DELETE_TOPIC',
|
'DELETE_TOPIC',
|
||||||
@ -65,9 +66,7 @@ class ActivityRow extends React.Component {
|
|||||||
<div className="activity-row">
|
<div className="activity-row">
|
||||||
<Icon {...this.getIconProps()} className="activity-row__icon"/>
|
<Icon {...this.getIconProps()} className="activity-row__icon"/>
|
||||||
<span>
|
<span>
|
||||||
<Link className="activity-row__name-link" to={this.getNameLinkDestination()}>
|
{this.renderAuthorName()}
|
||||||
{this.props.author.name}
|
|
||||||
</Link>
|
|
||||||
</span>
|
</span>
|
||||||
<span className="activity-row__message"> {i18n('ACTIVITY_' + this.props.type)} </span>
|
<span className="activity-row__message"> {i18n('ACTIVITY_' + this.props.type)} </span>
|
||||||
{_.includes(ticketRelatedTypes, this.props.type) ? this.renderTicketNumber() : this.props.to}
|
{_.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() {
|
renderTicketNumber() {
|
||||||
let ticketNumber = (this.props.mode === 'staff') ? this.props.ticketNumber : this.props.to;
|
let ticketNumber = (this.props.mode === 'staff') ? this.props.ticketNumber : this.props.to;
|
||||||
|
|
||||||
@ -106,6 +117,7 @@ class ActivityRow extends React.Component {
|
|||||||
|
|
||||||
'EDIT_SETTINGS': 'wrench',
|
'EDIT_SETTINGS': 'wrench',
|
||||||
'SIGNUP': 'user-plus',
|
'SIGNUP': 'user-plus',
|
||||||
|
'INVITE': 'user-plus',
|
||||||
'ADD_TOPIC': 'book',
|
'ADD_TOPIC': 'book',
|
||||||
'ADD_ARTICLE': 'book',
|
'ADD_ARTICLE': 'book',
|
||||||
'DELETE_TOPIC': 'book',
|
'DELETE_TOPIC': 'book',
|
||||||
|
@ -107,18 +107,18 @@ class AdminPanelMenu extends React.Component {
|
|||||||
getRoutes() {
|
getRoutes() {
|
||||||
const customLists = this.getCustomlists();
|
const customLists = this.getCustomlists();
|
||||||
|
|
||||||
return this.getItemsByFilteredByLevel([
|
return this.getItemsByFilteredByLevel(_.without([
|
||||||
{
|
{
|
||||||
groupName: i18n('DASHBOARD'),
|
groupName: i18n('DASHBOARD'),
|
||||||
path: '/admin/panel',
|
path: '/admin/panel',
|
||||||
icon: 'tachometer',
|
icon: 'tachometer',
|
||||||
level: 1,
|
level: 1,
|
||||||
items: this.getItemsByFilteredByLevel([
|
items: this.getItemsByFilteredByLevel([
|
||||||
{
|
/*{
|
||||||
name: i18n('STATISTICS'),
|
name: i18n('STATISTICS'),
|
||||||
path: '/admin/panel/stats',
|
path: '/admin/panel/stats',
|
||||||
level: 1
|
level: 1
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
name: i18n('LAST_ACTIVITY'),
|
name: i18n('LAST_ACTIVITY'),
|
||||||
path: '/admin/panel/activity',
|
path: '/admin/panel/activity',
|
||||||
@ -155,7 +155,7 @@ class AdminPanelMenu extends React.Component {
|
|||||||
...customLists
|
...customLists
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
{
|
this.props.config['user-system-enabled'] ? {
|
||||||
groupName: i18n('USERS'),
|
groupName: i18n('USERS'),
|
||||||
path: '/admin/panel/users',
|
path: '/admin/panel/users',
|
||||||
icon: 'user',
|
icon: 'user',
|
||||||
@ -177,7 +177,7 @@ class AdminPanelMenu extends React.Component {
|
|||||||
level: 1
|
level: 1
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
},
|
} : null,
|
||||||
{
|
{
|
||||||
groupName: i18n('ARTICLES'),
|
groupName: i18n('ARTICLES'),
|
||||||
path: '/admin/panel/articles',
|
path: '/admin/panel/articles',
|
||||||
@ -192,7 +192,6 @@ class AdminPanelMenu extends React.Component {
|
|||||||
])
|
])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
||||||
groupName: i18n('STAFF'),
|
groupName: i18n('STAFF'),
|
||||||
path: '/admin/panel/staff',
|
path: '/admin/panel/staff',
|
||||||
icon: 'users',
|
icon: 'users',
|
||||||
@ -239,7 +238,7 @@ class AdminPanelMenu extends React.Component {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
]);
|
], null));
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemsByFilteredByLevel(items) {
|
getItemsByFilteredByLevel(items) {
|
||||||
@ -249,6 +248,7 @@ class AdminPanelMenu extends React.Component {
|
|||||||
|
|
||||||
export default connect((store) => {
|
export default connect((store) => {
|
||||||
return {
|
return {
|
||||||
level: store.session.userLevel
|
level: store.session.userLevel,
|
||||||
|
config: store.config
|
||||||
};
|
};
|
||||||
})(AdminPanelMenu);
|
})(AdminPanelMenu);
|
||||||
|
@ -11,7 +11,7 @@ import SessionStore from 'lib-app/session-store';
|
|||||||
import PeopleList from 'app-components/people-list';
|
import PeopleList from 'app-components/people-list';
|
||||||
import ModalContainer from 'app-components/modal-container';
|
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 Header from 'core-components/header';
|
||||||
import DropDown from 'core-components/drop-down';
|
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')} />
|
<Header title={i18n('STAFF_MEMBERS')} description={i18n('STAFF_MEMBERS_DESCRIPTION')} />
|
||||||
<div className="admin-panel-staff-members__wrapper">
|
<div className="admin-panel-staff-members__wrapper">
|
||||||
<DepartmentDropdown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
<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">
|
<Button onClick={this.onInviteStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||||
<Icon name="user-plus" className=""/> {i18n('ADD_NEW_STAFF')}
|
<Icon name="user-plus" className=""/> {i18n('INVITE_STAFF')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{(this.props.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />}
|
{(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() {
|
onInviteStaff() {
|
||||||
ModalContainer.openModal(<AddStaffModal onSuccess={this.retrieveStaffMembers.bind(this)} />);
|
ModalContainer.openModal(<InviteStaffModal onSuccess={this.retrieveStaffMembers.bind(this)} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDepartmentDropdownProps() {
|
getDepartmentDropdownProps() {
|
||||||
|
120
client/src/app/admin/panel/staff/invite-staff-modal.js
Normal file
120
client/src/app/admin/panel/staff/invite-staff-modal.js
Normal file
@ -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;
|
23
client/src/app/admin/panel/staff/invite-staff-modal.scss
Normal file
23
client/src/app/admin/panel/staff/invite-staff-modal.scss
Normal file
@ -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 React from 'react';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
@ -12,10 +13,9 @@ import Button from 'core-components/button';
|
|||||||
import Message from 'core-components/message';
|
import Message from 'core-components/message';
|
||||||
import Icon from 'core-components/icon';
|
import Icon from 'core-components/icon';
|
||||||
import ModalContainer from 'app-components/modal-container';
|
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 {
|
class AdminPanelListUsers extends React.Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
users: [],
|
users: [],
|
||||||
@ -39,11 +39,19 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="admin-panel-list-users">
|
<div className="admin-panel-list-users">
|
||||||
<Header title={i18n('LIST_USERS')} description={i18n('LIST_USERS_DESCRIPTION')} />
|
<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)} />
|
<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}}>
|
<div style={{textAlign: 'right', marginTop: 10}}>
|
||||||
<Button onClick={this.onCreateUser.bind(this)} type="secondary" size="medium">
|
<Button onClick={this.onInviteUser.bind(this)} type="secondary" size="medium">
|
||||||
<Icon size="sm" name="plus"/> {i18n('ADD_USER')}
|
<Icon size="sm" name="plus"/> {i18n('INVITE_USER')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -167,17 +175,17 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
}).catch(this.onUsersRejected.bind(this)).then(this.onUsersRetrieved.bind(this));
|
}).catch(this.onUsersRejected.bind(this)).then(this.onUsersRetrieved.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
onCreateUser(user) {
|
onInviteUser(user) {
|
||||||
ModalContainer.openModal(
|
ModalContainer.openModal(
|
||||||
<div className="admin-panel-list-users__add-user-form">
|
<div className="admin-panel-list-users__invite-user-form">
|
||||||
<MainSignUpWidget onSuccess={this.onCreateUserSuccess.bind(this)} />
|
<InviteUserWidget onSuccess={this.onInviteUserSuccess.bind(this)} />
|
||||||
<div style={{textAlign: 'center'}}>
|
<div style={{textAlign: 'center'}}>
|
||||||
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
onCreateUserSuccess() {
|
onInviteUserSuccess() {
|
||||||
ModalContainer.closeModal();
|
ModalContainer.closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,4 +209,8 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminPanelListUsers;
|
export default connect((store) => {
|
||||||
|
return {
|
||||||
|
config: store.config
|
||||||
|
};
|
||||||
|
})(AdminPanelListUsers);
|
||||||
|
165
client/src/app/admin/panel/users/invite-user-widget.js
Normal file
165
client/src/app/admin/panel/users/invite-user-widget.js
Normal file
@ -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;
|
19
client/src/app/admin/panel/users/invite-user-widget.scss
Normal file
19
client/src/app/admin/panel/users/invite-user-widget.scss
Normal file
@ -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'
|
message: 'success'
|
||||||
}, () => {
|
}, () => {
|
||||||
if(this.props.onSuccess) {
|
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')) {
|
if((this.props.location.pathname !== '/create-ticket')) {
|
||||||
setTimeout(() => {history.push('/dashboard')}, 2000);
|
setTimeout(() => {history.push('/dashboard')}, 2000);
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,7 +30,7 @@ class MainRecoverPasswordPage extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="main-recover-password-page">
|
<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}>
|
<Form className="recover-password__form" onSubmit={this.onRecoverPasswordSubmit.bind(this)} loading={this.state.loading}>
|
||||||
<div className="recover-password__inputs">
|
<div className="recover-password__inputs">
|
||||||
<FormField placeholder={i18n('NEW_PASSWORD')} name="password" className="recover-password__input" validation="PASSWORD" fieldProps={{password: true}} required/>
|
<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 React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
|
|
||||||
import Widget from 'core-components/widget';
|
|
||||||
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
|
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
|
||||||
|
|
||||||
class MainSignUpPage extends React.Component {
|
class MainSignUpPage extends React.Component {
|
||||||
@ -9,70 +7,10 @@ class MainSignUpPage extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="main-signup-page">
|
<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>
|
</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;
|
export default MainSignUpPage;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
|
import history from 'lib-app/history';
|
||||||
|
|
||||||
import Captcha from 'app/main/captcha';
|
import Captcha from 'app/main/captcha';
|
||||||
import SubmitButton from 'core-components/submit-button';
|
import SubmitButton from 'core-components/submit-button';
|
||||||
@ -17,7 +17,6 @@ import Header from 'core-components/header';
|
|||||||
class MainSignUpWidget extends React.Component {
|
class MainSignUpWidget extends React.Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onSuccess: React.PropTypes.func,
|
|
||||||
className: React.PropTypes.string
|
className: React.PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,6 +152,8 @@ class MainSignUpWidget extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
message: 'success'
|
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_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.',
|
'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.',
|
'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.',
|
'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.',
|
'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.',
|
'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.',
|
||||||
|
@ -272,7 +272,7 @@ export default {
|
|||||||
'INSTALLATION_COMPLETED': 'Installation abgeschlossen.',
|
'INSTALLATION_COMPLETED': 'Installation abgeschlossen.',
|
||||||
'INSTALLATION_COMPLETED_DESCRIPTION': 'Die Installation von OpenSupports ist abgeschlossen. Umleitung zum Admin-Panel...',
|
'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_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_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.',
|
'STEP_3_DESCRIPTION': 'Bitte füllen Sie die MySQL-Datenbankkonfiguration aus.',
|
||||||
|
@ -11,6 +11,7 @@ export default {
|
|||||||
'SIGN_UP': 'Sign up',
|
'SIGN_UP': 'Sign up',
|
||||||
'FORGOT_PASSWORD': 'Forgot your password?',
|
'FORGOT_PASSWORD': 'Forgot your password?',
|
||||||
'RECOVER_PASSWORD': 'Recover Password',
|
'RECOVER_PASSWORD': 'Recover Password',
|
||||||
|
'SET_UP_PASSWORD': 'Set up your password',
|
||||||
'RECOVER_SENT': 'An email with recover instructions has been sent.',
|
'RECOVER_SENT': 'An email with recover instructions has been sent.',
|
||||||
'NEW_EMAIL': 'New email',
|
'NEW_EMAIL': 'New email',
|
||||||
'FULL_NAME': 'Full name',
|
'FULL_NAME': 'Full name',
|
||||||
@ -194,6 +195,8 @@ export default {
|
|||||||
'NEVER': 'Never',
|
'NEVER': 'Never',
|
||||||
'HIMSELF': 'himself',
|
'HIMSELF': 'himself',
|
||||||
'ADD_USER': 'Add user',
|
'ADD_USER': 'Add user',
|
||||||
|
'INVITE_USER': 'Invite user',
|
||||||
|
'INVITE_STAFF': 'Invite staff',
|
||||||
'UPLOAD_FILE': 'Upload file',
|
'UPLOAD_FILE': 'Upload file',
|
||||||
'PRIVATE': 'Private',
|
'PRIVATE': 'Private',
|
||||||
'ENABLE_USER': 'Enable User',
|
'ENABLE_USER': 'Enable User',
|
||||||
@ -233,6 +236,7 @@ export default {
|
|||||||
|
|
||||||
'ACTIVITY_EDIT_SETTINGS': 'edited settings',
|
'ACTIVITY_EDIT_SETTINGS': 'edited settings',
|
||||||
'ACTIVITY_SIGNUP': 'signed up',
|
'ACTIVITY_SIGNUP': 'signed up',
|
||||||
|
'ACTIVITY_INVITE': 'invited user',
|
||||||
'ACTIVITY_ADD_TOPIC': 'added topic',
|
'ACTIVITY_ADD_TOPIC': 'added topic',
|
||||||
'ACTIVITY_ADD_ARTICLE': 'added article',
|
'ACTIVITY_ADD_ARTICLE': 'added article',
|
||||||
'ACTIVITY_DELETE_TOPIC': 'deleted topic',
|
'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}',
|
'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.',
|
'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.',
|
'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
|
//ERRORS
|
||||||
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
||||||
@ -374,6 +380,7 @@ export default {
|
|||||||
|
|
||||||
//MESSAGES
|
//MESSAGES
|
||||||
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',
|
'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.',
|
'TICKET_SENT': 'Ticket has been created successfully.',
|
||||||
'VALID_RECOVER': 'Password recovered successfully',
|
'VALID_RECOVER': 'Password recovered successfully',
|
||||||
'EMAIL_EXISTS': 'Email already exists',
|
'EMAIL_EXISTS': 'Email already exists',
|
||||||
|
@ -206,7 +206,7 @@ export default {
|
|||||||
'TYPE': 'Tipo',
|
'TYPE': 'Tipo',
|
||||||
'SELECT_INPUT': 'Seleccionar entrada',
|
'SELECT_INPUT': 'Seleccionar entrada',
|
||||||
'TEXT_INPUT': 'Entrada de texto',
|
'TEXT_INPUT': 'Entrada de texto',
|
||||||
'OPTION': 'Opción {índice}',
|
'OPTION': 'Opción {index}',
|
||||||
'OPTIONS': 'Opciones',
|
'OPTIONS': 'Opciones',
|
||||||
'FIELD_DESCRIPTION': 'Descripción del campo (opcional)',
|
'FIELD_DESCRIPTION': 'Descripción del campo (opcional)',
|
||||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puedes agregar una nueva etiqueta personalizada',
|
'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puedes agregar una nueva etiqueta personalizada',
|
||||||
|
@ -62,7 +62,7 @@ export default {
|
|||||||
'HIGH': 'Haute',
|
'HIGH': 'Haute',
|
||||||
'MEDIUM': 'Moyenne',
|
'MEDIUM': 'Moyenne',
|
||||||
'LOW': 'Faible',
|
'LOW': 'Faible',
|
||||||
'TITLE': 'Titre',
|
'TITLE': 'Objet',
|
||||||
'CONTENT': 'Contenu',
|
'CONTENT': 'Contenu',
|
||||||
'SAVE': 'Enregistrer',
|
'SAVE': 'Enregistrer',
|
||||||
'DISCARD_CHANGES': 'Annuler les modifications',
|
'DISCARD_CHANGES': 'Annuler les modifications',
|
||||||
@ -343,7 +343,7 @@ export default {
|
|||||||
'ERROR_EMPTY': 'Valeur invalide',
|
'ERROR_EMPTY': 'Valeur invalide',
|
||||||
'ERROR_PASSWORD': 'Mot de passe incorrect',
|
'ERROR_PASSWORD': 'Mot de passe incorrect',
|
||||||
'ERROR_NAME': 'Nom incorrect',
|
'ERROR_NAME': 'Nom incorrect',
|
||||||
'ERROR_TITLE': 'Titre incorrect',
|
'ERROR_TITLE': 'Objet incorrect',
|
||||||
'ERROR_EMAIL': 'Email invalide',
|
'ERROR_EMAIL': 'Email invalide',
|
||||||
'ERROR_CONTENT_SHORT': 'Contenu trop court',
|
'ERROR_CONTENT_SHORT': 'Contenu trop court',
|
||||||
'PASSWORD_NOT_MATCH': 'Le mot de passe ne correspond pas',
|
'PASSWORD_NOT_MATCH': 'Le mot de passe ne correspond pas',
|
||||||
|
@ -300,7 +300,7 @@
|
|||||||
'DELETE_USER_DESCRIPTION': 'Ο χρήστης δεν θα μπορέσει να συνδεθεί με τη γήρανση και όλα τα εισιτήρια του θα διαγραφούν. Επίσης, το ηλεκτρονικό ταχυδρομείο δεν μπορεί πλέον να χρησιμοποιηθεί.',
|
'DELETE_USER_DESCRIPTION': 'Ο χρήστης δεν θα μπορέσει να συνδεθεί με τη γήρανση και όλα τα εισιτήρια του θα διαγραφούν. Επίσης, το ηλεκτρονικό ταχυδρομείο δεν μπορεί πλέον να χρησιμοποιηθεί.',
|
||||||
'DELETE_TOPIC_DESCRIPTION': 'Διαγράφοντας το θέμα, όλα τα άρθρα σε αυτό θα διαγραφούν.',
|
'DELETE_TOPIC_DESCRIPTION': 'Διαγράφοντας το θέμα, όλα τα άρθρα σε αυτό θα διαγραφούν.',
|
||||||
'EDIT_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να αλλάξετε το όνομα, το εικονίδιο και το χρώμα του εικονιδίου του θέματος.',
|
'EDIT_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να αλλάξετε το όνομα, το εικονίδιο και το χρώμα του εικονιδίου του θέματος.',
|
||||||
'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {κατηγορία}.',
|
'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {category}.',
|
||||||
'LIST_ARTICLES_DESCRIPTION': 'Αυτή είναι μια λίστα με άρθρα που περιλαμβάνουν πληροφορίες σχετικά με τις υπηρεσίες μας.',
|
'LIST_ARTICLES_DESCRIPTION': 'Αυτή είναι μια λίστα με άρθρα που περιλαμβάνουν πληροφορίες σχετικά με τις υπηρεσίες μας.',
|
||||||
'ADD_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα θέμα που λειτουργεί ως κατηγορία για άρθρα.',
|
'ADD_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα θέμα που λειτουργεί ως κατηγορία για άρθρα.',
|
||||||
'DELETE_ARTICLE_DESCRIPTION': 'Πρόκειται να διαγράψετε αυτό το άρθρο για πάντα.',
|
'DELETE_ARTICLE_DESCRIPTION': 'Πρόκειται να διαγράψετε αυτό το άρθρο για πάντα.',
|
||||||
|
@ -206,7 +206,7 @@ export default {
|
|||||||
'TYPE': 'genere',
|
'TYPE': 'genere',
|
||||||
'SELECT_INPUT': 'Seleziona input',
|
'SELECT_INPUT': 'Seleziona input',
|
||||||
'TEXT_INPUT': 'L\'immissione di testo',
|
'TEXT_INPUT': 'L\'immissione di testo',
|
||||||
'OPTION': 'Opzione {indice}',
|
'OPTION': 'Opzione {index}',
|
||||||
'OPTIONS': 'Opzioni',
|
'OPTIONS': 'Opzioni',
|
||||||
'FIELD_DESCRIPTION': 'Descrizione del campo (facoltativo)',
|
'FIELD_DESCRIPTION': 'Descrizione del campo (facoltativo)',
|
||||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'qui puoi aggiungere un nuovo tag personalizzato',
|
'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_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.',
|
'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.',
|
'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.',
|
'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.',
|
'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.',
|
'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 GetNewTicketsStaffController);
|
||||||
$systemControllerGroup->addController(new GetAllTicketsStaffController);
|
$systemControllerGroup->addController(new GetAllTicketsStaffController);
|
||||||
$systemControllerGroup->addController(new SearchTicketStaffController);
|
$systemControllerGroup->addController(new SearchTicketStaffController);
|
||||||
$systemControllerGroup->addController(new AddStaffController);
|
$systemControllerGroup->addController(new InviteStaffController);
|
||||||
$systemControllerGroup->addController(new GetAllStaffController);
|
$systemControllerGroup->addController(new GetAllStaffController);
|
||||||
$systemControllerGroup->addController(new DeleteStaffController);
|
$systemControllerGroup->addController(new DeleteStaffController);
|
||||||
$systemControllerGroup->addController(new EditStaffController);
|
$systemControllerGroup->addController(new EditStaffController);
|
||||||
|
@ -11,7 +11,7 @@ use Respect\Validation\Validator as DataValidator;
|
|||||||
*
|
*
|
||||||
* @apiDescription This path retrieves information about all the staff member.
|
* @apiDescription This path retrieves information about all the staff member.
|
||||||
*
|
*
|
||||||
* @apiPermission staff3
|
* @apiPermission staff1
|
||||||
*
|
*
|
||||||
* @apiUse NO_PERMISSION
|
* @apiUse NO_PERMISSION
|
||||||
*
|
*
|
||||||
|
@ -3,20 +3,19 @@ use Respect\Validation\Validator as DataValidator;
|
|||||||
DataValidator::with('CustomValidations', true);
|
DataValidator::with('CustomValidations', true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /staff/add Add staff
|
* @api {post} /staff/invite Invite staff
|
||||||
* @apiVersion 4.5.0
|
* @apiVersion 4.5.0
|
||||||
*
|
*
|
||||||
* @apiName Add staff
|
* @apiName Invite staff
|
||||||
*
|
*
|
||||||
* @apiGroup Staff
|
* @apiGroup Staff
|
||||||
*
|
*
|
||||||
* @apiDescription This path adds a new staff member.
|
* @apiDescription This path invites a new staff member.
|
||||||
*
|
*
|
||||||
* @apiPermission staff3
|
* @apiPermission staff3
|
||||||
*
|
*
|
||||||
* @apiParam {String} name The name of the new staff member.
|
* @apiParam {String} name The name of the new staff member.
|
||||||
* @apiParam {String} email The email 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 {Number} level The level of the new staff member.
|
||||||
* @apiParam {String} profilePic The profile pic 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.
|
* @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 {
|
class InviteStaffController extends Controller {
|
||||||
const PATH = '/add';
|
const PATH = '/invite';
|
||||||
const METHOD = 'POST';
|
const METHOD = 'POST';
|
||||||
|
|
||||||
private $name;
|
private $name;
|
||||||
private $email;
|
private $email;
|
||||||
private $password;
|
|
||||||
private $profilePic;
|
private $profilePic;
|
||||||
private $level;
|
private $level;
|
||||||
private $departments;
|
private $departments;
|
||||||
|
|
||||||
|
|
||||||
public function validations() {
|
public function validations() {
|
||||||
return [
|
return [
|
||||||
'permission' => 'staff_3',
|
'permission' => 'staff_3',
|
||||||
@ -57,53 +54,55 @@ class AddStaffController extends Controller {
|
|||||||
'validation' => DataValidator::email(),
|
'validation' => DataValidator::email(),
|
||||||
'error' => ERRORS::INVALID_EMAIL
|
'error' => ERRORS::INVALID_EMAIL
|
||||||
],
|
],
|
||||||
'password' => [
|
|
||||||
'validation' => DataValidator::length(5, 200),
|
|
||||||
'error' => ERRORS::INVALID_PASSWORD
|
|
||||||
],
|
|
||||||
'level' => [
|
'level' => [
|
||||||
'validation' => DataValidator::between(1, 3, true),
|
'validation' => DataValidator::between(1, 3, true),
|
||||||
'error' => ERRORS::INVALID_LEVEL
|
'error' => ERRORS::INVALID_LEVEL
|
||||||
]
|
]
|
||||||
|
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handler() {
|
public function handler() {
|
||||||
$this->storeRequestData();
|
$this->storeRequestData();
|
||||||
$staff = new Staff();
|
|
||||||
|
|
||||||
$staffRow = Staff::getDataStore($this->email, 'email');
|
$staffRow = Staff::getDataStore($this->email, 'email');
|
||||||
|
|
||||||
if($staffRow->isNull()) {
|
if(!$staffRow->isNull()) throw new RequestException(ERRORS::ALREADY_A_STAFF);
|
||||||
|
|
||||||
|
$staff = new Staff();
|
||||||
$staff->setProperties([
|
$staff->setProperties([
|
||||||
'name'=> $this->name,
|
'name'=> $this->name,
|
||||||
'email' => $this->email,
|
'email' => $this->email,
|
||||||
'password'=> Hashing::hashPassword($this->password),
|
'password'=> Hashing::hashPassword(Hashing::generateRandomToken()),
|
||||||
'profilePic' => $this->profilePic,
|
'profilePic' => $this->profilePic,
|
||||||
'level' => $this->level,
|
'level' => $this->level,
|
||||||
'sharedDepartmentList' => $this->getDepartmentList()
|
'sharedDepartmentList' => $this->getDepartmentList()
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->addOwner();
|
$this->addOwner();
|
||||||
|
|
||||||
Log::createLog('ADD_STAFF', $this->name);
|
$this->token = Hashing::generateRandomToken();
|
||||||
|
|
||||||
|
$recoverPassword = new RecoverPassword();
|
||||||
|
$recoverPassword->setProperties(array(
|
||||||
|
'email' => $this->email,
|
||||||
|
'token' => $this->token,
|
||||||
|
'staff' => true
|
||||||
|
));
|
||||||
|
$recoverPassword->store();
|
||||||
|
|
||||||
|
$this->sendInvitationMail();
|
||||||
|
|
||||||
Response::respondSuccess([
|
Response::respondSuccess([
|
||||||
'id' => $staff->store()
|
'id' => $staff->store()
|
||||||
]);
|
]);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RequestException(ERRORS::ALREADY_A_STAFF);
|
Log::createLog('INVITE', $this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function storeRequestData() {
|
public function storeRequestData() {
|
||||||
$this->name = Controller::request('name');
|
$this->name = Controller::request('name');
|
||||||
$this->email = Controller::request('email');
|
$this->email = Controller::request('email');
|
||||||
$this->password = Controller::request('password');
|
|
||||||
$this->profilePic = Controller::request('profilePic');
|
$this->profilePic = Controller::request('profilePic');
|
||||||
$this->level = Controller::request('level');
|
$this->level = Controller::request('level');
|
||||||
$this->departments = Controller::request('departments');
|
$this->departments = Controller::request('departments');
|
||||||
@ -120,6 +119,7 @@ class AddStaffController extends Controller {
|
|||||||
|
|
||||||
return $listDepartments;
|
return $listDepartments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addOwner() {
|
public function addOwner() {
|
||||||
$departmentIds = json_decode($this->departments);
|
$departmentIds = json_decode($this->departments);
|
||||||
|
|
||||||
@ -129,4 +129,17 @@ class AddStaffController extends Controller {
|
|||||||
$departmentRow->store();
|
$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 $ticket;
|
||||||
private $content;
|
private $content;
|
||||||
|
private $session;
|
||||||
|
|
||||||
public function validations() {
|
public function validations() {
|
||||||
$session = Session::getInstance();
|
$this->session = Session::getInstance();
|
||||||
|
|
||||||
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
|
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
|
||||||
return [
|
return [
|
||||||
@ -64,11 +65,11 @@ class CommentController extends Controller {
|
|||||||
'error' => ERRORS::INVALID_CONTENT
|
'error' => ERRORS::INVALID_CONTENT
|
||||||
],
|
],
|
||||||
'ticketNumber' => [
|
'ticketNumber' => [
|
||||||
'validation' => DataValidator::equals($session->getTicketNumber()),
|
'validation' => DataValidator::equals($this->session->getTicketNumber()),
|
||||||
'error' => ERRORS::INVALID_TICKET
|
'error' => ERRORS::INVALID_TICKET
|
||||||
],
|
],
|
||||||
'csrf_token' => [
|
'csrf_token' => [
|
||||||
'validation' => DataValidator::equals($session->getToken()),
|
'validation' => DataValidator::equals($this->session->getToken()),
|
||||||
'error' => ERRORS::INVALID_TOKEN
|
'error' => ERRORS::INVALID_TOKEN
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -79,28 +80,29 @@ class CommentController extends Controller {
|
|||||||
public function handler() {
|
public function handler() {
|
||||||
$this->requestData();
|
$this->requestData();
|
||||||
$ticketAuthor = $this->ticket->authorToArray();
|
$ticketAuthor = $this->ticket->authorToArray();
|
||||||
$isAuthor = $this->ticket->isAuthor(Controller::getLoggedUser()) || Session::getInstance()->isTicketSession();
|
$isAuthor = $this->session->isTicketSession() || $this->ticket->isAuthor($this->user);
|
||||||
$isOwner = $this->ticket->isOwner(Controller::getLoggedUser());
|
$isOwner = $this->ticket->isOwner($this->user);
|
||||||
$user = Controller::getLoggedUser();
|
$private = Controller::request('private');
|
||||||
|
|
||||||
if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() && !$isAuthor){
|
if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() && !$isAuthor){
|
||||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
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);
|
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->storeComment();
|
$this->storeComment();
|
||||||
|
|
||||||
if($isAuthor && $this->ticket->owner) {
|
if(!$isAuthor && !$private) {
|
||||||
|
$this->sendMail($ticketAuthor);
|
||||||
|
}
|
||||||
|
if($this->ticket->owner && !$isOwner) {
|
||||||
$this->sendMail([
|
$this->sendMail([
|
||||||
'email' => $this->ticket->owner->email,
|
'email' => $this->ticket->owner->email,
|
||||||
'name' => $this->ticket->owner->name,
|
'name' => $this->ticket->owner->name,
|
||||||
'staff' => true
|
'staff' => true
|
||||||
]);
|
]);
|
||||||
} else if($isOwner) {
|
|
||||||
!Controller::request('private') ? $this->sendMail($ticketAuthor) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::createLog('COMMENT', $this->ticket->ticketNumber);
|
Log::createLog('COMMENT', $this->ticket->ticketNumber);
|
||||||
@ -112,6 +114,7 @@ class CommentController extends Controller {
|
|||||||
$ticketNumber = Controller::request('ticketNumber');
|
$ticketNumber = Controller::request('ticketNumber');
|
||||||
$this->ticket = Ticket::getByTicketNumber($ticketNumber);
|
$this->ticket = Ticket::getByTicketNumber($ticketNumber);
|
||||||
$this->content = Controller::request('content', true);
|
$this->content = Controller::request('content', true);
|
||||||
|
$this->user = Controller::getLoggedUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function storeComment() {
|
private function storeComment() {
|
||||||
@ -129,12 +132,14 @@ class CommentController extends Controller {
|
|||||||
));
|
));
|
||||||
|
|
||||||
if(Controller::isStaffLogged()) {
|
if(Controller::isStaffLogged()) {
|
||||||
$this->ticket->unread = !$this->ticket->isAuthor(Controller::getLoggedUser());
|
$this->ticket->unread = !$this->ticket->isAuthor($this->user);
|
||||||
$this->ticket->unreadStaff = !$this->ticket->isOwner(Controller::getLoggedUser());
|
$this->ticket->unreadStaff = !$this->ticket->isOwner($this->user);
|
||||||
$comment->authorStaff = Controller::getLoggedUser();
|
$comment->authorStaff = $this->user;
|
||||||
} else if(Controller::isUserSystemEnabled()) {
|
} else if(Controller::isUserSystemEnabled()) {
|
||||||
$this->ticket->unreadStaff = true;
|
$this->ticket->unreadStaff = true;
|
||||||
$comment->authorUser = Controller::getLoggedUser();
|
$comment->authorUser = $this->user;
|
||||||
|
} else {
|
||||||
|
$this->ticket->unreadStaff = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ticket->addEvent($comment);
|
$this->ticket->addEvent($comment);
|
||||||
|
@ -115,10 +115,16 @@ class CreateController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::createLog('CREATE_TICKET', $this->ticketNumber);
|
|
||||||
Response::respondSuccess([
|
Response::respondSuccess([
|
||||||
'ticketNumber' => $this->ticketNumber
|
'ticketNumber' => $this->ticketNumber
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
|
||||||
|
$session = Session::getInstance();
|
||||||
|
$session->createTicketSession($this->ticketNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::createLog('CREATE_TICKET', $this->ticketNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function storeTicket() {
|
private function storeTicket() {
|
||||||
|
@ -4,6 +4,7 @@ $userControllers->setGroupPath('/user');
|
|||||||
|
|
||||||
$userControllers->addController(new LoginController);
|
$userControllers->addController(new LoginController);
|
||||||
$userControllers->addController(new SignUpController);
|
$userControllers->addController(new SignUpController);
|
||||||
|
$userControllers->addController(new InviteUserController);
|
||||||
$userControllers->addController(new LogoutController);
|
$userControllers->addController(new LogoutController);
|
||||||
$userControllers->addController(new CheckSessionController);
|
$userControllers->addController(new CheckSessionController);
|
||||||
$userControllers->addController(new SendRecoverPasswordController);
|
$userControllers->addController(new SendRecoverPasswordController);
|
||||||
|
140
server/controllers/user/invite.php
Executable file
140
server/controllers/user/invite.php
Executable file
@ -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() {
|
public function handler() {
|
||||||
if(!Controller::isUserSystemEnabled()) {
|
|
||||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->requestData();
|
$this->requestData();
|
||||||
$this->changePassword();
|
$this->changePassword();
|
||||||
}
|
}
|
||||||
@ -69,16 +65,26 @@ class RecoverPasswordController extends Controller {
|
|||||||
$this->token = Controller::request('token');
|
$this->token = Controller::request('token');
|
||||||
$this->password = Controller::request('password');
|
$this->password = Controller::request('password');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function changePassword() {
|
public function changePassword() {
|
||||||
$recoverPassword = RecoverPassword::getDataStore($this->token, 'token');
|
$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) {
|
if($recoverPassword->staff) {
|
||||||
$this->user = Staff::getDataStore($this->email, 'email');
|
$this->user = Staff::getDataStore($this->email, 'email');
|
||||||
} else {
|
} else {
|
||||||
$this->user = User::getDataStore($this->email, 'email');
|
$this->user = User::getDataStore($this->email, 'email');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$recoverPassword->isNull() && !$this->user->isNull()) {
|
if($this->user->isNull()) throw new RequestException(ERRORS::NO_PERMISSION);
|
||||||
|
|
||||||
$recoverPassword->delete();
|
$recoverPassword->delete();
|
||||||
|
|
||||||
$this->user->setProperties([
|
$this->user->setProperties([
|
||||||
@ -89,10 +95,8 @@ class RecoverPasswordController extends Controller {
|
|||||||
|
|
||||||
$this->sendMail();
|
$this->sendMail();
|
||||||
Response::respondSuccess(['staff' => $recoverPassword->staff]);
|
Response::respondSuccess(['staff' => $recoverPassword->staff]);
|
||||||
} else {
|
|
||||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMail() {
|
public function sendMail() {
|
||||||
$mailSender = MailSender::getInstance();
|
$mailSender = MailSender::getInstance();
|
||||||
|
|
||||||
|
@ -49,11 +49,12 @@ class SendRecoverPasswordController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function handler() {
|
public function handler() {
|
||||||
if(!Controller::isUserSystemEnabled()) {
|
$this->staff = Controller::request('staff');
|
||||||
|
|
||||||
|
if(!Controller::isUserSystemEnabled() && !$this->staff) {
|
||||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->staff = Controller::request('staff');
|
|
||||||
$email = Controller::request('email');
|
$email = Controller::request('email');
|
||||||
|
|
||||||
if($this->staff){
|
if($this->staff){
|
||||||
|
@ -18,7 +18,7 @@ DataValidator::with('CustomValidations', true);
|
|||||||
* @apiParam {String} name The name of the new user.
|
* @apiParam {String} name The name of the new user.
|
||||||
* @apiParam {String} email The email of the new user.
|
* @apiParam {String} email The email of the new user.
|
||||||
* @apiParam {String} password The password 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.
|
* @apiParam {String} customfield_ Custom field values for this user.
|
||||||
*
|
*
|
||||||
* @apiUse INVALID_NAME
|
* @apiUse INVALID_NAME
|
||||||
|
@ -25,6 +25,12 @@ class MailTexts {
|
|||||||
'Hi, {{name}}. You have requested to recover your password.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Access system changed - OpenSupports',
|
'Access system changed - OpenSupports',
|
||||||
'Access system changed',
|
'Access system changed',
|
||||||
@ -85,6 +91,12 @@ class MailTexts {
|
|||||||
'喂 {{name}}。 您已要求恢复密码。',
|
'喂 {{name}}。 您已要求恢复密码。',
|
||||||
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}} 或单击下面的按钮.',
|
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}} 或单击下面的按钮.',
|
||||||
],
|
],
|
||||||
|
'USER_INVITE' => [
|
||||||
|
'您已受邀 - OpenSupports',
|
||||||
|
'您已受邀',
|
||||||
|
'你好, {{name}}. 邀请您加入我们的支持中心.',
|
||||||
|
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}}&invited=true 或单击下面的按钮来设置密码.'
|
||||||
|
],
|
||||||
'USER_SYSTEM_DISABLED' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'访问系统更改 - OpenSupports',
|
'访问系统更改 - OpenSupports',
|
||||||
'访问系统更改',
|
'访问系统更改',
|
||||||
@ -145,6 +157,12 @@ class MailTexts {
|
|||||||
'Hallo, {{name}}. Sie haben aufgefordert, Ihr Passwort wiederherzustellen.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Access system changed - OpenSupports',
|
'Access system changed - OpenSupports',
|
||||||
'Zugriffssystem geändert',
|
'Zugriffssystem geändert',
|
||||||
@ -205,6 +223,12 @@ class MailTexts {
|
|||||||
'Hola, {{name}}. Has requerido recuperar tu contraseña.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Sistema de acceso cambiado - OpenSupports',
|
'Sistema de acceso cambiado - OpenSupports',
|
||||||
'Sistema de acceso cambiado',
|
'Sistema de acceso cambiado',
|
||||||
@ -265,6 +289,12 @@ class MailTexts {
|
|||||||
'Salut, {{name}}. Vous avez demandé à récupérer votre mot de passe.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Système d\'accès modifié - OpenSupports',
|
'Système d\'accès modifié - OpenSupports',
|
||||||
'Système d\'accès modifié',
|
'Système d\'accès modifié',
|
||||||
@ -325,6 +355,12 @@ class MailTexts {
|
|||||||
'नमस्ते {{name}}. आपने अपना पासवर्ड पुनर्प्राप्त करने का अनुरोध किया है',
|
'नमस्ते {{name}}. आपने अपना पासवर्ड पुनर्प्राप्त करने का अनुरोध किया है',
|
||||||
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}} या नीचे दिए गए बटन पर क्लिक करें.',
|
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}} या नीचे दिए गए बटन पर क्लिक करें.',
|
||||||
],
|
],
|
||||||
|
'USER_INVITE' => [
|
||||||
|
'आपको आमंत्रित किया गया है - OpenSupports',
|
||||||
|
'आपको आमंत्रित किया गया है',
|
||||||
|
'नमस्ते, {{name}}. आपको हमारे सहायता केंद्र से जुड़ने के लिए आमंत्रित किया गया है.',
|
||||||
|
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}}&invited=true या अपना पासवर्ड सेट करने के लिए नीचे दिए गए बटन पर क्लिक करें.'
|
||||||
|
],
|
||||||
'USER_SYSTEM_DISABLED' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'sistem akses berubah - OpenSupports',
|
'sistem akses berubah - OpenSupports',
|
||||||
'एक्सेस सिस्टम बदल गया',
|
'एक्सेस सिस्टम बदल गया',
|
||||||
@ -385,6 +421,12 @@ class MailTexts {
|
|||||||
'Ciao, {{name}}. Hai richiesto di recuperare la tua password.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Il sistema di accesso è cambiato - OpenSupports',
|
'Il sistema di accesso è cambiato - OpenSupports',
|
||||||
'Modifica sistema di accesso',
|
'Modifica sistema di accesso',
|
||||||
@ -445,6 +487,12 @@ class MailTexts {
|
|||||||
'こんにちは、{{name}}。 パスワードの回復を要求しました。',
|
'こんにちは、{{name}}。 パスワードの回復を要求しました。',
|
||||||
'でこのコードを使用 {{url}}/recover-password?email={{to}}&token={{token}} 下のボタンをクリックしてください.',
|
'でこのコードを使用 {{url}}/recover-password?email={{to}}&token={{token}} 下のボタンをクリックしてください.',
|
||||||
],
|
],
|
||||||
|
'USER_INVITE' => [
|
||||||
|
'招待されました - OpenSupports',
|
||||||
|
'招待されました',
|
||||||
|
'こんにちは, {{name}}. サポートセンターに招待されました.',
|
||||||
|
'このコードを {{url}}/recover-password?email={{to}}&token={{token}}&invited=true または、下のボタンをクリックしてパスワードを設定します.'
|
||||||
|
],
|
||||||
'USER_SYSTEM_DISABLED' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'アクセスシステムが変更されました - OpenSupports',
|
'アクセスシステムが変更されました - OpenSupports',
|
||||||
'アクセスシステムが変更されました',
|
'アクセスシステムが変更されました',
|
||||||
@ -505,6 +553,12 @@ class MailTexts {
|
|||||||
'Olá, {{name}}. Você solicitou a recuperação da sua senha.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Sistema de acesso alterado - OpenSupports',
|
'Sistema de acesso alterado - OpenSupports',
|
||||||
'Sistema de acesso alterado',
|
'Sistema de acesso alterado',
|
||||||
@ -565,6 +619,12 @@ class MailTexts {
|
|||||||
'Здравствуй, {{name}}. Вы запросили восстановить пароль.',
|
'Здравствуй, {{name}}. Вы запросили восстановить пароль.',
|
||||||
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}} или нажмите кнопку ниже.',
|
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}} или нажмите кнопку ниже.',
|
||||||
],
|
],
|
||||||
|
'USER_INVITE' => [
|
||||||
|
'Вы были приглашены - OpenSupports',
|
||||||
|
'Вы были приглашены',
|
||||||
|
'Здравствуй, {{name}}. Вас пригласили присоединиться к нашему центру поддержки.',
|
||||||
|
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}}&invited=true или нажмите кнопку ниже, чтобы установить пароль.'
|
||||||
|
],
|
||||||
'USER_SYSTEM_DISABLED' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Система доступа изменена - OpenSupports',
|
'Система доступа изменена - OpenSupports',
|
||||||
'Система доступа изменена',
|
'Система доступа изменена',
|
||||||
@ -625,6 +685,12 @@ class MailTexts {
|
|||||||
'Merhaba, {{name}}. Şifrenizi geri yüklemenizi istediniz.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Erişim sistemi değiştirildi - OpenSupports',
|
'Erişim sistemi değiştirildi - OpenSupports',
|
||||||
'Erişim sistemi değiştirildi',
|
'Erişim sistemi değiştirildi',
|
||||||
@ -685,6 +751,12 @@ class MailTexts {
|
|||||||
'Olá, {{name}}. Você solicitou a recuperação da sua senha.',
|
'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.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Sistema de acesso alterado - OpenSupports',
|
'Sistema de acesso alterado - OpenSupports',
|
||||||
'Sistema de acesso alterado',
|
'Sistema de acesso alterado',
|
||||||
@ -745,6 +817,12 @@ class MailTexts {
|
|||||||
'Γεια σου, {{name}}. Ζητήσατε να ανακτήσετε τον κωδικό πρόσβασής σας.',
|
'Γεια σου, {{name}}. Ζητήσατε να ανακτήσετε τον κωδικό πρόσβασής σας.',
|
||||||
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}} / recover-password? Email = {{to}} & token = {{token}} ή κάντε κλικ στο παρακάτω κουμπί.',
|
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}} / recover-password? Email = {{to}} & token = {{token}} ή κάντε κλικ στο παρακάτω κουμπί.',
|
||||||
],
|
],
|
||||||
|
'USER_INVITE' => [
|
||||||
|
'Έχετε προσκληθεί - OpenSupports',
|
||||||
|
'Έχετε προσκληθεί',
|
||||||
|
'Γεια σου, {{name}}. Έχετε προσκληθεί να συμμετάσχετε στο κέντρο υποστήριξής μας.',
|
||||||
|
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ή κάντε κλικ στο παρακάτω κουμπί για να ρυθμίσετε τον κωδικό πρόσβασής σας.'
|
||||||
|
],
|
||||||
'USER_SYSTEM_DISABLED' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Το σύστημα πρόσβασης άλλαξε - OpenSupports',
|
'Το σύστημα πρόσβασης άλλαξε - OpenSupports',
|
||||||
'Το σύστημα πρόσβασης άλλαξε',
|
'Το σύστημα πρόσβασης άλλαξε',
|
||||||
@ -805,6 +883,12 @@ class MailTexts {
|
|||||||
'Hallo, {{name}}. U heeft een verzoek gedaan om uw wachtwoord te resetten.',
|
'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.'
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Toegangssysteem gewijzigd - OpenSupports',
|
'Toegangssysteem gewijzigd - OpenSupports',
|
||||||
'Toegang tot incidenten is gewijzigd',
|
'Toegang tot incidenten is gewijzigd',
|
||||||
@ -865,6 +949,12 @@ class MailTexts {
|
|||||||
'Hej, {{name}}. Zażądałeś odzyskania hasła.',
|
'Hej, {{name}}. Zażądałeś odzyskania hasła.',
|
||||||
'Użyj tego linka {{url}}/recover-password?email={{to}}&token={{token}} lub kliknij przycisk poniżej.',
|
'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' => [
|
'USER_SYSTEM_DISABLED' => [
|
||||||
'Zmieniono dostęp do systemu - OpenSupports',
|
'Zmieniono dostęp do systemu - OpenSupports',
|
||||||
'Zmieniono dostęp do systemu',
|
'Zmieniono dostęp do systemu',
|
||||||
|
384
server/data/mail-templates/user-invite.html
Executable file
384
server/data/mail-templates/user-invite.html
Executable file
@ -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',
|
'authorUser',
|
||||||
'authorStaff',
|
'authorStaff',
|
||||||
'to',
|
'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) {
|
if($author === null) {
|
||||||
$author = Controller::getLoggedUser();
|
$author = Controller::getLoggedUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!$author->isNull()) {
|
||||||
|
$authorName = $author->name;
|
||||||
|
}
|
||||||
|
|
||||||
$log = new Log();
|
$log = new Log();
|
||||||
|
|
||||||
$log->setProperties(array(
|
$log->setProperties(array(
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'to' => $to,
|
'to' => $to,
|
||||||
'date' => Date::getCurrentDate()
|
'date' => Date::getCurrentDate(),
|
||||||
|
'authorName' => $authorName
|
||||||
));
|
));
|
||||||
|
|
||||||
if($author instanceof User) {
|
if($author instanceof User) {
|
||||||
@ -55,8 +70,8 @@ class Log extends DataStore {
|
|||||||
'type' => $this->type,
|
'type' => $this->type,
|
||||||
'to' => $this->to,
|
'to' => $this->to,
|
||||||
'author' => [
|
'author' => [
|
||||||
'name' => $author->name,
|
'name' => $this->authorName,
|
||||||
'id' => $author->id,
|
'id' => ($author && !$author->isNull()) ? $author->id : null,
|
||||||
'staff' => $author instanceof Staff
|
'staff' => $author instanceof Staff
|
||||||
],
|
],
|
||||||
'date' => $this->date
|
'date' => $this->date
|
||||||
|
@ -19,6 +19,7 @@ class MailTemplate extends DataStore {
|
|||||||
const USER_SIGNUP = 'USER_SIGNUP';
|
const USER_SIGNUP = 'USER_SIGNUP';
|
||||||
const USER_PASSWORD = 'USER_PASSWORD';
|
const USER_PASSWORD = 'USER_PASSWORD';
|
||||||
const PASSWORD_FORGOT = 'PASSWORD_FORGOT';
|
const PASSWORD_FORGOT = 'PASSWORD_FORGOT';
|
||||||
|
const USER_INVITE = 'USER_INVITE';
|
||||||
const USER_SYSTEM_DISABLED = 'USER_SYSTEM_DISABLED';
|
const USER_SYSTEM_DISABLED = 'USER_SYSTEM_DISABLED';
|
||||||
const USER_SYSTEM_ENABLED = 'USER_SYSTEM_ENABLED';
|
const USER_SYSTEM_ENABLED = 'USER_SYSTEM_ENABLED';
|
||||||
const TICKET_CREATED = 'TICKET_CREATED';
|
const TICKET_CREATED = 'TICKET_CREATED';
|
||||||
@ -32,6 +33,7 @@ class MailTemplate extends DataStore {
|
|||||||
'USER_PASSWORD' => 'data/mail-templates/user-edit-password.html',
|
'USER_PASSWORD' => 'data/mail-templates/user-edit-password.html',
|
||||||
'USER_EMAIL' => 'data/mail-templates/user-edit-email.html',
|
'USER_EMAIL' => 'data/mail-templates/user-edit-email.html',
|
||||||
'PASSWORD_FORGOT' => 'data/mail-templates/user-password-forgot.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_DISABLED' => 'data/mail-templates/user-system-disabled.html',
|
||||||
'USER_SYSTEM_ENABLED' => 'data/mail-templates/user-system-enabled.html',
|
'USER_SYSTEM_ENABLED' => 'data/mail-templates/user-system-enabled.html',
|
||||||
'TICKET_CREATED' => 'data/mail-templates/ticket-created.html',
|
'TICKET_CREATED' => 'data/mail-templates/ticket-created.html',
|
||||||
|
@ -83,15 +83,16 @@ class Ticketevent extends DataStore {
|
|||||||
|
|
||||||
public function toArray() {
|
public function toArray() {
|
||||||
$user = ($this->authorStaff) ? $this->authorStaff : $this->authorUser;
|
$user = ($this->authorStaff) ? $this->authorStaff : $this->authorUser;
|
||||||
|
$author = $this->ticket->authorToArray();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'type' => $this->type,
|
'type' => $this->type,
|
||||||
'ticketNumber' => $this->ticket->ticketNumber,
|
'ticketNumber' => $this->ticket->ticketNumber,
|
||||||
'author' => [
|
'author' => [
|
||||||
'name' => $user ? $user->name : null,
|
'name' => $user ? $user->name : $author['name'],
|
||||||
'staff' => $user instanceOf Staff,
|
'staff' => $user instanceOf Staff,
|
||||||
'id' => $user ? $user->id : null,
|
'id' => $user ? $user->id : null,
|
||||||
'customfields' => $user->xownCustomfieldvalueList ? $user->xownCustomfieldvalueList->toArray() : [],
|
'customfields' => ($user && $user->xownCustomfieldvalueList) ? $user->xownCustomfieldvalueList->toArray() : [],
|
||||||
],
|
],
|
||||||
'edited' => $this->editedContent
|
'edited' => $this->editedContent
|
||||||
];
|
];
|
||||||
|
@ -33,7 +33,7 @@ require './ticket/change-department.rb'
|
|||||||
require './ticket/close.rb'
|
require './ticket/close.rb'
|
||||||
require './ticket/re-open.rb'
|
require './ticket/re-open.rb'
|
||||||
require './ticket/delete.rb'
|
require './ticket/delete.rb'
|
||||||
require './staff/add.rb'
|
require './staff/invite.rb'
|
||||||
require './staff/get.rb'
|
require './staff/get.rb'
|
||||||
require './staff/edit.rb'
|
require './staff/edit.rb'
|
||||||
require './staff/delete.rb'
|
require './staff/delete.rb'
|
||||||
|
@ -16,25 +16,33 @@ class Scripts
|
|||||||
})
|
})
|
||||||
end
|
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', {
|
departments = request('/system/get-settings', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token
|
csrf_token: $csrf_token
|
||||||
})['data']['departments']
|
})['data']['departments']
|
||||||
departments = departments.collect { |x| x.id }
|
departments = departments.collect { |x| x.id }
|
||||||
|
|
||||||
response = request('/staff/add', {
|
response = request('/staff/invite', {
|
||||||
:name => name,
|
:name => name,
|
||||||
:email => email,
|
:email => email,
|
||||||
:password => password,
|
|
||||||
:level => level,
|
:level => level,
|
||||||
:departments => departments.to_string
|
: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'
|
if response['status'] === 'fail'
|
||||||
raise response['message']
|
raise response['message']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.deleteStaff(staffId)
|
def self.deleteStaff(staffId)
|
||||||
response = request('/staff/delete', {
|
response = request('/staff/delete', {
|
||||||
staffId: staffId,
|
staffId: staffId,
|
||||||
@ -106,6 +114,7 @@ class Scripts
|
|||||||
description: description
|
description: description
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.createTag(name, color)
|
def self.createTag(name, color)
|
||||||
request('/ticket/create-tag', {
|
request('/ticket/create-tag', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
@ -114,6 +123,7 @@ class Scripts
|
|||||||
color: color
|
color: color
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.assignTicket(ticketnumber)
|
def self.assignTicket(ticketnumber)
|
||||||
request('/staff/assign-ticket', {
|
request('/staff/assign-ticket', {
|
||||||
ticketNumber: ticketnumber,
|
ticketNumber: ticketnumber,
|
||||||
@ -121,6 +131,7 @@ class Scripts
|
|||||||
csrf_token: $csrf_token
|
csrf_token: $csrf_token
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.commentTicket(ticketnumber,content)
|
def self.commentTicket(ticketnumber,content)
|
||||||
request('/ticket/comment', {
|
request('/ticket/comment', {
|
||||||
content: content,
|
content: content,
|
||||||
|
@ -32,17 +32,24 @@ describe'/staff/edit' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'should edit own data staff' do
|
it 'should edit own data staff' do
|
||||||
request('/staff/add', {
|
request('/staff/invite', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
name: 'Arya Stark',
|
name: 'Arya Stark',
|
||||||
password: 'starkpassword',
|
|
||||||
email: 'arya@opensupports.com',
|
email: 'arya@opensupports.com',
|
||||||
level: 1,
|
level: 1,
|
||||||
profilePic: '',
|
profilePic: '',
|
||||||
departments: '[1]'
|
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')
|
row = $database.getRow('staff', 'arya@opensupports.com', 'email')
|
||||||
|
|
||||||
result = request('/staff/edit', {
|
result = request('/staff/edit', {
|
||||||
|
@ -1,21 +1,28 @@
|
|||||||
describe'/staff/add' do
|
describe'/staff/invite' do
|
||||||
request('/user/logout')
|
request('/user/logout')
|
||||||
Scripts.login($staff[:email], $staff[:password], true)
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
it 'should add staff member' do
|
it 'should add staff member' do
|
||||||
result= request('/staff/add', {
|
|
||||||
|
result = request('/staff/invite', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
name: 'Tyrion Lannister',
|
name: 'Tyrion Lannister',
|
||||||
email: 'tyrion@opensupports.com',
|
email: 'tyrion@opensupports.com',
|
||||||
password: 'testpassword',
|
|
||||||
level: 2,
|
level: 2,
|
||||||
profilePic: '',
|
profilePic: '',
|
||||||
departments: '[1]'
|
departments: '[1]'
|
||||||
})
|
})
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
(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 = $database.getRow('staff', result['data']['id'], 'id')
|
||||||
|
|
||||||
(row['name']).should.equal('Tyrion Lannister')
|
(row['name']).should.equal('Tyrion Lannister')
|
||||||
@ -27,16 +34,15 @@ describe'/staff/add' do
|
|||||||
(row['owners']).should.equal('4')
|
(row['owners']).should.equal('4')
|
||||||
|
|
||||||
lastLog = $database.getLastRow('log')
|
lastLog = $database.getLastRow('log')
|
||||||
(lastLog['type']).should.equal('ADD_STAFF')
|
(lastLog['type']).should.equal('INVITE')
|
||||||
|
|
||||||
end
|
end
|
||||||
it 'should fail if staff member is alrady a staff' do
|
it 'should fail if staff member is alrady a staff' do
|
||||||
result= request('/staff/add', {
|
result = request('/staff/invite', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
name: 'Tyrion Lannister',
|
name: 'Tyrion Lannister',
|
||||||
email: 'tyrion@opensupports.com',
|
email: 'tyrion@opensupports.com',
|
||||||
password: 'testpassword',
|
|
||||||
level: 2,
|
level: 2,
|
||||||
profilePic: '',
|
profilePic: '',
|
||||||
departments: '[1]'
|
departments: '[1]'
|
@ -65,6 +65,35 @@ describe'system/disable-user-system' do
|
|||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
end
|
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
|
it 'should be able to assign and respond tickets' do
|
||||||
Scripts.login($staff[:email], $staff[:password], true);
|
Scripts.login($staff[:email], $staff[:password], true);
|
||||||
ticket = $database.getLastRow('ticket');
|
ticket = $database.getLastRow('ticket');
|
||||||
@ -84,6 +113,26 @@ describe'system/disable-user-system' do
|
|||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
end
|
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
|
it 'should be be able to create a ticket as an admin' do
|
||||||
result = request('/ticket/create', {
|
result = request('/ticket/create', {
|
||||||
title: 'created by staff with user system disabled',
|
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')
|
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_DISABLED')
|
||||||
end
|
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
|
it 'should enable the user system' do
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
result = request('/system/enable-user-system', {
|
result = request('/system/enable-user-system', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
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= $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
|
end
|
||||||
|
|
||||||
it 'should not enable the user system' do
|
it 'should not enable the user system' do
|
||||||
@ -140,6 +217,5 @@ describe'system/disable-user-system' do
|
|||||||
|
|
||||||
(result['status']).should.equal('fail')
|
(result['status']).should.equal('fail')
|
||||||
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_ENABLED')
|
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_ENABLED')
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -182,18 +182,28 @@ describe '/ticket/comment/' do
|
|||||||
|
|
||||||
request('/user/logout')
|
request('/user/logout')
|
||||||
Scripts.login($staff[:email], $staff[:password], true)
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
request('/staff/add', {
|
|
||||||
|
result = request('/staff/invite', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
name: 'Jorah mormont',
|
name: 'Jorah mormont',
|
||||||
email: 'jorah@opensupports.com',
|
email: 'jorah@opensupports.com',
|
||||||
password: 'testpassword',
|
|
||||||
level: 2,
|
level: 2,
|
||||||
profilePic: '',
|
profilePic: '',
|
||||||
departments: '[1]'
|
departments: '[1]'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
(result['status'].should.equal('success'))
|
||||||
|
|
||||||
request('/user/logout')
|
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)
|
Scripts.login('jorah@opensupports.com', 'testpassword', true)
|
||||||
result = request('/ticket/comment', {
|
result = request('/ticket/comment', {
|
||||||
content: 'some comment content',
|
content: 'some comment content',
|
||||||
|
@ -6,17 +6,24 @@ describe '/ticket/delete' do
|
|||||||
Scripts.createTicket('ticket_to_delete')
|
Scripts.createTicket('ticket_to_delete')
|
||||||
ticket = $database.getRow('ticket', 'ticket_to_delete', 'title')
|
ticket = $database.getRow('ticket', 'ticket_to_delete', 'title')
|
||||||
|
|
||||||
request('/staff/add', {
|
request('/staff/invite', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
name: 'Ned Stark',
|
name: 'Ned Stark',
|
||||||
password: 'headless',
|
|
||||||
email: 'ned@opensupports.com',
|
email: 'ned@opensupports.com',
|
||||||
level: 3,
|
level: 3,
|
||||||
profilePic: '',
|
profilePic: '',
|
||||||
departments: '[1]'
|
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')
|
request('/user/logout')
|
||||||
Scripts.login('ned@opensupports.com', 'headless', true)
|
Scripts.login('ned@opensupports.com', 'headless', true)
|
||||||
|
|
||||||
@ -80,16 +87,24 @@ describe '/ticket/delete' do
|
|||||||
|
|
||||||
ticket = $database.getRow('ticket', 'ticket_to_delete_4', 'title');
|
ticket = $database.getRow('ticket', 'ticket_to_delete_4', 'title');
|
||||||
|
|
||||||
request('/staff/add', {
|
request('/staff/invite', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
name: 'Joan Chris',
|
name: 'Joan Chris',
|
||||||
password: 'theyaregonnafireme',
|
|
||||||
email: 'uselessstaff@opensupports.com',
|
email: 'uselessstaff@opensupports.com',
|
||||||
level: 2,
|
level: 2,
|
||||||
profilePic: '',
|
profilePic: '',
|
||||||
departments: '[1]'
|
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')
|
request('/user/logout')
|
||||||
|
|
||||||
Scripts.login('uselessstaff@opensupports.com', 'theyaregonnafireme',true)
|
Scripts.login('uselessstaff@opensupports.com', 'theyaregonnafireme',true)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user