Merge branch 'master' into create-ticket-api-key
This commit is contained in:
commit
4d18bc9aa6
|
@ -23,6 +23,7 @@ class ActivityRow extends React.Component {
|
|||
|
||||
'EDIT_SETTINGS',
|
||||
'SIGNUP',
|
||||
'INVITE',
|
||||
'ADD_TOPIC',
|
||||
'ADD_ARTICLE',
|
||||
'DELETE_TOPIC',
|
||||
|
@ -65,9 +66,7 @@ class ActivityRow extends React.Component {
|
|||
<div className="activity-row">
|
||||
<Icon {...this.getIconProps()} className="activity-row__icon"/>
|
||||
<span>
|
||||
<Link className="activity-row__name-link" to={this.getNameLinkDestination()}>
|
||||
{this.props.author.name}
|
||||
</Link>
|
||||
{this.renderAuthorName()}
|
||||
</span>
|
||||
<span className="activity-row__message"> {i18n('ACTIVITY_' + this.props.type)} </span>
|
||||
{_.includes(ticketRelatedTypes, this.props.type) ? this.renderTicketNumber() : this.props.to}
|
||||
|
@ -76,6 +75,18 @@ class ActivityRow extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderAuthorName() {
|
||||
let name = this.props.author.name;
|
||||
|
||||
if (this.props.author.id) {
|
||||
name = <Link className="activity-row__name-link" to={this.getNameLinkDestination()}>
|
||||
{this.props.author.name}
|
||||
</Link>;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
renderTicketNumber() {
|
||||
let ticketNumber = (this.props.mode === 'staff') ? this.props.ticketNumber : this.props.to;
|
||||
|
||||
|
@ -106,6 +117,7 @@ class ActivityRow extends React.Component {
|
|||
|
||||
'EDIT_SETTINGS': 'wrench',
|
||||
'SIGNUP': 'user-plus',
|
||||
'INVITE': 'user-plus',
|
||||
'ADD_TOPIC': 'book',
|
||||
'ADD_ARTICLE': 'book',
|
||||
'DELETE_TOPIC': 'book',
|
||||
|
|
|
@ -274,4 +274,4 @@ export default connect((store) => {
|
|||
return {
|
||||
tags: store.config['tags']
|
||||
};
|
||||
})(TicketList);
|
||||
})(TicketList);
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import API from 'lib-app/api-call';
|
||||
import i18n from 'lib-app/i18n';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import TicketList from 'app-components/ticket-list';
|
||||
import Message from 'core-components/message';
|
||||
|
||||
class TicketQueryList extends React.Component {
|
||||
|
||||
state = {
|
||||
tickets: [],
|
||||
page: 1,
|
||||
pages: 0,
|
||||
error: null,
|
||||
loading: true
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.getTickets();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.customList.title !== prevProps.customList.title) {
|
||||
this.getTickets();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{(this.state.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getTicketListProps()}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getTickets() {
|
||||
this.setState({
|
||||
loading:true
|
||||
})
|
||||
API.call({
|
||||
path: '/ticket/search',
|
||||
data: {
|
||||
page : this.state.page,
|
||||
...this.props.customList.filters
|
||||
}
|
||||
}).then((result) => {
|
||||
this.setState({
|
||||
tickets: result.data.tickets,
|
||||
page: result.data.page,
|
||||
pages: result.data.pages,
|
||||
error: null,
|
||||
loading: false
|
||||
})
|
||||
}).catch((result) => this.setState({
|
||||
loading: false,
|
||||
error: result.message
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
onPageChange(event) {
|
||||
this.setState({page: event.target.value}, () => this.getTickets());
|
||||
}
|
||||
|
||||
getTicketListProps () {
|
||||
const {page,pages,loading,tickets} = this.state;
|
||||
return {
|
||||
userId: this.props.userId,
|
||||
ticketPath: '/admin/panel/tickets/view-ticket/',
|
||||
tickets,
|
||||
page,
|
||||
pages,
|
||||
loading,
|
||||
type: 'secondary',
|
||||
showDepartmentDropdown: false,
|
||||
closedTicketsShown: false,
|
||||
onPageChange:this.onPageChange.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect((store) => {
|
||||
return {
|
||||
userId: store.session.userId
|
||||
};
|
||||
})(TicketQueryList);
|
|
@ -33,6 +33,7 @@ import AdminPanelMyAccount from 'app/admin/panel/dashboard/admin-panel-my-accoun
|
|||
import AdminPanelMyTickets from 'app/admin/panel/tickets/admin-panel-my-tickets';
|
||||
import AdminPanelNewTickets from 'app/admin/panel/tickets/admin-panel-new-tickets';
|
||||
import AdminPanelAllTickets from 'app/admin/panel/tickets/admin-panel-all-tickets';
|
||||
import AdminPanelSearchTickets from 'app/admin/panel/tickets/admin-panel-search-tickets';
|
||||
import AdminPanelViewTicket from 'app/admin/panel/tickets/admin-panel-view-ticket';
|
||||
import AdminPanelCustomResponses from 'app/admin/panel/tickets/admin-panel-custom-responses';
|
||||
|
||||
|
@ -113,6 +114,7 @@ export default (
|
|||
<Route path="my-tickets" component={AdminPanelMyTickets} />
|
||||
<Route path="new-tickets" component={AdminPanelNewTickets} />
|
||||
<Route path="all-tickets" component={AdminPanelAllTickets} />
|
||||
<Route path="search-tickets" component={AdminPanelSearchTickets} />
|
||||
<Route path="custom-responses" component={AdminPanelCustomResponses} />
|
||||
<Route path="view-ticket/:ticketNumber" component={AdminPanelViewTicket} />
|
||||
</Route>
|
||||
|
|
|
@ -76,7 +76,7 @@ class AdminPanelMenu extends React.Component {
|
|||
|
||||
getGroupItemIndex() {
|
||||
const group = this.getRoutes()[this.getGroupIndex()];
|
||||
const pathname = this.props.location.pathname;
|
||||
const pathname = this.props.location.pathname + this.props.location.search;
|
||||
|
||||
return _.findIndex(group.items, {path: pathname});
|
||||
}
|
||||
|
@ -90,19 +90,35 @@ class AdminPanelMenu extends React.Component {
|
|||
return (groupIndex === -1) ? 0 : groupIndex;
|
||||
}
|
||||
|
||||
getCustomlists() {
|
||||
if(window.customTicketList){
|
||||
return window.customTicketList.map((item, index) => {
|
||||
return {
|
||||
name: item.title,
|
||||
path: '/admin/panel/tickets/search-tickets?custom=' + index,
|
||||
level: 1
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getRoutes() {
|
||||
return this.getItemsByFilteredByLevel([
|
||||
const customLists = this.getCustomlists();
|
||||
|
||||
return this.getItemsByFilteredByLevel(_.without([
|
||||
{
|
||||
groupName: i18n('DASHBOARD'),
|
||||
path: '/admin/panel',
|
||||
icon: 'tachometer',
|
||||
level: 1,
|
||||
items: this.getItemsByFilteredByLevel([
|
||||
{
|
||||
/*{
|
||||
name: i18n('STATISTICS'),
|
||||
path: '/admin/panel/stats',
|
||||
level: 1
|
||||
},
|
||||
},*/
|
||||
{
|
||||
name: i18n('LAST_ACTIVITY'),
|
||||
path: '/admin/panel/activity',
|
||||
|
@ -135,10 +151,11 @@ class AdminPanelMenu extends React.Component {
|
|||
name: i18n('CUSTOM_RESPONSES'),
|
||||
path: '/admin/panel/tickets/custom-responses',
|
||||
level: 2
|
||||
}
|
||||
},
|
||||
...customLists
|
||||
])
|
||||
},
|
||||
{
|
||||
this.props.config['user-system-enabled'] ? {
|
||||
groupName: i18n('USERS'),
|
||||
path: '/admin/panel/users',
|
||||
icon: 'user',
|
||||
|
@ -160,7 +177,7 @@ class AdminPanelMenu extends React.Component {
|
|||
level: 1
|
||||
}
|
||||
])
|
||||
},
|
||||
} : null,
|
||||
{
|
||||
groupName: i18n('ARTICLES'),
|
||||
path: '/admin/panel/articles',
|
||||
|
@ -175,7 +192,6 @@ class AdminPanelMenu extends React.Component {
|
|||
])
|
||||
},
|
||||
{
|
||||
|
||||
groupName: i18n('STAFF'),
|
||||
path: '/admin/panel/staff',
|
||||
icon: 'users',
|
||||
|
@ -222,7 +238,7 @@ class AdminPanelMenu extends React.Component {
|
|||
}
|
||||
])
|
||||
}
|
||||
]);
|
||||
], null));
|
||||
}
|
||||
|
||||
getItemsByFilteredByLevel(items) {
|
||||
|
@ -232,6 +248,7 @@ class AdminPanelMenu extends React.Component {
|
|||
|
||||
export default connect((store) => {
|
||||
return {
|
||||
level: store.session.userLevel
|
||||
level: store.session.userLevel,
|
||||
config: store.config
|
||||
};
|
||||
})(AdminPanelMenu);
|
||||
|
|
|
@ -67,7 +67,7 @@ class AddStaffModal extends React.Component {
|
|||
return SessionStore.getDepartments().map(department => {
|
||||
if(department.private*1){
|
||||
return <spam>{department.name} <Icon name='user-secret'/> </spam>
|
||||
}else {
|
||||
} else {
|
||||
return department.name;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import SessionStore from 'lib-app/session-store';
|
|||
import PeopleList from 'app-components/people-list';
|
||||
import ModalContainer from 'app-components/modal-container';
|
||||
|
||||
import AddStaffModal from 'app/admin/panel/staff/add-staff-modal';
|
||||
import InviteStaffModal from 'app/admin/panel/staff/invite-staff-modal';
|
||||
|
||||
import Header from 'core-components/header';
|
||||
import DropDown from 'core-components/drop-down';
|
||||
|
@ -47,8 +47,8 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
<Header title={i18n('STAFF_MEMBERS')} description={i18n('STAFF_MEMBERS_DESCRIPTION')} />
|
||||
<div className="admin-panel-staff-members__wrapper">
|
||||
<DepartmentDropdown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
||||
<Button onClick={this.onAddNewStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||
<Icon name="user-plus" className=""/> {i18n('ADD_NEW_STAFF')}
|
||||
<Button onClick={this.onInviteStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||
<Icon name="user-plus" className=""/> {i18n('INVITE_STAFF')}
|
||||
</Button>
|
||||
</div>
|
||||
{(this.props.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />}
|
||||
|
@ -56,8 +56,8 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
onAddNewStaff() {
|
||||
ModalContainer.openModal(<AddStaffModal onSuccess={this.retrieveStaffMembers.bind(this)} />);
|
||||
onInviteStaff() {
|
||||
ModalContainer.openModal(<InviteStaffModal onSuccess={this.retrieveStaffMembers.bind(this)} />);
|
||||
}
|
||||
|
||||
getDepartmentDropdownProps() {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
import SessionStore from 'lib-app/session-store';
|
||||
|
||||
import Header from 'core-components/header'
|
||||
import Form from 'core-components/form';
|
||||
import FormField from 'core-components/form-field';
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
import Button from 'core-components/button';
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class InviteStaffModal extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
closeModal: React.PropTypes.func
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
onSuccess: React.PropTypes.func
|
||||
};
|
||||
|
||||
state = {
|
||||
loading: false,
|
||||
errors: {},
|
||||
error: null
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="invite-staff-modal">
|
||||
<Header title={i18n('INVITE_STAFF')} description={i18n('INVITE_STAFF_DESCRIPTION')} />
|
||||
<Form onSubmit={this.onSubmit.bind(this)} errors={this.getErrors()} onValidateErrors={errors => this.setState({errors})} loading={this.state.loading}>
|
||||
<div className="row">
|
||||
<div className="col-md-7">
|
||||
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} validation="NAME" required />
|
||||
<FormField name="email" label={i18n('EMAIL')} fieldProps={{size: 'large'}} validation="EMAIL" required />
|
||||
<div className="invite-staff-modal__level-selector">
|
||||
<FormField name="level" label={i18n('LEVEL')} field="select" fieldProps={{
|
||||
items: [{content: i18n('LEVEL_1')}, {content: i18n('LEVEL_2')}, {content: i18n('LEVEL_3')}],
|
||||
size: 'large'
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<div className="invite-staff-modal__departments">
|
||||
<div className="invite-staff-modal__departments-title">{i18n('Departments')}</div>
|
||||
<FormField name="departments" field="checkbox-group" fieldProps={{items: this.getDepartments()}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SubmitButton type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
<Button type="clean" onClick={this.onCancelClick.bind(this)}>
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getDepartments() {
|
||||
return SessionStore.getDepartments().map(department => {
|
||||
if(department.private*1){
|
||||
return <span>{department.name} <Icon name='user-secret'/> </span>
|
||||
} else {
|
||||
return department.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(form) {
|
||||
let departments = _.filter(SessionStore.getDepartments(), (department, index) => {
|
||||
return _.includes(form.departments, index);
|
||||
}).map(department => department.id);
|
||||
|
||||
this.setState({loading: true});
|
||||
|
||||
API.call({
|
||||
path: '/staff/invite',
|
||||
data: {
|
||||
name: form.name,
|
||||
email: form.email,
|
||||
level: form.level + 1,
|
||||
departments: JSON.stringify(departments)
|
||||
}
|
||||
}).then(() => {
|
||||
this.context.closeModal();
|
||||
|
||||
if(this.props.onSuccess) {
|
||||
this.props.onSuccess();
|
||||
}
|
||||
}).catch((result) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
error: result.message
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onCancelClick(event) {
|
||||
event.preventDefault();
|
||||
this.context.closeModal();
|
||||
}
|
||||
|
||||
getErrors() {
|
||||
let errors = _.extend({}, this.state.errors);
|
||||
|
||||
if (this.state.error === 'ALREADY_A_STAFF') {
|
||||
errors.email = i18n('EMAIL_EXISTS');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
export default InviteStaffModal;
|
|
@ -0,0 +1,23 @@
|
|||
@import "../../../../scss/vars";
|
||||
|
||||
.invite-staff-modal {
|
||||
width: 700px;
|
||||
|
||||
&__level-selector {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__departments {
|
||||
@include scrollbars();
|
||||
|
||||
border: 1px solid $grey;
|
||||
padding: 20px;
|
||||
height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__departments-title {
|
||||
font-size: $font-size--md;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
|
||||
import TicketQueryList from 'app-components/ticket-query-list';
|
||||
|
||||
import Header from 'core-components/header';
|
||||
import Message from 'core-components/message';
|
||||
|
||||
class AdminPanelSearchTickets extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="admin-panel-all-tickets">
|
||||
<Header title={i18n('ALL_TICKETS')} description={i18n('SEARCH_TICKETS_DESCRIPTION')} />
|
||||
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketQueryList customList ={this.getFilters()}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getFilters() {
|
||||
let customList = (window.customTicketList && window.customTicketList[this.props.location.query.custom*1]) ? window.customTicketList[this.props.location.query.custom*1] : null
|
||||
return {
|
||||
...customList
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default connect((store) => {
|
||||
return {
|
||||
error: store.adminData.allTicketsError
|
||||
};
|
||||
})(AdminPanelSearchTickets);
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
|
@ -12,10 +13,9 @@ import Button from 'core-components/button';
|
|||
import Message from 'core-components/message';
|
||||
import Icon from 'core-components/icon';
|
||||
import ModalContainer from 'app-components/modal-container';
|
||||
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
|
||||
import InviteUserWidget from 'app/admin/panel/users/invite-user-widget';
|
||||
|
||||
class AdminPanelListUsers extends React.Component {
|
||||
|
||||
state = {
|
||||
loading: true,
|
||||
users: [],
|
||||
|
@ -39,11 +39,19 @@ class AdminPanelListUsers extends React.Component {
|
|||
return (
|
||||
<div className="admin-panel-list-users">
|
||||
<Header title={i18n('LIST_USERS')} description={i18n('LIST_USERS_DESCRIPTION')} />
|
||||
{(this.state.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_USERS')}</Message> : this.renderTableAndInviteButton()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTableAndInviteButton() {
|
||||
return (
|
||||
<div>
|
||||
<SearchBox className="admin-panel-list-users__search-box" placeholder={i18n('SEARCH_USERS')} onSearch={this.onSearch.bind(this)} />
|
||||
{(this.state.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_USERS')}</Message> : <Table {...this.getTableProps()}/>}
|
||||
<Table {...this.getTableProps()}/>
|
||||
<div style={{textAlign: 'right', marginTop: 10}}>
|
||||
<Button onClick={this.onCreateUser.bind(this)} type="secondary" size="medium">
|
||||
<Icon size="sm" name="plus"/> {i18n('ADD_USER')}
|
||||
<Button onClick={this.onInviteUser.bind(this)} type="secondary" size="medium">
|
||||
<Icon size="sm" name="plus"/> {i18n('INVITE_USER')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,17 +175,17 @@ class AdminPanelListUsers extends React.Component {
|
|||
}).catch(this.onUsersRejected.bind(this)).then(this.onUsersRetrieved.bind(this));
|
||||
}
|
||||
|
||||
onCreateUser(user) {
|
||||
onInviteUser(user) {
|
||||
ModalContainer.openModal(
|
||||
<div className="admin-panel-list-users__add-user-form">
|
||||
<MainSignUpWidget onSuccess={this.onCreateUserSuccess.bind(this)} />
|
||||
<div className="admin-panel-list-users__invite-user-form">
|
||||
<InviteUserWidget onSuccess={this.onInviteUserSuccess.bind(this)} />
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
onCreateUserSuccess() {
|
||||
onInviteUserSuccess() {
|
||||
ModalContainer.closeModal();
|
||||
}
|
||||
|
||||
|
@ -201,4 +209,8 @@ class AdminPanelListUsers extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default AdminPanelListUsers;
|
||||
export default connect((store) => {
|
||||
return {
|
||||
config: store.config
|
||||
};
|
||||
})(AdminPanelListUsers);
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
|
||||
import Captcha from 'app/main/captcha';
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
import Message from 'core-components/message';
|
||||
import Form from 'core-components/form';
|
||||
import FormField from 'core-components/form-field';
|
||||
import Widget from 'core-components/widget';
|
||||
import Header from 'core-components/header';
|
||||
|
||||
class InviteUserWidget extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
onSuccess: React.PropTypes.func,
|
||||
className: React.PropTypes.string
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
email: null,
|
||||
customFields: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
API.call({
|
||||
path: '/system/get-custom-fields',
|
||||
data: {}
|
||||
})
|
||||
.then(result => this.setState({customFields: result.data}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Widget className={this.getClass()}>
|
||||
<Header title={i18n('INVITE_USER')} description={i18n('INVITE_USER_VIEW_DESCRIPTION')} />
|
||||
<Form {...this.getFormProps()}>
|
||||
<div className="invite-user-widget__inputs">
|
||||
<FormField {...this.getInputProps()} label={i18n('FULL_NAME')} name="name" validation="NAME" required/>
|
||||
<FormField {...this.getInputProps()} label={i18n('EMAIL')} name="email" validation="EMAIL" required/>
|
||||
{this.state.customFields.map(this.renderCustomField.bind(this))}
|
||||
</div>
|
||||
<div className="invite-user-widget__captcha">
|
||||
<Captcha ref="captcha"/>
|
||||
</div>
|
||||
<SubmitButton type="primary">{i18n('INVITE_USER')}</SubmitButton>
|
||||
</Form>
|
||||
|
||||
{this.renderMessage()}
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
renderCustomField(customField, key) {
|
||||
if(customField.type === 'text') {
|
||||
return (
|
||||
<FormField {...this.getInputProps()}
|
||||
name={`customfield_${customField.name}`}
|
||||
key={key}
|
||||
label={customField.name}
|
||||
infoMessage={customField.description}
|
||||
field="input"/>
|
||||
);
|
||||
} else {
|
||||
const items = customField.options.map(option => ({content: option.name, value: option.name}));
|
||||
|
||||
return (
|
||||
<FormField
|
||||
name={`customfield_${customField.name}`}
|
||||
key={key}
|
||||
label={customField.name}
|
||||
infoMessage={customField.description}
|
||||
field="select"
|
||||
fieldProps={{size:'medium', items}}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderMessage() {
|
||||
switch (this.state.message) {
|
||||
case 'success':
|
||||
return <Message type="success">{i18n('INVITE_USER_SUCCESS')}</Message>;
|
||||
case 'fail':
|
||||
return <Message type="error">{i18n('EMAIL_EXISTS')}</Message>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getClass() {
|
||||
let classes = {
|
||||
'invite-user-widget': true,
|
||||
[this.props.className]: this.props.className
|
||||
};
|
||||
return classNames(classes);
|
||||
}
|
||||
|
||||
getFormProps() {
|
||||
return {
|
||||
loading: this.state.loading,
|
||||
className: 'invite-user-widget__form',
|
||||
onSubmit: this.onInviteUserFormSubmit.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
getInputProps(password) {
|
||||
return {
|
||||
className: 'invite-user-widget__input',
|
||||
fieldProps: {
|
||||
size: 'medium',
|
||||
password: password
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onInviteUserFormSubmit(formState) {
|
||||
const captcha = this.refs.captcha.getWrappedInstance();
|
||||
|
||||
if (!captcha.getValue()) {
|
||||
captcha.focus();
|
||||
} else {
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
|
||||
const form = _.clone(formState);
|
||||
|
||||
this.state.customFields.forEach(customField => {
|
||||
if(customField.type === 'select') {
|
||||
form[`customfield_${customField.name}`] = customField.options[form[`customfield_${customField.name}`]].name;
|
||||
}
|
||||
})
|
||||
|
||||
API.call({
|
||||
path: '/user/invite',
|
||||
data: _.extend({captcha: captcha.getValue()}, form)
|
||||
}).then(this.onInviteUserSuccess.bind(this)).catch(this.onInviteUserFail.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
onInviteUserSuccess() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: 'success'
|
||||
});
|
||||
}
|
||||
|
||||
onInviteUserFail() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: 'fail'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default InviteUserWidget;
|
|
@ -0,0 +1,19 @@
|
|||
.invite-user-widget {
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
|
||||
&__form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__inputs {
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&__captcha {
|
||||
margin: 10px auto 20px;
|
||||
height: 78px;
|
||||
width: 304px;
|
||||
}
|
||||
}
|
|
@ -146,7 +146,7 @@ class CreateTicketForm extends React.Component {
|
|||
message: 'success'
|
||||
}, () => {
|
||||
if(this.props.onSuccess) {
|
||||
this.props.onSuccess();
|
||||
this.props.onSuccess(result, email);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class DashboardCreateTicketPage extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
onCreateTicketSuccess() {
|
||||
onCreateTicketSuccess(result, email) {
|
||||
if((this.props.location.pathname !== '/create-ticket')) {
|
||||
setTimeout(() => {history.push('/dashboard')}, 2000);
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,7 @@ import _ from 'lodash';
|
|||
|
||||
import API from 'lib-app/api-call';
|
||||
import i18n from 'lib-app/i18n';
|
||||
import { getCustomFieldParamName } from 'lib-core/APIUtils';
|
||||
|
||||
import SessionActions from 'actions/session-actions';
|
||||
import AreYouSure from 'app-components/are-you-sure';
|
||||
|
@ -42,15 +43,6 @@ class DashboardEditProfilePage extends React.Component {
|
|||
return (
|
||||
<div className="edit-profile-page">
|
||||
<Header title={i18n('EDIT_PROFILE')} description={i18n('EDIT_PROFILE_VIEW_DESCRIPTION')} />
|
||||
<div className="edit-profile-page__title">{i18n('ADDITIONAL_FIELDS')}</div>
|
||||
<Form loading={this.state.loadingCustomFields} values={this.state.customFieldsFrom} onChange={form => this.setState({customFieldsFrom: form})} onSubmit={this.onCustomFieldsSubmit.bind(this)}>
|
||||
<div className="edit-profile-page__custom-fields">
|
||||
{this.state.customFields.map(this.renderCustomField.bind(this))}
|
||||
</div>
|
||||
<div className="row">
|
||||
<SubmitButton>{i18n('SAVE')}</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
<div className="edit-profile-page__title">{i18n('EDIT_EMAIL')}</div>
|
||||
<Form loading={this.state.loadingEmail} onSubmit={this.onSubmitEditEmail.bind(this)}>
|
||||
<FormField name="newEmail" label={i18n('NEW_EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required/>
|
||||
|
@ -65,6 +57,23 @@ class DashboardEditProfilePage extends React.Component {
|
|||
<SubmitButton>{i18n('CHANGE_PASSWORD')}</SubmitButton>
|
||||
{this.renderMessagePass()}
|
||||
</Form>
|
||||
{this.state.customFields.length ? this.renderCustomFields() : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCustomFields() {
|
||||
return (
|
||||
<div>
|
||||
<div className="edit-profile-page__title">{i18n('ADDITIONAL_FIELDS')}</div>
|
||||
<Form loading={this.state.loadingCustomFields} values={this.state.customFieldsFrom} onChange={form => this.setState({customFieldsFrom: form})} onSubmit={this.onCustomFieldsSubmit.bind(this)}>
|
||||
<div className="edit-profile-page__custom-fields">
|
||||
{this.state.customFields.map(this.renderCustomField.bind(this))}
|
||||
</div>
|
||||
<div className="row">
|
||||
<SubmitButton>{i18n('SAVE')}</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -116,9 +125,9 @@ class DashboardEditProfilePage extends React.Component {
|
|||
|
||||
customFields.forEach(customField => {
|
||||
if(customField.type === 'select') {
|
||||
parsedFrom[`customfield_${customField.name}`] = customField.options[form[customField.name]].name;
|
||||
parsedFrom[getCustomFieldParamName(customField.name)] = customField.options[form[customField.name]].name;
|
||||
} else {
|
||||
parsedFrom[`customfield_${customField.name}`] = form[customField.name];
|
||||
parsedFrom[getCustomFieldParamName(customField.name)] = form[customField.name];
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class MainRecoverPasswordPage extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="main-recover-password-page">
|
||||
<Widget title={i18n('RECOVER_PASSWORD')} className="col-md-4 col-md-offset-4">
|
||||
<Widget title={this.props.location.query.invited ? i18n('SET_UP_PASSWORD') : i18n('RECOVER_PASSWORD')} className="col-md-4 col-md-offset-4">
|
||||
<Form className="recover-password__form" onSubmit={this.onRecoverPasswordSubmit.bind(this)} loading={this.state.loading}>
|
||||
<div className="recover-password__inputs">
|
||||
<FormField placeholder={i18n('NEW_PASSWORD')} name="password" className="recover-password__input" validation="PASSWORD" fieldProps={{password: true}} required/>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Widget from 'core-components/widget';
|
||||
import MainSignUpWidget from 'app/main/main-signup/main-signup-widget';
|
||||
|
||||
class MainSignUpPage extends React.Component {
|
||||
|
@ -9,70 +7,10 @@ class MainSignUpPage extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="main-signup-page">
|
||||
<MainSignUpWidget {...this.props} className="col-md-6 col-md-offset-3" />
|
||||
<MainSignUpWidget className="col-md-6 col-md-offset-3" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderMessage() {
|
||||
switch (this.state.message) {
|
||||
case 'success':
|
||||
return <Message type="success">{i18n('SIGNUP_SUCCESS')}</Message>;
|
||||
case 'fail':
|
||||
return <Message type="error">{i18n('EMAIL_EXISTS')}</Message>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getFormProps() {
|
||||
return {
|
||||
loading: this.state.loading,
|
||||
className: 'signup-widget__form',
|
||||
onSubmit: this.onSignupFormSubmit.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
getInputProps(password) {
|
||||
return {
|
||||
className: 'signup-widget__input',
|
||||
fieldProps: {
|
||||
size: 'medium',
|
||||
password: password
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onSignupFormSubmit(formState) {
|
||||
const captcha = this.refs.captcha.getWrappedInstance();
|
||||
|
||||
if (!captcha.getValue()) {
|
||||
captcha.focus();
|
||||
} else {
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
|
||||
API.call({
|
||||
path: '/user/signup',
|
||||
data: _.extend({captcha: captcha.getValue()}, formState)
|
||||
}).then(this.onSignupSuccess.bind(this)).catch(this.onSignupFail.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
onSignupSuccess() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: 'success'
|
||||
});
|
||||
}
|
||||
|
||||
onSignupFail() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: 'fail'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default MainSignUpPage;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
import history from 'lib-app/history';
|
||||
|
||||
import Captcha from 'app/main/captcha';
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
|
@ -17,7 +17,6 @@ import Header from 'core-components/header';
|
|||
class MainSignUpWidget extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
onSuccess: React.PropTypes.func,
|
||||
className: React.PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -27,7 +26,7 @@ class MainSignUpWidget extends React.Component {
|
|||
this.state = {
|
||||
loading: false,
|
||||
email: null,
|
||||
customFields: []
|
||||
customFields: null
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -40,6 +39,7 @@ class MainSignUpWidget extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if(!this.state.customFields) return null;
|
||||
return (
|
||||
<Widget className={this.getClass()}>
|
||||
<Header title={i18n('SIGN_UP')} description={i18n('SIGN_UP_VIEW_DESCRIPTION')} />
|
||||
|
@ -153,6 +153,8 @@ class MainSignUpWidget extends React.Component {
|
|||
this.setState({
|
||||
loading: false,
|
||||
message: 'success'
|
||||
}, () => {
|
||||
setTimeout(() => {history.push('/check-ticket')}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -140,4 +140,4 @@ class Menu extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default Menu;
|
||||
export default Menu;
|
||||
|
|
|
@ -201,4 +201,4 @@ $transition: background-color 0.3s ease, color 0.3s ease;
|
|||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,4 +98,4 @@ class Pagination extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default Pagination;
|
||||
export default Pagination;
|
||||
|
|
|
@ -55,7 +55,7 @@ class Table extends React.Component {
|
|||
'table__header-column': true,
|
||||
[header.className]: (header.className)
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<th className={classNames(classes)} key={header.key}>
|
||||
{header.value}
|
||||
|
@ -97,7 +97,7 @@ class Table extends React.Component {
|
|||
};
|
||||
|
||||
return (
|
||||
|
||||
|
||||
<td className={classNames(classes)} key={key}>{row[key]}</td>
|
||||
);
|
||||
}
|
||||
|
@ -139,15 +139,15 @@ class Table extends React.Component {
|
|||
this.props.onPageChange({target: {value: index}});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getRowClass(row) {
|
||||
let classes = {
|
||||
'table__row': true,
|
||||
'table__row-highlighted': row.highlighted
|
||||
};
|
||||
|
||||
|
||||
classes[row.className] = (row.className);
|
||||
|
||||
|
||||
return classNames(classes);
|
||||
}
|
||||
|
||||
|
@ -167,4 +167,4 @@ class Table extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default Table;
|
||||
export default Table;
|
||||
|
|
|
@ -300,7 +300,7 @@ export default {
|
|||
'DELETE_USER_DESCRIPTION': 'O usuário não será capaz de entrar no sistema e todos os seus chamados serão apagados. Além disso, o e-mail não poderá mais ser usado.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Ao excluir o tópico, todos os artigos dele serão apagados.',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Aqui você pode alterar o nome, o ícone ea cor do ícone do tópico.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {categoria}.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Esta é uma lista de artigos que inclui informações sobre nossos serviços.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Aqui você pode adicionar um tópico que funciona como uma categoria para artigos.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.',
|
||||
|
|
|
@ -272,7 +272,7 @@ export default {
|
|||
'INSTALLATION_COMPLETED': 'Installation abgeschlossen.',
|
||||
'INSTALLATION_COMPLETED_DESCRIPTION': 'Die Installation von OpenSupports ist abgeschlossen. Umleitung zum Admin-Panel...',
|
||||
|
||||
'STEP_TITLE': 'Schritt {aktuell} von {total} - {title}',
|
||||
'STEP_TITLE': 'Schritt {current} von {total} - {title}',
|
||||
'STEP_1_DESCRIPTION': 'Wählen Sie Ihre bevorzugte Sprache für den Installationsassistenten aus.',
|
||||
'STEP_2_DESCRIPTION': 'Hier sind die Voraussetzungen für das Ausführen von OpenSupports aufgelistet. Bitte stellen Sie sicher, dass alle Anforderungen erfüllt sind.',
|
||||
'STEP_3_DESCRIPTION': 'Bitte füllen Sie die MySQL-Datenbankkonfiguration aus.',
|
||||
|
|
|
@ -11,6 +11,7 @@ export default {
|
|||
'SIGN_UP': 'Sign up',
|
||||
'FORGOT_PASSWORD': 'Forgot your password?',
|
||||
'RECOVER_PASSWORD': 'Recover Password',
|
||||
'SET_UP_PASSWORD': 'Set up your password',
|
||||
'RECOVER_SENT': 'An email with recover instructions has been sent.',
|
||||
'NEW_EMAIL': 'New email',
|
||||
'FULL_NAME': 'Full name',
|
||||
|
@ -93,6 +94,7 @@ export default {
|
|||
'CHANGE_EMAIL': 'Change email',
|
||||
'CHANGE_PASSWORD': 'Change password',
|
||||
'NAME': 'Name',
|
||||
'SEARCH': 'Search',
|
||||
'SIGNUP_DATE': 'Sign up date',
|
||||
'SEARCH_USERS': 'Search users...',
|
||||
'SEARCH_EMAIL': 'Search email...',
|
||||
|
@ -193,6 +195,8 @@ export default {
|
|||
'NEVER': 'Never',
|
||||
'HIMSELF': 'himself',
|
||||
'ADD_USER': 'Add user',
|
||||
'INVITE_USER': 'Invite user',
|
||||
'INVITE_STAFF': 'Invite staff',
|
||||
'UPLOAD_FILE': 'Upload file',
|
||||
'PRIVATE': 'Private',
|
||||
'ENABLE_USER': 'Enable User',
|
||||
|
@ -201,7 +205,7 @@ export default {
|
|||
'IMAGE_HEADER_URL': 'Image header URL',
|
||||
'IMAGE_HEADER_DESCRIPTION': 'Image that will be used as header of the email',
|
||||
'EMAIL_SETTINGS': 'Email Settings',
|
||||
'ADDITIONAL_FIELDS': 'Additonal Fields',
|
||||
'ADDITIONAL_FIELDS': 'Edit additonal fields',
|
||||
'NEW_CUSTOM_FIELD': 'New Custom field',
|
||||
'TYPE': 'Type',
|
||||
'SELECT_INPUT': 'Select input',
|
||||
|
@ -232,6 +236,7 @@ export default {
|
|||
|
||||
'ACTIVITY_EDIT_SETTINGS': 'edited settings',
|
||||
'ACTIVITY_SIGNUP': 'signed up',
|
||||
'ACTIVITY_INVITE': 'invited user',
|
||||
'ACTIVITY_ADD_TOPIC': 'added topic',
|
||||
'ACTIVITY_ADD_ARTICLE': 'added article',
|
||||
'ACTIVITY_DELETE_TOPIC': 'deleted topic',
|
||||
|
@ -293,6 +298,7 @@ export default {
|
|||
'MY_TICKETS_DESCRIPTION': 'Here you can view the tickets you are responsible for.',
|
||||
'NEW_TICKETS_DESCRIPTION': 'Here you can view all the new tickets that are not assigned by anyone.',
|
||||
'ALL_TICKETS_DESCRIPTION': 'Here you can view the tickets of the departments you are assigned.',
|
||||
'SEARCH_TICKETS_DESCRIPTION': 'Here you can search tickets by specific filters',
|
||||
'TICKET_VIEW_DESCRIPTION': 'This ticket has been sent by a customer. Here you can respond or assign the ticket',
|
||||
'BAN_USERS_DESCRIPTION': 'Here you can see a list of banned emails, you can un-ban them or add more emails to the list.',
|
||||
'LIST_USERS_DESCRIPTION': 'This is the list of users that are registered in this platform. You can search for someone in particular, delete it or ban it.',
|
||||
|
@ -322,7 +328,7 @@ export default {
|
|||
'REGISTRATION_ENABLED': 'Registration has been enabled',
|
||||
'ADD_API_KEY_DESCRIPTION': 'Insert the name and a registration api key will be generated.',
|
||||
'SIGN_UP_VIEW_DESCRIPTION': 'Here you can create an account for our support center. It is required to send tickets and see documentation.',
|
||||
'EDIT_PROFILE_VIEW_DESCRIPTION': 'Here you can edit your user by changing your email or your password.',
|
||||
'EDIT_PROFILE_VIEW_DESCRIPTION': 'Here you can edit your user preferences.',
|
||||
'ENABLE_USER_SYSTEM_DESCRIPTION': 'Enable/disable the use of an user system. If you disable it, all users will be deleted but the tickets will be kept. If you enable it, the users of existent tickets will be created.',
|
||||
'CSV_DESCRIPTION': 'The CSV file must have 3 columns: email, password, name. There is no limit in row count. It will be created one user per row in the file.',
|
||||
'SMTP_SERVER_DESCRIPTION': 'The configuration of the SMTP server allows the application to send mails. If you do not configure it, no emails will be sent by OpenSupports.',
|
||||
|
@ -336,6 +342,8 @@ export default {
|
|||
'IMAP_POLLING_DESCRIPTION': 'Inbox checking will not be done automatically by OpenSupports. You have to make POST requests periodically to this url to process the emails: {url}',
|
||||
'NEW_CUSTOM_FIELD_DESCRIPTION': 'Here you can create a custom field for an user, it can be a blank text box or a fixed set of options.',
|
||||
'CUSTOM_FIELDS_DESCRIPTION': 'Custom fields are defined additional fields the users are able to fill to provide more information about them.',
|
||||
'INVITE_USER_VIEW_DESCRIPTION': 'Here you can invite an user to join our support system, he will just need to provide his password to create a new user.',
|
||||
'INVITE_STAFF_DESCRIPTION': 'Here you can invite staff members to your teams.',
|
||||
|
||||
//ERRORS
|
||||
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
||||
|
@ -372,6 +380,7 @@ export default {
|
|||
|
||||
//MESSAGES
|
||||
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',
|
||||
'INVITE_USER_SUCCESS': 'You have invited a new user successfully in our support system',
|
||||
'TICKET_SENT': 'Ticket has been created successfully.',
|
||||
'VALID_RECOVER': 'Password recovered successfully',
|
||||
'EMAIL_EXISTS': 'Email already exists',
|
||||
|
|
|
@ -206,7 +206,7 @@ export default {
|
|||
'TYPE': 'Tipo',
|
||||
'SELECT_INPUT': 'Seleccionar entrada',
|
||||
'TEXT_INPUT': 'Entrada de texto',
|
||||
'OPTION': 'Opción {índice}',
|
||||
'OPTION': 'Opción {index}',
|
||||
'OPTIONS': 'Opciones',
|
||||
'FIELD_DESCRIPTION': 'Descripción del campo (opcional)',
|
||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puedes agregar una nueva etiqueta personalizada',
|
||||
|
|
|
@ -62,7 +62,7 @@ export default {
|
|||
'HIGH': 'Haute',
|
||||
'MEDIUM': 'Moyenne',
|
||||
'LOW': 'Faible',
|
||||
'TITLE': 'Titre',
|
||||
'TITLE': 'Objet',
|
||||
'CONTENT': 'Contenu',
|
||||
'SAVE': 'Enregistrer',
|
||||
'DISCARD_CHANGES': 'Annuler les modifications',
|
||||
|
@ -343,7 +343,7 @@ export default {
|
|||
'ERROR_EMPTY': 'Valeur invalide',
|
||||
'ERROR_PASSWORD': 'Mot de passe incorrect',
|
||||
'ERROR_NAME': 'Nom incorrect',
|
||||
'ERROR_TITLE': 'Titre incorrect',
|
||||
'ERROR_TITLE': 'Objet incorrect',
|
||||
'ERROR_EMAIL': 'Email invalide',
|
||||
'ERROR_CONTENT_SHORT': 'Contenu trop court',
|
||||
'PASSWORD_NOT_MATCH': 'Le mot de passe ne correspond pas',
|
||||
|
|
|
@ -300,7 +300,7 @@
|
|||
'DELETE_USER_DESCRIPTION': 'Ο χρήστης δεν θα μπορέσει να συνδεθεί με τη γήρανση και όλα τα εισιτήρια του θα διαγραφούν. Επίσης, το ηλεκτρονικό ταχυδρομείο δεν μπορεί πλέον να χρησιμοποιηθεί.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Διαγράφοντας το θέμα, όλα τα άρθρα σε αυτό θα διαγραφούν.',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να αλλάξετε το όνομα, το εικονίδιο και το χρώμα του εικονιδίου του θέματος.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {κατηγορία}.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Αυτή είναι μια λίστα με άρθρα που περιλαμβάνουν πληροφορίες σχετικά με τις υπηρεσίες μας.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα θέμα που λειτουργεί ως κατηγορία για άρθρα.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Πρόκειται να διαγράψετε αυτό το άρθρο για πάντα.',
|
||||
|
|
|
@ -206,7 +206,7 @@ export default {
|
|||
'TYPE': 'genere',
|
||||
'SELECT_INPUT': 'Seleziona input',
|
||||
'TEXT_INPUT': 'L\'immissione di testo',
|
||||
'OPTION': 'Opzione {indice}',
|
||||
'OPTION': 'Opzione {index}',
|
||||
'OPTIONS': 'Opzioni',
|
||||
'FIELD_DESCRIPTION': 'Descrizione del campo (facoltativo)',
|
||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'qui puoi aggiungere un nuovo tag personalizzato',
|
||||
|
|
|
@ -300,7 +300,7 @@ export default {
|
|||
'DELETE_USER_DESCRIPTION': 'O usuário não será capaz de entrar no envelhecimento e todos os seus bilhetes serão apagados. Além disso, o e-mail não pode mais ser usado.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Ao excluir o tópico, todos os artigos dele serão apagados.',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Aqui você pode alterar o nome, o ícone ea cor do ícone do tópico.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {categoria}.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Esta é uma lista de artigos que inclui informações sobre nossos serviços.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Aqui você pode adicionar um tópico que funciona como uma categoria para artigos.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.',
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="/config.js"></script>
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=String.prototype.startsWith,Array.from,Array.prototype.fill,Array.prototype.keys,Array.prototype.find,Array.prototype.findIndex,Array.prototype.includes,String.prototype.repeat,Number.isInteger,Promise&flags=gated"></script>
|
||||
</body>
|
||||
|
|
|
@ -44,4 +44,8 @@ const APIUtils = {
|
|||
}
|
||||
};
|
||||
|
||||
export const getCustomFieldParamName = function (customFieldName) {
|
||||
return `customfield_${customFieldName}`.replace(/ /g,'_');
|
||||
}
|
||||
|
||||
export default APIUtils;
|
||||
|
|
|
@ -9,7 +9,7 @@ $systemControllerGroup->addController(new GetTicketStaffController);
|
|||
$systemControllerGroup->addController(new GetNewTicketsStaffController);
|
||||
$systemControllerGroup->addController(new GetAllTicketsStaffController);
|
||||
$systemControllerGroup->addController(new SearchTicketStaffController);
|
||||
$systemControllerGroup->addController(new AddStaffController);
|
||||
$systemControllerGroup->addController(new InviteStaffController);
|
||||
$systemControllerGroup->addController(new GetAllStaffController);
|
||||
$systemControllerGroup->addController(new DeleteStaffController);
|
||||
$systemControllerGroup->addController(new EditStaffController);
|
||||
|
|
|
@ -50,7 +50,7 @@ class GetAllTicketsStaffController extends Controller {
|
|||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Response::respondSuccess([
|
||||
'tickets' => $this->getTicketList()->toArray(true),
|
||||
'pages' => $this->getTotalPages()
|
||||
|
|
|
@ -11,7 +11,7 @@ use Respect\Validation\Validator as DataValidator;
|
|||
*
|
||||
* @apiDescription This path retrieves information about all the staff member.
|
||||
*
|
||||
* @apiPermission staff3
|
||||
* @apiPermission staff1
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
*
|
||||
|
|
|
@ -3,20 +3,19 @@ use Respect\Validation\Validator as DataValidator;
|
|||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
/**
|
||||
* @api {post} /staff/add Add staff
|
||||
* @api {post} /staff/invite Invite staff
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Add staff
|
||||
* @apiName Invite staff
|
||||
*
|
||||
* @apiGroup Staff
|
||||
*
|
||||
* @apiDescription This path adds a new staff member.
|
||||
* @apiDescription This path invites a new staff member.
|
||||
*
|
||||
* @apiPermission staff3
|
||||
*
|
||||
* @apiParam {String} name The name of the new staff member.
|
||||
* @apiParam {String} email The email of the new staff member.
|
||||
* @apiParam {String} password The password of the new staff member.
|
||||
* @apiParam {Number} level The level of the new staff member.
|
||||
* @apiParam {String} profilePic The profile pic of the new staff member.
|
||||
* @apiParam {Number[]} departments The departments that will have assigned the new staff member.
|
||||
|
@ -33,18 +32,16 @@ DataValidator::with('CustomValidations', true);
|
|||
*
|
||||
*/
|
||||
|
||||
class AddStaffController extends Controller {
|
||||
const PATH = '/add';
|
||||
class InviteStaffController extends Controller {
|
||||
const PATH = '/invite';
|
||||
const METHOD = 'POST';
|
||||
|
||||
private $name;
|
||||
private $email;
|
||||
private $password;
|
||||
private $profilePic;
|
||||
private $level;
|
||||
private $departments;
|
||||
|
||||
|
||||
public function validations() {
|
||||
return [
|
||||
'permission' => 'staff_3',
|
||||
|
@ -57,53 +54,55 @@ class AddStaffController extends Controller {
|
|||
'validation' => DataValidator::email(),
|
||||
'error' => ERRORS::INVALID_EMAIL
|
||||
],
|
||||
'password' => [
|
||||
'validation' => DataValidator::length(5, 200),
|
||||
'error' => ERRORS::INVALID_PASSWORD
|
||||
],
|
||||
'level' => [
|
||||
'validation' => DataValidator::between(1, 3, true),
|
||||
'error' => ERRORS::INVALID_LEVEL
|
||||
]
|
||||
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
$this->storeRequestData();
|
||||
|
||||
$staffRow = Staff::getDataStore($this->email, 'email');
|
||||
|
||||
if(!$staffRow->isNull()) throw new RequestException(ERRORS::ALREADY_A_STAFF);
|
||||
|
||||
$staff = new Staff();
|
||||
$staff->setProperties([
|
||||
'name'=> $this->name,
|
||||
'email' => $this->email,
|
||||
'password'=> Hashing::hashPassword(Hashing::generateRandomToken()),
|
||||
'profilePic' => $this->profilePic,
|
||||
'level' => $this->level,
|
||||
'sharedDepartmentList' => $this->getDepartmentList()
|
||||
]);
|
||||
|
||||
$staffRow = Staff::getDataStore($this->email,'email');
|
||||
$this->addOwner();
|
||||
|
||||
if($staffRow->isNull()) {
|
||||
$staff->setProperties([
|
||||
'name'=> $this->name,
|
||||
'email' => $this->email,
|
||||
'password'=> Hashing::hashPassword($this->password),
|
||||
'profilePic' => $this->profilePic,
|
||||
'level' => $this->level,
|
||||
'sharedDepartmentList' => $this->getDepartmentList()
|
||||
$this->token = Hashing::generateRandomToken();
|
||||
|
||||
]);
|
||||
|
||||
$this->addOwner();
|
||||
|
||||
Log::createLog('ADD_STAFF', $this->name);
|
||||
|
||||
Response::respondSuccess([
|
||||
'id' => $staff->store()
|
||||
]);
|
||||
return;
|
||||
}
|
||||
$recoverPassword = new RecoverPassword();
|
||||
$recoverPassword->setProperties(array(
|
||||
'email' => $this->email,
|
||||
'token' => $this->token,
|
||||
'staff' => true
|
||||
));
|
||||
$recoverPassword->store();
|
||||
|
||||
throw new RequestException(ERRORS::ALREADY_A_STAFF);
|
||||
$this->sendInvitationMail();
|
||||
|
||||
Response::respondSuccess([
|
||||
'id' => $staff->store()
|
||||
]);
|
||||
|
||||
Log::createLog('INVITE', $this->name);
|
||||
}
|
||||
|
||||
public function storeRequestData() {
|
||||
$this->name = Controller::request('name');
|
||||
$this->email = Controller::request('email');
|
||||
$this->password = Controller::request('password');
|
||||
$this->profilePic = Controller::request('profilePic');
|
||||
$this->level = Controller::request('level');
|
||||
$this->departments = Controller::request('departments');
|
||||
|
@ -120,6 +119,7 @@ class AddStaffController extends Controller {
|
|||
|
||||
return $listDepartments;
|
||||
}
|
||||
|
||||
public function addOwner() {
|
||||
$departmentIds = json_decode($this->departments);
|
||||
|
||||
|
@ -129,4 +129,17 @@ class AddStaffController extends Controller {
|
|||
$departmentRow->store();
|
||||
}
|
||||
}
|
||||
|
||||
public function sendInvitationMail() {
|
||||
$mailSender = MailSender::getInstance();
|
||||
|
||||
$mailSender->setTemplate(MailTemplate::USER_INVITE, [
|
||||
'to' => $this->email,
|
||||
'name' => $this->name,
|
||||
'url' => Setting::getSetting('url')->getValue(),
|
||||
'token' => $this->token
|
||||
]);
|
||||
|
||||
$mailSender->send();
|
||||
}
|
||||
}
|
|
@ -23,5 +23,6 @@ $ticketControllers->addController(new DeleteTagController);
|
|||
$ticketControllers->addController(new GetTagsController);
|
||||
$ticketControllers->addController(new AddTagController);
|
||||
$ticketControllers->addController(new RemoveTagController);
|
||||
$ticketControllers->addController(new SearchController);
|
||||
|
||||
$ticketControllers->finalize();
|
||||
|
|
|
@ -37,9 +37,10 @@ class CommentController extends Controller {
|
|||
|
||||
private $ticket;
|
||||
private $content;
|
||||
private $session;
|
||||
|
||||
public function validations() {
|
||||
$session = Session::getInstance();
|
||||
$this->session = Session::getInstance();
|
||||
|
||||
if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) {
|
||||
return [
|
||||
|
@ -64,11 +65,11 @@ class CommentController extends Controller {
|
|||
'error' => ERRORS::INVALID_CONTENT
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::equals($session->getTicketNumber()),
|
||||
'validation' => DataValidator::equals($this->session->getTicketNumber()),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
],
|
||||
'csrf_token' => [
|
||||
'validation' => DataValidator::equals($session->getToken()),
|
||||
'validation' => DataValidator::equals($this->session->getToken()),
|
||||
'error' => ERRORS::INVALID_TOKEN
|
||||
]
|
||||
]
|
||||
|
@ -79,28 +80,29 @@ class CommentController extends Controller {
|
|||
public function handler() {
|
||||
$this->requestData();
|
||||
$ticketAuthor = $this->ticket->authorToArray();
|
||||
$isAuthor = $this->ticket->isAuthor(Controller::getLoggedUser()) || Session::getInstance()->isTicketSession();
|
||||
$isOwner = $this->ticket->isOwner(Controller::getLoggedUser());
|
||||
$user = Controller::getLoggedUser();
|
||||
$isAuthor = $this->session->isTicketSession() || $this->ticket->isAuthor($this->user);
|
||||
$isOwner = $this->ticket->isOwner($this->user);
|
||||
$private = Controller::request('private');
|
||||
|
||||
if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() && !$isAuthor){
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
if(!$user->canManageTicket($this->ticket)) {
|
||||
if(!$this->session->isTicketSession() && !$this->user->canManageTicket($this->ticket)) {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
$this->storeComment();
|
||||
|
||||
if($isAuthor && $this->ticket->owner) {
|
||||
if(!$isAuthor && !$private) {
|
||||
$this->sendMail($ticketAuthor);
|
||||
}
|
||||
if($this->ticket->owner && !$isOwner) {
|
||||
$this->sendMail([
|
||||
'email' => $this->ticket->owner->email,
|
||||
'name' => $this->ticket->owner->name,
|
||||
'staff' => true
|
||||
]);
|
||||
} else if($isOwner) {
|
||||
!Controller::request('private') ? $this->sendMail($ticketAuthor) : null;
|
||||
}
|
||||
|
||||
Log::createLog('COMMENT', $this->ticket->ticketNumber);
|
||||
|
@ -112,6 +114,7 @@ class CommentController extends Controller {
|
|||
$ticketNumber = Controller::request('ticketNumber');
|
||||
$this->ticket = Ticket::getByTicketNumber($ticketNumber);
|
||||
$this->content = Controller::request('content', true);
|
||||
$this->user = Controller::getLoggedUser();
|
||||
}
|
||||
|
||||
private function storeComment() {
|
||||
|
@ -129,12 +132,14 @@ class CommentController extends Controller {
|
|||
));
|
||||
|
||||
if(Controller::isStaffLogged()) {
|
||||
$this->ticket->unread = !$this->ticket->isAuthor(Controller::getLoggedUser());
|
||||
$this->ticket->unreadStaff = !$this->ticket->isOwner(Controller::getLoggedUser());
|
||||
$comment->authorStaff = Controller::getLoggedUser();
|
||||
$this->ticket->unread = !$this->ticket->isAuthor($this->user);
|
||||
$this->ticket->unreadStaff = !$this->ticket->isOwner($this->user);
|
||||
$comment->authorStaff = $this->user;
|
||||
} else if(Controller::isUserSystemEnabled()) {
|
||||
$this->ticket->unreadStaff = true;
|
||||
$comment->authorUser = Controller::getLoggedUser();
|
||||
$comment->authorUser = $this->user;
|
||||
} else {
|
||||
$this->ticket->unreadStaff = true;
|
||||
}
|
||||
|
||||
$this->ticket->addEvent($comment);
|
||||
|
|
|
@ -115,10 +115,16 @@ class CreateController extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
Log::createLog('CREATE_TICKET', $this->ticketNumber);
|
||||
Response::respondSuccess([
|
||||
'ticketNumber' => $this->ticketNumber
|
||||
]);
|
||||
|
||||
if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
|
||||
$session = Session::getInstance();
|
||||
$session->createTicketSession($this->ticketNumber);
|
||||
}
|
||||
|
||||
Log::createLog('CREATE_TICKET', $this->ticketNumber);
|
||||
}
|
||||
|
||||
private function storeTicket() {
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
<?php
|
||||
use Respect\Validation\Validator as DataValidator;
|
||||
use RedBeanPHP\Facade as RedBean;
|
||||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
|
||||
/**
|
||||
* @api {post} /ticket/search Search tickets
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Search ticket
|
||||
*
|
||||
* @apiGroup Ticket
|
||||
*
|
||||
* @apiDescription This path search specific tickets.
|
||||
*
|
||||
* @apiPermission user
|
||||
*
|
||||
* @apiParam {Number[]} tags The ids of the tags to make a custom search.
|
||||
* @apiParam {Number} closed The status of closed 1 or 0 to make a custom search.
|
||||
* @apiParam {Number} unreadStaff The status of unread_staff 1 or 0 to make a custom search.
|
||||
* @apiParam {Number[]} priority The values of priority to make a custom search.
|
||||
* @apiParam {Number[]} dateRange The numbers of the range of date to make a custom search.
|
||||
* @apiParam {Number[]} departments The ids of the departments to make a custom search.
|
||||
* @apiParam {Object[]} authors A object {id,staff} with id and boolean to make a custom search.
|
||||
* @apiParam {Number} assigned The status of assigned 1 or 0 to make a custom search.
|
||||
* @apiParam {String} query A string to find into a ticket to make a custom search.
|
||||
* @apiParam {Number} page The number of the page of the tickets.
|
||||
* @apiParam {Object} orderBy A object {value, asc}with string and boolean to make a especific order of the search.
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_TAG_FILTER
|
||||
* @apiUse INVALID_CLOSED_FILTER
|
||||
* @apiUse INVALID_UNREAD_STAFF_FILTER
|
||||
* @apiUse INVALID_PRIORITY_FILTER
|
||||
* @apiUse INVALID_DATE_RANGE_FILTER
|
||||
* @apiUse INVALID_DEPARTMENT_FILTER
|
||||
* @apiUse INVALID_AUTHOR_FILTER
|
||||
* @apiUse INVALID_ASSIGNED_FILTER
|
||||
* @apiUse INVALID_ORDER_BY
|
||||
* @apiUse INVALID_PAGE
|
||||
*
|
||||
* @apiSuccess {Object} data Empty object
|
||||
*
|
||||
*
|
||||
|
||||
*/
|
||||
|
||||
class SearchController extends Controller {
|
||||
const PATH = '/search';
|
||||
const METHOD = 'POST';
|
||||
|
||||
public function validations() {
|
||||
return [
|
||||
'permission' => 'staff_1',
|
||||
'requestData' => [
|
||||
'page' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::numeric()->positive(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_PAGE
|
||||
],
|
||||
'tags' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validTagsId(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_TAG_FILTER
|
||||
],
|
||||
'closed' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::in(['0','1']),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_CLOSED_FILTER
|
||||
],
|
||||
'unreadStaff' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::in(['0','1']),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_UNREAD_STAFF_FILTER
|
||||
],
|
||||
'priority' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validPriorities(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_PRIORITY_FILTER
|
||||
],
|
||||
'dateRange' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validDateRange(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_DATE_RANGE_FILTER
|
||||
],
|
||||
'departments' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validDepartmentsId(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_DEPARTMENT_FILTER
|
||||
],
|
||||
'authors' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validAuthorsId(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_AUTHOR_FILTER
|
||||
],
|
||||
'assigned' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::in(['0','1']),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_ASSIGNED_FILTER
|
||||
],
|
||||
'orderBy' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validOrderBy(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_ORDER_BY
|
||||
],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
$inputs = [
|
||||
'closed' => Controller::request('closed'),
|
||||
'tags' => json_decode(Controller::request('tags')),
|
||||
'unreadStaff' => Controller::request('unreadStaff'),
|
||||
'priority' => json_decode(Controller::request('priority')),
|
||||
'dateRange' => json_decode(Controller::request('dateRange')),
|
||||
'departments' => json_decode(Controller::request('departments')),
|
||||
'authors' => json_decode(Controller::request('authors'),true),
|
||||
'assigned' => Controller::request('assigned'),
|
||||
'query' => Controller::request('query'),
|
||||
'orderBy' => json_decode(Controller::request('orderBy'),true),
|
||||
'page' => Controller::request('page'),
|
||||
'allowedDepartments' => Controller::getLoggedUser()->sharedDepartmentList->toArray(),
|
||||
];
|
||||
|
||||
|
||||
$query = $this->getSQLQuery($inputs);
|
||||
$queryWithOrder = $this->getSQLQueryWithOrder($inputs);
|
||||
$totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2", [':query' => $inputs['query']])[0]['COUNT(*)'];
|
||||
$ticketIdList = RedBean::getAll($queryWithOrder, [':query' => "%" . $inputs['query'] . "%"]);
|
||||
$ticketList = [];
|
||||
|
||||
foreach ($ticketIdList as $item) {
|
||||
$ticket = Ticket::getDataStore($item['id']);
|
||||
array_push($ticketList, $ticket->toArray());
|
||||
}
|
||||
$ticketTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticket';");
|
||||
|
||||
if($ticketTableExists){
|
||||
Response::respondSuccess([
|
||||
'tickets' => $ticketList,
|
||||
'pages' => ceil($totalCount / 10),
|
||||
'page' => $inputs['page'] ? ($inputs['page']*1) : 1
|
||||
]);
|
||||
}else{
|
||||
Response::respondSuccess([]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getSQLQuery($inputs) {
|
||||
$tagsTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'tag_ticket';");
|
||||
$ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';");
|
||||
|
||||
$taglistQuery = ( $tagsTableExists ? " LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id" : '');
|
||||
$ticketeventlistQuery = ( $ticketEventTableExists ? " LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id" : '');
|
||||
|
||||
$query = "FROM (ticket" . $taglistQuery . $ticketeventlistQuery .")";
|
||||
$filters = "";
|
||||
$this->setQueryFilters($inputs, $filters);
|
||||
$query .= $filters . " GROUP BY ticket.id";
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function getSQLQueryWithOrder($inputs) {
|
||||
$query = $this->getSQLQuery($inputs);
|
||||
$order = "";
|
||||
$query = "SELECT" . " ticket.id " . $query;
|
||||
|
||||
$this->setQueryOrder($inputs, $order);
|
||||
$inputs['page'] ? $page = $inputs['page'] : $page = 1 ;
|
||||
$query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10);
|
||||
return $query;
|
||||
}
|
||||
|
||||
//FILTER
|
||||
private function setQueryFilters($inputs, &$filters){
|
||||
if(array_key_exists('tags',$inputs)) $this->setTagFilter($inputs['tags'], $filters);
|
||||
if(array_key_exists('closed',$inputs)) $this->setClosedFilter($inputs['closed'], $filters);
|
||||
if(array_key_exists('assigned',$inputs)) $this->setAssignedFilter($inputs['assigned'], $filters);
|
||||
if(array_key_exists('unreadStaff',$inputs)) $this->setSeenFilter($inputs['unreadStaff'], $filters);
|
||||
if(array_key_exists('priority',$inputs)) $this->setPriorityFilter($inputs['priority'], $filters);
|
||||
if(array_key_exists('dateRange',$inputs)) $this->setDateFilter($inputs['dateRange'], $filters);
|
||||
if(array_key_exists('departments',$inputs)) $this->setDepartmentFilter($inputs['departments'],$inputs['allowedDepartments'], $filters);
|
||||
if(array_key_exists('authors',$inputs)) $this->setAuthorFilter($inputs['authors'], $filters);
|
||||
if(array_key_exists('query',$inputs)) $this->setStringFilter($inputs['query'], $filters);
|
||||
if($filters != "") $filters = " WHERE " . $filters;
|
||||
}
|
||||
|
||||
private function setTagFilter($tagList, &$filters){
|
||||
$tagsTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'tag_ticket';");
|
||||
|
||||
if($tagList && $tagsTableExists){
|
||||
$filters != "" ? $filters .= " and " : null;
|
||||
|
||||
foreach($tagList as $key => $tag) {
|
||||
|
||||
$key == 0 ? $filters .= " ( " : null;
|
||||
($key != 0 && $key != sizeof($tagList)) ? $filters .= " or " : null;
|
||||
|
||||
$filters .= "tag_ticket.tag_id = " . $tag ;
|
||||
}
|
||||
$filters .= ")";
|
||||
}
|
||||
}
|
||||
public function setClosedFilter($closed, &$filters){
|
||||
if ($closed !== null) {
|
||||
if ($filters != "") $filters .= " and ";
|
||||
$filters .= "ticket.closed = " . $closed ;
|
||||
}
|
||||
}
|
||||
private function setSeenFilter($unreadStaff, &$filters){
|
||||
if ($unreadStaff !== null) {
|
||||
if ($filters != "") $filters .= " and ";
|
||||
$filters .= "ticket.unread_staff = " . $unreadStaff;
|
||||
}
|
||||
}
|
||||
private function setPriorityFilter($priorities, &$filters){
|
||||
if($priorities !== null){
|
||||
$first = TRUE;
|
||||
if ($filters != "") $filters .= " and ";
|
||||
foreach(array_unique($priorities) as $priority) {
|
||||
|
||||
if($first){
|
||||
$filters .= " ( ";
|
||||
$first = FALSE;
|
||||
} else {
|
||||
$filters .= " or ";
|
||||
}
|
||||
|
||||
if($priority == 0){
|
||||
$filters .= "ticket.priority = 'low'";
|
||||
}elseif($priority == 1){
|
||||
$filters .= "ticket.priority = 'medium'";
|
||||
}elseif($priority == 2){
|
||||
$filters .= "ticket.priority = 'high'";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
$priorities != "" ? $filters .= ") " : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function setDateFilter($dateRange, &$filters){
|
||||
if ($dateRange !== null) {
|
||||
if ($filters != "") $filters .= " and ";
|
||||
|
||||
foreach($dateRange as $key => $date) {
|
||||
$key == 0 ? ($filters .= "(ticket.date >= " . $date ): ($filters .= " and ticket.date <= " . $date . ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setDepartmentFilter($departments,$allowedDepartments, &$filters){
|
||||
$validDepartments = $this->generateValidDepartmentList($departments, $allowedDepartments);
|
||||
if ($filters != "") $filters .= " and ";
|
||||
$first = TRUE;
|
||||
|
||||
foreach($validDepartments as $department) {
|
||||
if($first){
|
||||
$filters .= " ( ";
|
||||
$first = FALSE;
|
||||
} else {
|
||||
$filters .= " or ";
|
||||
}
|
||||
$filters .= "ticket.department_id = " . $department;
|
||||
}
|
||||
$filters .= ")";
|
||||
|
||||
}
|
||||
|
||||
private function setAuthorFilter($authors, &$filters){
|
||||
if($authors !== null){
|
||||
$first = TRUE;
|
||||
if ($filters != "") $filters .= " and ";
|
||||
|
||||
foreach($authors as $author){
|
||||
|
||||
if($first){
|
||||
$filters .= " ( ";
|
||||
$first = FALSE;
|
||||
} else {
|
||||
$filters .= " or ";
|
||||
}
|
||||
|
||||
if($author['staff']){
|
||||
$filters .= "ticket.author_staff_id = " . $author['id'];
|
||||
} else {
|
||||
$filters .= "ticket.author_id = " . $author['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$filters .= ")";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function setAssignedFilter($assigned, &$filters){
|
||||
if($assigned !== null){
|
||||
if ($filters != "") $filters .= " and ";
|
||||
$key = "";
|
||||
$assigned == 0 ? $key = "IS NULL" : $key = "IS NOT NULL";
|
||||
$filters .= "ticket.owner_id " . $key;
|
||||
}
|
||||
}
|
||||
|
||||
private function setStringFilter($search, &$filters){
|
||||
$ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';");
|
||||
|
||||
if($search !== null){
|
||||
if ($filters != "") $filters .= " and ";
|
||||
$ticketevent = ( $ticketEventTableExists ? " or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query)" : "");
|
||||
$filters .= " (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query". $ticketevent ." )";
|
||||
};
|
||||
}
|
||||
|
||||
private function generateValidDepartmentList($departments, $allowedDepartments){
|
||||
$result = [];
|
||||
$managedDepartments = [];
|
||||
if($departments == null) $departments = [];
|
||||
foreach ($allowedDepartments as $department) {
|
||||
array_push($managedDepartments,$department['id']);
|
||||
}
|
||||
$result = array_intersect($departments,$managedDepartments);
|
||||
|
||||
if(empty($result)) $result = $managedDepartments;
|
||||
|
||||
$result = array_unique($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//ORDER
|
||||
private function setQueryOrder($inputs, &$order){
|
||||
$order = " ORDER BY ";
|
||||
if(array_key_exists('query',$inputs)) $this->setStringOrder($inputs['query'], $order);
|
||||
if(array_key_exists('orderBy',$inputs)) $this->setEspecificOrder($inputs['orderBy'], $order);
|
||||
$order .= "ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc ";
|
||||
}
|
||||
private function setEspecificOrder($orderBy, &$order){
|
||||
if($orderBy !== null){
|
||||
$orientation = ($orderBy['asc'] ? " asc" : " desc" );
|
||||
$order .= "ticket." . $orderBy['value'] . $orientation . ",";
|
||||
};
|
||||
}
|
||||
private function setStringOrder($querysearch, &$order){
|
||||
$ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';");
|
||||
|
||||
if($querysearch !== null){
|
||||
$ticketeventOrder = ( $ticketEventTableExists ? " CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) THEN ticketevent.content END desc," : "");
|
||||
$order .= "CASE WHEN (ticket.ticket_number LIKE :query) THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE :query) THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE :query) THEN ticket.content END desc," . $ticketeventOrder ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ $userControllers->setGroupPath('/user');
|
|||
|
||||
$userControllers->addController(new LoginController);
|
||||
$userControllers->addController(new SignUpController);
|
||||
$userControllers->addController(new InviteUserController);
|
||||
$userControllers->addController(new LogoutController);
|
||||
$userControllers->addController(new CheckSessionController);
|
||||
$userControllers->addController(new SendRecoverPasswordController);
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
use Respect\Validation\Validator as DataValidator;
|
||||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
/**
|
||||
* @api {post} /user/invite Invite
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Invite
|
||||
*
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiDescription This path invites an user on the system.
|
||||
*
|
||||
* @apiPermission staff1
|
||||
*
|
||||
* @apiParam {String} name The name of the invited user.
|
||||
* @apiParam {String} email The email of the invited user.
|
||||
* @apiParam {String} customfield_ Custom field values for this user.
|
||||
*
|
||||
* @apiUse INVALID_NAME
|
||||
* @apiUse INVALID_EMAIL
|
||||
* @apiUse INVALID_CAPTCHA
|
||||
* @apiUse USER_EXISTS
|
||||
* @apiUse ALREADY_BANNED
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_CUSTOM_FIELD_OPTION
|
||||
*
|
||||
* @apiSuccess {Object} data Information about invited user
|
||||
* @apiSuccess {Number} data.userId Id of the invited user
|
||||
* @apiSuccess {String} data.userEmail Email of the invited user
|
||||
*
|
||||
*/
|
||||
|
||||
class InviteUserController extends Controller {
|
||||
const PATH = '/invite';
|
||||
const METHOD = 'POST';
|
||||
|
||||
private $userEmail;
|
||||
private $userName;
|
||||
|
||||
public function validations() {
|
||||
$validations = [
|
||||
'permission' => 'staff_1',
|
||||
'requestData' => [
|
||||
'name' => [
|
||||
'validation' => DataValidator::length(2, 55),
|
||||
'error' => ERRORS::INVALID_NAME
|
||||
],
|
||||
'email' => [
|
||||
'validation' => DataValidator::email(),
|
||||
'error' => ERRORS::INVALID_EMAIL
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$validations['requestData']['captcha'] = [
|
||||
'validation' => DataValidator::captcha(),
|
||||
'error' => ERRORS::INVALID_CAPTCHA
|
||||
];
|
||||
|
||||
return $validations;
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
if (!Controller::isUserSystemEnabled()) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
$this->storeRequestData();
|
||||
|
||||
$existentUser = User::getUser($this->userEmail, 'email');
|
||||
|
||||
if (!$existentUser->isNull()) {
|
||||
throw new RequestException(ERRORS::USER_EXISTS);
|
||||
}
|
||||
|
||||
$banRow = Ban::getDataStore($this->userEmail, 'email');
|
||||
|
||||
if (!$banRow->isNull()) {
|
||||
throw new RequestException(ERRORS::ALREADY_BANNED);
|
||||
}
|
||||
|
||||
$userId = $this->createNewUserAndRetrieveId();
|
||||
|
||||
$this->token = Hashing::generateRandomToken();
|
||||
|
||||
$recoverPassword = new RecoverPassword();
|
||||
$recoverPassword->setProperties(array(
|
||||
'email' => $this->userEmail,
|
||||
'token' => $this->token,
|
||||
'staff' => false
|
||||
));
|
||||
$recoverPassword->store();
|
||||
|
||||
$this->sendInvitationMail();
|
||||
|
||||
Response::respondSuccess([
|
||||
'userId' => $userId,
|
||||
'userEmail' => $this->userEmail
|
||||
]);
|
||||
|
||||
Log::createLog('INVITE', $this->userName);
|
||||
}
|
||||
|
||||
public function storeRequestData() {
|
||||
$this->userName = Controller::request('name');
|
||||
$this->userEmail = Controller::request('email');
|
||||
}
|
||||
|
||||
public function createNewUserAndRetrieveId() {
|
||||
$userInstance = new User();
|
||||
|
||||
$userInstance->setProperties([
|
||||
'name' => $this->userName,
|
||||
'signupDate' => Date::getCurrentDate(),
|
||||
'tickets' => 0,
|
||||
'email' => $this->userEmail,
|
||||
'password' => Hashing::hashPassword(Hashing::generateRandomToken()),
|
||||
'verificationToken' => null,
|
||||
'xownCustomfieldvalueList' => $this->getCustomFieldValues()
|
||||
]);
|
||||
|
||||
return $userInstance->store();
|
||||
}
|
||||
|
||||
public function sendInvitationMail() {
|
||||
$mailSender = MailSender::getInstance();
|
||||
|
||||
$mailSender->setTemplate(MailTemplate::USER_INVITE, [
|
||||
'to' => $this->userEmail,
|
||||
'name' => $this->userName,
|
||||
'url' => Setting::getSetting('url')->getValue(),
|
||||
'token' => $this->token
|
||||
]);
|
||||
|
||||
$mailSender->send();
|
||||
}
|
||||
}
|
|
@ -56,10 +56,6 @@ class RecoverPasswordController extends Controller {
|
|||
}
|
||||
|
||||
public function handler() {
|
||||
if(!Controller::isUserSystemEnabled()) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
$this->requestData();
|
||||
$this->changePassword();
|
||||
}
|
||||
|
@ -69,30 +65,38 @@ class RecoverPasswordController extends Controller {
|
|||
$this->token = Controller::request('token');
|
||||
$this->password = Controller::request('password');
|
||||
}
|
||||
|
||||
public function changePassword() {
|
||||
$recoverPassword = RecoverPassword::getDataStore($this->token, 'token');
|
||||
|
||||
if($recoverPassword->isNull() || $recoverPassword->email !== $this->email) {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
if(!Controller::isUserSystemEnabled() && !$recoverPassword->staff) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
if($recoverPassword->staff) {
|
||||
$this->user = Staff::getDataStore($this->email, 'email');
|
||||
}else {
|
||||
} else {
|
||||
$this->user = User::getDataStore($this->email, 'email');
|
||||
}
|
||||
|
||||
if (!$recoverPassword->isNull() && !$this->user->isNull()) {
|
||||
$recoverPassword->delete();
|
||||
if($this->user->isNull()) throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
|
||||
$this->user->setProperties([
|
||||
'password' => Hashing::hashPassword($this->password)
|
||||
]);
|
||||
$recoverPassword->delete();
|
||||
|
||||
$this->user->store();
|
||||
$this->user->setProperties([
|
||||
'password' => Hashing::hashPassword($this->password)
|
||||
]);
|
||||
|
||||
$this->sendMail();
|
||||
Response::respondSuccess(['staff' => $recoverPassword->staff]);
|
||||
} else {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
$this->user->store();
|
||||
|
||||
$this->sendMail();
|
||||
Response::respondSuccess(['staff' => $recoverPassword->staff]);
|
||||
}
|
||||
|
||||
public function sendMail() {
|
||||
$mailSender = MailSender::getInstance();
|
||||
|
||||
|
|
|
@ -49,17 +49,18 @@ class SendRecoverPasswordController extends Controller {
|
|||
}
|
||||
|
||||
public function handler() {
|
||||
if(!Controller::isUserSystemEnabled()) {
|
||||
$this->staff = Controller::request('staff');
|
||||
|
||||
if(!Controller::isUserSystemEnabled() && !$this->staff) {
|
||||
throw new RequestException(ERRORS::USER_SYSTEM_DISABLED);
|
||||
}
|
||||
|
||||
$this->staff = Controller::request('staff');
|
||||
$email = Controller::request('email');
|
||||
|
||||
if($this->staff){
|
||||
$this->user = Staff::getUser($email,'email');
|
||||
}else {
|
||||
$this->user = User::getUser($email,'email');
|
||||
$this->user = Staff::getUser($email, 'email');
|
||||
} else {
|
||||
$this->user = User::getUser($email, 'email');
|
||||
}
|
||||
|
||||
if(!$this->user->isNull()) {
|
||||
|
|
|
@ -18,7 +18,7 @@ DataValidator::with('CustomValidations', true);
|
|||
* @apiParam {String} name The name of the new user.
|
||||
* @apiParam {String} email The email of the new user.
|
||||
* @apiParam {String} password The password of the new user.
|
||||
* @apiParam {String} apiKey APIKey to sign up an user if the user system is disabled.
|
||||
* @apiParam {String} apiKey APIKey to sign up an user if the registration system is disabled.
|
||||
* @apiParam {String} customfield_ Custom field values for this user.
|
||||
*
|
||||
* @apiUse INVALID_NAME
|
||||
|
|
|
@ -281,6 +281,15 @@ class ERRORS {
|
|||
const INVALID_PRIORITY = 'INVALID_PRIORITY';
|
||||
const INVALID_PAGE = 'INVALID_PAGE';
|
||||
const INVALID_QUERY = 'INVALID_QUERY';
|
||||
const INVALID_TAG_FILTER = 'INVALID_TAG_FILTER';
|
||||
const INVALID_CLOSED_FILTER = 'INVALID_CLOSED_FILTER';
|
||||
const INVALID_UNREAD_STAFF_FILTER = 'INVALID_UNREAD_STAFF_FILTER';
|
||||
const INVALID_PRIORITY_FILTER = 'INVALID_PRIORITY_FILTER';
|
||||
const INVALID_DATE_RANGE_FILTER = 'INVALID_DATE_RANGE_FILTER';
|
||||
const INVALID_DEPARTMENT_FILTER = 'INVALID_DEPARTMENT_FILTER';
|
||||
const INVALID_AUTHOR_FILTER = 'INVALID_AUTHOR_FILTER';
|
||||
const INVALID_ASSIGNED_FILTER = 'INVALID_ASSIGNED_FILTER';
|
||||
const INVALID_ORDER_BY = 'INVALID_ORDER_BY';
|
||||
const INVALID_TOPIC = 'INVALID_TOPIC';
|
||||
const INVALID_SEARCH = 'INVALID_SEARCH';
|
||||
const INVALID_ORDER = 'INVALID_ORDER';
|
||||
|
|
|
@ -25,6 +25,12 @@ class MailTexts {
|
|||
'Hi, {{name}}. You have requested to recover your password.',
|
||||
'Use this code in {{url}}/recover-password?email={{to}}&token={{token}} or click the button below.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'You have been invited - OpenSupports',
|
||||
'You have been invited',
|
||||
'Hi, {{name}}. You have been invited to join our support center.',
|
||||
'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Access system changed - OpenSupports',
|
||||
'Access system changed',
|
||||
|
@ -85,6 +91,12 @@ class MailTexts {
|
|||
'喂 {{name}}。 您已要求恢复密码。',
|
||||
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}} 或单击下面的按钮.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'您已受邀 - OpenSupports',
|
||||
'您已受邀',
|
||||
'你好, {{name}}. 邀请您加入我们的支持中心.',
|
||||
'使用此代码 {{url}}/recover-password?email={{to}}&token={{token}}&invited=true 或单击下面的按钮来设置密码.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'访问系统更改 - OpenSupports',
|
||||
'访问系统更改',
|
||||
|
@ -145,6 +157,12 @@ class MailTexts {
|
|||
'Hallo, {{name}}. Sie haben aufgefordert, Ihr Passwort wiederherzustellen.',
|
||||
'Verwenden Sie diesen Code in {{url}}/recover-password?email={{to}}&token={{token}} oder klicken Sie auf die Schaltfläche unten.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Du bist eingeladen - OpenSupports',
|
||||
'Du bist eingeladen',
|
||||
'Hallo, {{name}}. Sie wurden zu unserem Support-Center eingeladen.',
|
||||
'Verwenden Sie diesen Code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true oder klicken Sie auf die Schaltfläche unten, um Ihr Passwort einzurichten.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Access system changed - OpenSupports',
|
||||
'Zugriffssystem geändert',
|
||||
|
@ -205,6 +223,12 @@ class MailTexts {
|
|||
'Hola, {{name}}. Has requerido recuperar tu contraseña.',
|
||||
'Usá este codigo en {{url}}/recover-password?email={{to}}&token={{token}} o hacé click en el botón de abajo.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Haz sido invitado - OpenSupports',
|
||||
'Haz sido invitado',
|
||||
'Hola, {{name}}. Haz sido invitado a unirte a nuestro sistema de soporte.',
|
||||
'Usa este código en {{url}}/recover-password?email={{to}}&token={{token}}&invited=true o haz click en el botón de abajo para establecer tu contraseña.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Sistema de acceso cambiado - OpenSupports',
|
||||
'Sistema de acceso cambiado',
|
||||
|
@ -265,6 +289,12 @@ class MailTexts {
|
|||
'Salut, {{name}}. Vous avez demandé à récupérer votre mot de passe.',
|
||||
'Utilisez ce code dans {{url}}/recover-password?email={{to}}&token={{token}} ou cliquez sur le bouton ci-dessous.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'You have been invited - OpenSupports',
|
||||
'You have been invited',
|
||||
'Hi, {{name}}. You have been invited to join our support center.',
|
||||
'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Système d\'accès modifié - OpenSupports',
|
||||
'Système d\'accès modifié',
|
||||
|
@ -325,6 +355,12 @@ class MailTexts {
|
|||
'नमस्ते {{name}}. आपने अपना पासवर्ड पुनर्प्राप्त करने का अनुरोध किया है',
|
||||
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}} या नीचे दिए गए बटन पर क्लिक करें.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'आपको आमंत्रित किया गया है - OpenSupports',
|
||||
'आपको आमंत्रित किया गया है',
|
||||
'नमस्ते, {{name}}. आपको हमारे सहायता केंद्र से जुड़ने के लिए आमंत्रित किया गया है.',
|
||||
'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}}&invited=true या अपना पासवर्ड सेट करने के लिए नीचे दिए गए बटन पर क्लिक करें.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'sistem akses berubah - OpenSupports',
|
||||
'एक्सेस सिस्टम बदल गया',
|
||||
|
@ -385,6 +421,12 @@ class MailTexts {
|
|||
'Ciao, {{name}}. Hai richiesto di recuperare la tua password.',
|
||||
'Clicca sul link {{url}}/recover-password?email={{to}}&token={{token}} o clicca sul pulsante qui sotto.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Sei stato invitato - OpenSupports',
|
||||
'Sei stato invitato',
|
||||
'Ciao, {{name}}. Sei stato invitato a far parte del nostro centro di supporto.',
|
||||
'Usa questo codice in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true oppure fai clic sul pulsante in basso per impostare la password.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Il sistema di accesso è cambiato - OpenSupports',
|
||||
'Modifica sistema di accesso',
|
||||
|
@ -445,6 +487,12 @@ class MailTexts {
|
|||
'こんにちは、{{name}}。 パスワードの回復を要求しました。',
|
||||
'でこのコードを使用 {{url}}/recover-password?email={{to}}&token={{token}} 下のボタンをクリックしてください.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'招待されました - OpenSupports',
|
||||
'招待されました',
|
||||
'こんにちは, {{name}}. サポートセンターに招待されました.',
|
||||
'このコードを {{url}}/recover-password?email={{to}}&token={{token}}&invited=true または、下のボタンをクリックしてパスワードを設定します.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'アクセスシステムが変更されました - OpenSupports',
|
||||
'アクセスシステムが変更されました',
|
||||
|
@ -505,6 +553,12 @@ class MailTexts {
|
|||
'Olá, {{name}}. Você solicitou a recuperação da sua senha.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}} ou clique no botão abaixo.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Você foi convidado - OpenSupports',
|
||||
'Você foi convidado',
|
||||
'Oi, {{name}}. Você foi convidado a participar do nosso centro de suporte.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ou clique no botão abaixo para configurar sua senha.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Sistema de acesso alterado - OpenSupports',
|
||||
'Sistema de acesso alterado',
|
||||
|
@ -565,6 +619,12 @@ class MailTexts {
|
|||
'Здравствуй, {{name}}. Вы запросили восстановить пароль.',
|
||||
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}} или нажмите кнопку ниже.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Вы были приглашены - OpenSupports',
|
||||
'Вы были приглашены',
|
||||
'Здравствуй, {{name}}. Вас пригласили присоединиться к нашему центру поддержки.',
|
||||
'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}}&invited=true или нажмите кнопку ниже, чтобы установить пароль.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Система доступа изменена - OpenSupports',
|
||||
'Система доступа изменена',
|
||||
|
@ -625,6 +685,12 @@ class MailTexts {
|
|||
'Merhaba, {{name}}. Şifrenizi geri yüklemenizi istediniz.',
|
||||
'Bu kodu şu adreste kullanın {{url}}/recover-password?email={{to}}&token={{token}} veya aşağıdaki butona tıklayın.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Davet edildin - OpenSupports',
|
||||
'Davet edildin',
|
||||
'Merhaba, {{name}}. Destek merkezimize katılmaya davet edildiniz.',
|
||||
'Bu kodu {{url}}/recover-password?email={{to}}&token={{token}}&invited=true veya şifrenizi ayarlamak için aşağıdaki butona tıklayın.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Erişim sistemi değiştirildi - OpenSupports',
|
||||
'Erişim sistemi değiştirildi',
|
||||
|
@ -685,6 +751,12 @@ class MailTexts {
|
|||
'Olá, {{name}}. Você solicitou a recuperação da sua senha.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}} ou clique no botão abaixo.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Você foi convidado - OpenSupports',
|
||||
'Você foi convidado',
|
||||
'Oi, {{name}}. Você foi convidado a participar do nosso centro de suporte.',
|
||||
'Use este código em {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ou clique no botão abaixo para configurar sua senha.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Sistema de acesso alterado - OpenSupports',
|
||||
'Sistema de acesso alterado',
|
||||
|
@ -745,6 +817,12 @@ class MailTexts {
|
|||
'Γεια σου, {{name}}. Ζητήσατε να ανακτήσετε τον κωδικό πρόσβασής σας.',
|
||||
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}} / recover-password? Email = {{to}} & token = {{token}} ή κάντε κλικ στο παρακάτω κουμπί.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Έχετε προσκληθεί - OpenSupports',
|
||||
'Έχετε προσκληθεί',
|
||||
'Γεια σου, {{name}}. Έχετε προσκληθεί να συμμετάσχετε στο κέντρο υποστήριξής μας.',
|
||||
'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ή κάντε κλικ στο παρακάτω κουμπί για να ρυθμίσετε τον κωδικό πρόσβασής σας.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Το σύστημα πρόσβασης άλλαξε - OpenSupports',
|
||||
'Το σύστημα πρόσβασης άλλαξε',
|
||||
|
@ -805,6 +883,12 @@ class MailTexts {
|
|||
'Hallo, {{name}}. U heeft een verzoek gedaan om uw wachtwoord te resetten.',
|
||||
'Gebruik deze code {{url}}/recover-password?email={{to}}&token={{token}} of klik op de knop hieronder.'
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Je bent uitgenodigd - OpenSupports',
|
||||
'Je bent uitgenodigd',
|
||||
'Hallo, {{name}}. U bent uitgenodigd om lid te worden van ons ondersteuningscentrum.',
|
||||
'Gebruik deze code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true of klik op de onderstaande knop om uw wachtwoord in te stellen.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Toegangssysteem gewijzigd - OpenSupports',
|
||||
'Toegang tot incidenten is gewijzigd',
|
||||
|
@ -865,6 +949,12 @@ class MailTexts {
|
|||
'Hej, {{name}}. Zażądałeś odzyskania hasła.',
|
||||
'Użyj tego linka {{url}}/recover-password?email={{to}}&token={{token}} lub kliknij przycisk poniżej.',
|
||||
],
|
||||
'USER_INVITE' => [
|
||||
'Zostałeś zaproszony - OpenSupports',
|
||||
'Zostałeś zaproszony',
|
||||
'Hej, {{name}}. Zaproszono Cię do dołączenia do naszego centrum wsparcia.',
|
||||
'Użyj tego kodu w {{url}}/recover-password?email={{to}}&token={{token}}&invited=true lub kliknij przycisk poniżej, aby ustawić hasło.'
|
||||
],
|
||||
'USER_SYSTEM_DISABLED' => [
|
||||
'Zmieniono dostęp do systemu - OpenSupports',
|
||||
'Zmieniono dostęp do systemu',
|
||||
|
|
|
@ -0,0 +1,384 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Support Center</title>
|
||||
|
||||
<style type="text/css">
|
||||
/* Take care of image borders and formatting, client hacks */
|
||||
img { max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic;}
|
||||
a img { border: none; }
|
||||
table { border-collapse: collapse !important;}
|
||||
#outlook a { padding:0; }
|
||||
.ReadMsgBody { width: 100%; }
|
||||
.ExternalClass { width: 100%; }
|
||||
.backgroundTable { margin: 0 auto; padding: 0; width: 100% !important; }
|
||||
table td { border-collapse: collapse; }
|
||||
.ExternalClass * { line-height: 115%; }
|
||||
.container-for-gmail-android { min-width: 600px; }
|
||||
|
||||
|
||||
/* General styling */
|
||||
* {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100% !important;
|
||||
margin: 0 !important;
|
||||
height: 100%;
|
||||
color: #676767;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #777777;
|
||||
text-align: center;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #676767;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.header-lg,
|
||||
.header-md,
|
||||
.header-sm {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
padding: 35px 0 0;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.header-md {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.header-sm {
|
||||
padding: 5px 0;
|
||||
font-size: 18px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.content-padding {
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
.mobile-header-padding-right {
|
||||
width: 290px;
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.mobile-header-padding-left {
|
||||
width: 290px;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.free-text {
|
||||
width: 100% !important;
|
||||
padding: 10px 60px 0px;
|
||||
}
|
||||
|
||||
.block-rounded {
|
||||
border-radius: 5px;
|
||||
border: 1px solid #e5e5e5;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 55px 0 0;
|
||||
}
|
||||
|
||||
.info-block {
|
||||
padding: 0 20px;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.mini-block-container {
|
||||
padding: 30px 50px;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.mini-block {
|
||||
background-color: #ffffff;
|
||||
width: 498px;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
padding: 60px 75px;
|
||||
}
|
||||
|
||||
.block-rounded {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.info-img {
|
||||
width: 258px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.force-width-img {
|
||||
width: 480px;
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.force-width-full {
|
||||
width: 600px;
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.user-img img {
|
||||
width: 82px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.user-img {
|
||||
width: 92px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-msg {
|
||||
width: 236px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
padding: 10px 0;
|
||||
border: 1px solid #cccccc;
|
||||
color: #4d4d4d;
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.force-width-gmail {
|
||||
min-width:600px;
|
||||
height: 0px !important;
|
||||
line-height: 1px !important;
|
||||
font-size: 1px !important;
|
||||
}
|
||||
|
||||
.button-width {
|
||||
width: 228px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style type="text/css" media="screen">
|
||||
@import url(http://fonts.googleapis.com/css?family=Oxygen:400,700);
|
||||
</style>
|
||||
|
||||
<style type="text/css" media="screen">
|
||||
@media screen {
|
||||
/* Thanks Outlook 2013! */
|
||||
* {
|
||||
font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style type="text/css" media="only screen and (max-width: 480px)">
|
||||
/* Mobile styles */
|
||||
@media only screen and (max-width: 480px) {
|
||||
|
||||
table[class*="container-for-gmail-android"] {
|
||||
min-width: 290px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class="w320"] {
|
||||
width: 320px !important;
|
||||
}
|
||||
|
||||
img[class="force-width-gmail"] {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
a[class="button-width"],
|
||||
a[class="button-mobile"] {
|
||||
width: 248px !important;
|
||||
}
|
||||
|
||||
td[class*="mobile-header-padding-left"] {
|
||||
width: 160px !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
td[class*="mobile-header-padding-right"] {
|
||||
width: 160px !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
td[class="header-lg"] {
|
||||
font-size: 24px !important;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
td[class="header-md"] {
|
||||
font-size: 18px !important;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
td[class="content-padding"] {
|
||||
padding: 5px 0 30px !important;
|
||||
}
|
||||
|
||||
td[class="button"] {
|
||||
padding: 15px 0 5px !important;
|
||||
}
|
||||
|
||||
td[class*="free-text"] {
|
||||
padding: 10px 18px 30px !important;
|
||||
}
|
||||
|
||||
img[class="force-width-img"],
|
||||
img[class="force-width-full"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
td[class="info-block"] {
|
||||
display: block !important;
|
||||
width: 280px !important;
|
||||
padding-bottom: 40px !important;
|
||||
}
|
||||
|
||||
td[class="info-img"],
|
||||
img[class="info-img"] {
|
||||
width: 278px !important;
|
||||
}
|
||||
|
||||
td[class="mini-block-container"] {
|
||||
padding: 8px 20px !important;
|
||||
width: 280px !important;
|
||||
}
|
||||
|
||||
td[class="mini-block"] {
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
||||
td[class="user-img"] {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
width: 100% !important;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
td[class="user-msg"] {
|
||||
display: block !important;
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#f7f7f7">
|
||||
<table align="center" cellpadding="0" cellspacing="0" class="container-for-gmail-android" width="100%">
|
||||
<tr>
|
||||
<td align="left" valign="top" width="100%" style="background-color: #ffffff;">
|
||||
<center>
|
||||
<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#ffffff" style="border-bottom: 1px solid #cccccc">
|
||||
<tr>
|
||||
<td width="100%" height="80" valign="top" style="text-align: center; vertical-align:middle;">
|
||||
<center>
|
||||
<table cellpadding="0" cellspacing="0" width="600" class="w320">
|
||||
<tr>
|
||||
<td style="vertical-align: middle;padding: 15px 0;">
|
||||
<img src="{{IMAGE_HEADER_URL}}" alt="logo">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="100%" style="background-color: #f7f7f7;" class="content-padding">
|
||||
<center>
|
||||
<table cellspacing="0" cellpadding="0" width="600" class="w320">
|
||||
<tr>
|
||||
<td class="header-lg">
|
||||
{{USER_INVITE_MATCH_1}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="free-text">
|
||||
{{USER_INVITE_MATCH_2}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mini-block-container">
|
||||
<table cellspacing="0" cellpadding="0" width="100%" style="border-collapse:separate !important;">
|
||||
<tr>
|
||||
<td class="mini-block">
|
||||
<table cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding-bottom: 30px;">
|
||||
{{USER_INVITE_MATCH_3}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="code-block">
|
||||
{{token}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="button">
|
||||
<div><a class="button-mobile" target="_blank" href="{{url}}/recover-password?email={{to}}&token={{token}}&invited=true"
|
||||
style="background-color:#ff6f6f;border-radius:5px;color:#ffffff;display:inline-block;font-family:'Cabin', Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;line-height:45px;text-align:center;text-decoration:none;width:155px;-webkit-text-size-adjust:none;mso-hide:all;">Set up your password</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="100%" style="background-color: #ffffff; border-top: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5; height: 100px;">
|
||||
<center>
|
||||
<table cellspacing="0" cellpadding="0" width="600" class="w320">
|
||||
<tr>
|
||||
<td style="padding: 25px 0 25px">
|
||||
<strong>OpenSupports</strong><br />
|
||||
Open source ticket system<br />
|
||||
www.opensupports.com<br /><br />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -152,9 +152,8 @@ abstract class Controller {
|
|||
public static function getCustomFieldValues() {
|
||||
$customFields = Customfield::getAll();
|
||||
$customFieldValues = new DataStoreList();
|
||||
|
||||
foreach($customFields as $customField) {
|
||||
$value = Controller::request('customfield_' . $customField->name);
|
||||
$value = Controller::request('customfield_' . str_replace(' ', '_', $customField->name));
|
||||
if($value !== null) {
|
||||
$customFieldValue = new Customfieldvalue();
|
||||
$customFieldValue->setProperties([
|
||||
|
@ -183,7 +182,6 @@ abstract class Controller {
|
|||
$customFieldValues->add($customFieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $customFieldValues;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace CustomValidations;
|
||||
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class ValidAuthorsId extends AbstractRule {
|
||||
|
||||
public function validate($authors) {
|
||||
if(is_array(json_decode($authors))){
|
||||
foreach (json_decode($authors) as $authorObject) {
|
||||
if($authorObject->staff){
|
||||
$author = \Staff::getDataStore($authorObject->id);
|
||||
}else{
|
||||
$author = \User::getDataStore($authorObject->id);
|
||||
}
|
||||
if($author->isNull()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace CustomValidations;
|
||||
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class ValidDateRange extends AbstractRule {
|
||||
|
||||
public function validate($dateRange) {
|
||||
$dateArray = json_decode($dateRange);
|
||||
|
||||
if(is_array($dateArray) && count($dateArray) == 2 ){
|
||||
foreach ($dateArray as $date) {
|
||||
if (!is_numeric($date)) return false;
|
||||
}
|
||||
return $dateArray[0] <= $dateArray[1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace CustomValidations;
|
||||
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class ValidDepartmentsId extends AbstractRule {
|
||||
|
||||
public function validate($departments) {
|
||||
$DepartmentsList = json_decode($departments);
|
||||
if(is_array($DepartmentsList)){
|
||||
foreach ($DepartmentsList as $departmentsId) {
|
||||
$department = \Department::getDataStore($departmentsId);
|
||||
if($department->isNull()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace CustomValidations;
|
||||
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class ValidOrderBy extends AbstractRule {
|
||||
public function validate($orderBy) {
|
||||
if(is_object(json_decode($orderBy))){
|
||||
$values =["closed","owner_id","unread_staff","priority","date"];
|
||||
|
||||
$object = json_decode($orderBy);
|
||||
|
||||
if(($object->asc !== 1 && $object->asc !== 0) || !in_array($object->value, $values)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace CustomValidations;
|
||||
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class ValidPriorities extends AbstractRule {
|
||||
public function validate($priorities) {
|
||||
$PriorityList = json_decode($priorities);
|
||||
|
||||
if(is_array($PriorityList)){
|
||||
foreach (array_unique($PriorityList) as $priorityId) {
|
||||
if($priorityId != 0 && $priorityId != 1 && $priorityId != 2) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace CustomValidations;
|
||||
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class ValidTagsId extends AbstractRule {
|
||||
|
||||
public function validate($tags) {
|
||||
$listTags = json_decode($tags);
|
||||
if(is_array($listTags)){
|
||||
foreach ($listTags as $TagId) {
|
||||
$tag = \Tag::getDataStore($TagId);
|
||||
if($tag->isNull()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -22,21 +22,36 @@ class Log extends DataStore {
|
|||
'authorUser',
|
||||
'authorStaff',
|
||||
'to',
|
||||
'date'
|
||||
'date',
|
||||
'authorName'
|
||||
];
|
||||
}
|
||||
|
||||
public static function createLog($type,$to, $author = null) {
|
||||
public static function createLog($type, $to, $author = null) {
|
||||
$session = Session::getInstance();
|
||||
$authorName = '';
|
||||
|
||||
if($session->isTicketSession()) {
|
||||
$ticketNumber = $session->getTicketNumber();
|
||||
$ticket = Ticket::getByTicketNumber($ticketNumber);
|
||||
$authorName = $ticket->authorToArray()['name'];
|
||||
}
|
||||
|
||||
if($author === null) {
|
||||
$author = Controller::getLoggedUser();
|
||||
}
|
||||
|
||||
if(!$author->isNull()) {
|
||||
$authorName = $author->name;
|
||||
}
|
||||
|
||||
$log = new Log();
|
||||
|
||||
$log->setProperties(array(
|
||||
'type' => $type,
|
||||
'to' => $to,
|
||||
'date' => Date::getCurrentDate()
|
||||
'date' => Date::getCurrentDate(),
|
||||
'authorName' => $authorName
|
||||
));
|
||||
|
||||
if($author instanceof User) {
|
||||
|
@ -55,8 +70,8 @@ class Log extends DataStore {
|
|||
'type' => $this->type,
|
||||
'to' => $this->to,
|
||||
'author' => [
|
||||
'name' => $author->name,
|
||||
'id' => $author->id,
|
||||
'name' => $this->authorName,
|
||||
'id' => ($author && !$author->isNull()) ? $author->id : null,
|
||||
'staff' => $author instanceof Staff
|
||||
],
|
||||
'date' => $this->date
|
||||
|
|
|
@ -19,6 +19,7 @@ class MailTemplate extends DataStore {
|
|||
const USER_SIGNUP = 'USER_SIGNUP';
|
||||
const USER_PASSWORD = 'USER_PASSWORD';
|
||||
const PASSWORD_FORGOT = 'PASSWORD_FORGOT';
|
||||
const USER_INVITE = 'USER_INVITE';
|
||||
const USER_SYSTEM_DISABLED = 'USER_SYSTEM_DISABLED';
|
||||
const USER_SYSTEM_ENABLED = 'USER_SYSTEM_ENABLED';
|
||||
const TICKET_CREATED = 'TICKET_CREATED';
|
||||
|
@ -32,6 +33,7 @@ class MailTemplate extends DataStore {
|
|||
'USER_PASSWORD' => 'data/mail-templates/user-edit-password.html',
|
||||
'USER_EMAIL' => 'data/mail-templates/user-edit-email.html',
|
||||
'PASSWORD_FORGOT' => 'data/mail-templates/user-password-forgot.html',
|
||||
'USER_INVITE' => 'data/mail-templates/user-invite.html',
|
||||
'USER_SYSTEM_DISABLED' => 'data/mail-templates/user-system-disabled.html',
|
||||
'USER_SYSTEM_ENABLED' => 'data/mail-templates/user-system-enabled.html',
|
||||
'TICKET_CREATED' => 'data/mail-templates/ticket-created.html',
|
||||
|
|
|
@ -83,15 +83,16 @@ class Ticketevent extends DataStore {
|
|||
|
||||
public function toArray() {
|
||||
$user = ($this->authorStaff) ? $this->authorStaff : $this->authorUser;
|
||||
$author = $this->ticket->authorToArray();
|
||||
|
||||
return [
|
||||
'type' => $this->type,
|
||||
'ticketNumber' => $this->ticket->ticketNumber,
|
||||
'author' => [
|
||||
'name' => $user ? $user->name : null,
|
||||
'name' => $user ? $user->name : $author['name'],
|
||||
'staff' => $user instanceOf Staff,
|
||||
'id' => $user ? $user->id : null,
|
||||
'customfields' => $user->xownCustomfieldvalueList ? $user->xownCustomfieldvalueList->toArray() : [],
|
||||
'customfields' => ($user && $user->xownCustomfieldvalueList) ? $user->xownCustomfieldvalueList->toArray() : [],
|
||||
],
|
||||
'edited' => $this->editedContent
|
||||
];
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
<?php
|
||||
|
||||
class Controller {
|
||||
public static $requestReturnMock = 'mockRequestValue';
|
||||
const USE_VALUE_RETURN = 'sadf64a5s6d1f5sa';
|
||||
public static $requestReturnMock = null;
|
||||
public static $checkUserLoggedReturnMock = true;
|
||||
public static $isUserSystemEnabledReturnMock = true;
|
||||
|
||||
public static function useValueReturn() {
|
||||
static::$requestReturnMock = self::USE_VALUE_RETURN;
|
||||
}
|
||||
|
||||
public static function request($value) {
|
||||
if($value === 'staff') return false;
|
||||
return static::$requestReturnMock;
|
||||
if(static::$requestReturnMock !== self::USE_VALUE_RETURN) return static::$requestReturnMock;
|
||||
return $value . '_REQUEST_RESULT';
|
||||
}
|
||||
|
||||
public static function checkUserLogged() {
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
<?php
|
||||
// MOCKS
|
||||
include_once 'tests/__lib__/Mock.php';
|
||||
include_once 'tests/__mocks__/NullDataStoreMock.php';
|
||||
include_once 'tests/__mocks__/ResponseMock.php';
|
||||
include_once 'tests/__mocks__/ControllerMock.php';
|
||||
include_once 'tests/__mocks__/SessionMock.php';
|
||||
include_once 'tests/__mocks__/UserMock.php';
|
||||
include_once 'tests/__mocks__/HashingMock.php';
|
||||
include_once 'tests/__mocks__/SessionCookieMock.php';
|
||||
include_once 'tests/__mocks__/RedBeanMock.php';
|
||||
include_once 'data/ERRORS.php';
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RedBeanPHP\Facade as RedBean;
|
||||
|
||||
class SearchControllerTest extends TestCase {
|
||||
private $searchController;
|
||||
|
||||
protected function setUp() {
|
||||
Session::initStubs();
|
||||
Response::initStubs();
|
||||
RedBean::initStubs();
|
||||
RedBean::setStatics([
|
||||
'exec' => \Mock::stub()->returns(1)
|
||||
]);
|
||||
Controller::$requestReturnMock = null;
|
||||
$_SERVER['REMOTE_ADDR'] = 'MOCK_REMOTE';
|
||||
$this->searchController = new SearchController();
|
||||
}
|
||||
|
||||
public function testTagsFilter() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => []
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => [0]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 0) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => [0,1,2]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 0 or tag_ticket.tag_id = 1 or tag_ticket.tag_id = 2) GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
|
||||
public function testClosedFilter() {
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'closed'=> null
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'closed'=> 1
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.closed = 1 GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'closed'=> '0'
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.closed = 0 GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
public function testAssignedFilter(){
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'assigned'=> null
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'assigned'=> '0'
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.owner_id IS NULL GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'assigned'=> 1
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.owner_id IS NOT NULL GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
public function testUnreadStaffFilter() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'unreadStaff' => null
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'unreadStaff' => '0'
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.unread_staff = 0 GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'unreadStaff' => 1
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.unread_staff = 1 GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
|
||||
public function testPriorityFilter() {
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => []
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => [1]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 1) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => [2,3]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 2 or tag_ticket.tag_id = 3) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'tags' => [1,2,3]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 1 or tag_ticket.tag_id = 2 or tag_ticket.tag_id = 3) GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
|
||||
public function testdateRangeFilter() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'dateRange' => null
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'dateRange' => [1,2]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.date >= 1 and ticket.date <= 2) GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
|
||||
public function testDepartmentsFilter() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'departments' => null,
|
||||
'allowedDepartments' => [
|
||||
[
|
||||
'id' => 2
|
||||
],
|
||||
[
|
||||
'id' => 1
|
||||
],
|
||||
[
|
||||
'id' => 3
|
||||
]
|
||||
]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 2 or ticket.department_id = 1 or ticket.department_id = 3) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'departments' => [1],
|
||||
'allowedDepartments' => [
|
||||
[
|
||||
'id' => 2
|
||||
],
|
||||
[
|
||||
'id' => 1
|
||||
],
|
||||
[
|
||||
'id' => 3
|
||||
]
|
||||
]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 1) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'departments' => [1,2,3],
|
||||
'allowedDepartments' => [
|
||||
[
|
||||
'id' => 2
|
||||
],
|
||||
[
|
||||
'id' => 1
|
||||
],
|
||||
[
|
||||
'id' => 3
|
||||
]
|
||||
]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 1 or ticket.department_id = 2 or ticket.department_id = 3) GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
|
||||
public function testAuthorsFilter() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'authors' => null
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'authors' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'staff' => 1
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'staff' => 0
|
||||
]
|
||||
]
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.author_staff_id = 1 or ticket.author_id = 2) GROUP BY ticket.id'
|
||||
);
|
||||
}
|
||||
|
||||
public function testQueryFilter() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'query' => null
|
||||
]),
|
||||
'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQuery([
|
||||
'query' => 'hello world'
|
||||
]),
|
||||
"FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) ) GROUP BY ticket.id"
|
||||
|
||||
);
|
||||
}
|
||||
public function testQueryWithOrder() {
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQueryWithOrder([
|
||||
'page' => 1
|
||||
]),
|
||||
"SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0"
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQueryWithOrder([
|
||||
'page' => 1,
|
||||
'query' => 'stark'
|
||||
]),
|
||||
"SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE :query) THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE :query) THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE :query) THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0"
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->searchController->getSQLQueryWithOrder([
|
||||
'page' => 1,
|
||||
'orderBy' => ['value' => 'closed', 'asc' => 1]
|
||||
]),
|
||||
"SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -35,9 +35,9 @@ class LoginControllerTest extends TestCase {
|
|||
|
||||
public function testShouldCreateSessionAndRespondSuccessIfCredentialsAreValid() {
|
||||
Session::mockInstanceFunction('sessionExists', \Mock::stub()->returns(false));
|
||||
Controller::useValueReturn();
|
||||
|
||||
$this->loginController->handler();
|
||||
|
||||
$this->assertTrue(!!Session::getInstance()->createSession->hasBeenCalledWithArgs('MOCK_ID', false));
|
||||
$this->assertTrue(Response::get('respondSuccess')->hasBeenCalledWithArgs(array(
|
||||
'userId' => 'MOCK_ID',
|
||||
|
|
|
@ -33,7 +33,7 @@ require './ticket/change-department.rb'
|
|||
require './ticket/close.rb'
|
||||
require './ticket/re-open.rb'
|
||||
require './ticket/delete.rb'
|
||||
require './staff/add.rb'
|
||||
require './staff/invite.rb'
|
||||
require './staff/get.rb'
|
||||
require './staff/edit.rb'
|
||||
require './staff/delete.rb'
|
||||
|
@ -71,4 +71,5 @@ require './ticket/add-tag.rb'
|
|||
require './ticket/delete-tag.rb'
|
||||
require './ticket/edit-comment.rb'
|
||||
require './system/disable-user-system.rb'
|
||||
require './ticket/search.rb'
|
||||
# require './system/get-stats.rb'
|
||||
|
|
|
@ -16,25 +16,33 @@ class Scripts
|
|||
})
|
||||
end
|
||||
|
||||
def self.createStaff(email, password, name, level='1')
|
||||
def self.createStaff(email, password, name, level='1') # WARNING: NOT USED ANYWHERE
|
||||
departments = request('/system/get-settings', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})['data']['departments']
|
||||
departments = departments.collect { |x| x.id }
|
||||
|
||||
response = request('/staff/add', {
|
||||
response = request('/staff/invite', {
|
||||
:name => name,
|
||||
:email => email,
|
||||
:password => password,
|
||||
:level => level,
|
||||
:departments => departments.to_string
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', email, 'email')
|
||||
|
||||
response = request('/user/recover-password', {
|
||||
email: email,
|
||||
password: password,
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
if response['status'] === 'fail'
|
||||
raise response['message']
|
||||
end
|
||||
end
|
||||
|
||||
def self.deleteStaff(staffId)
|
||||
response = request('/staff/delete', {
|
||||
staffId: staffId,
|
||||
|
@ -107,6 +115,7 @@ class Scripts
|
|||
description: description
|
||||
})
|
||||
end
|
||||
|
||||
def self.createTag(name, color)
|
||||
request('/ticket/create-tag', {
|
||||
csrf_userid: $csrf_userid,
|
||||
|
@ -115,6 +124,7 @@ class Scripts
|
|||
color: color
|
||||
})
|
||||
end
|
||||
|
||||
def self.assignTicket(ticketnumber)
|
||||
request('/staff/assign-ticket', {
|
||||
ticketNumber: ticketnumber,
|
||||
|
@ -122,6 +132,7 @@ class Scripts
|
|||
csrf_token: $csrf_token
|
||||
})
|
||||
end
|
||||
|
||||
def self.commentTicket(ticketnumber,content)
|
||||
request('/ticket/comment', {
|
||||
content: content,
|
||||
|
|
|
@ -32,17 +32,24 @@ describe'/staff/edit' do
|
|||
end
|
||||
|
||||
it 'should edit own data staff' do
|
||||
request('/staff/add', {
|
||||
request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Arya Stark',
|
||||
password: 'starkpassword',
|
||||
email: 'arya@opensupports.com',
|
||||
level: 1,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'arya@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'arya@opensupports.com',
|
||||
password: 'starkpassword',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
row = $database.getRow('staff', 'arya@opensupports.com', 'email')
|
||||
|
||||
result = request('/staff/edit', {
|
||||
|
|
|
@ -10,14 +10,18 @@ describe'/staff/get-all' do
|
|||
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
result['data'][0]['departments'] = result['data'][0]['departments'].sort_by do |department|
|
||||
department['id'].to_i
|
||||
end
|
||||
|
||||
(result['data'][0]['name']).should.equal('Emilia Clarke')
|
||||
(result['data'][0]['email']).should.equal('staff@opensupports.com')
|
||||
(result['data'][0]['profilePic']).should.equal('')
|
||||
(result['data'][0]['level']).should.equal('3')
|
||||
(result['data'][0]['departments'][0]['id']).should.equal('2')
|
||||
(result['data'][0]['departments'][0]['name']).should.equal('useless private deapartment')
|
||||
(result['data'][0]['departments'][1]['id']).should.equal('1')
|
||||
(result['data'][0]['departments'][1]['name']).should.equal('Help and Support')
|
||||
(result['data'][0]['departments'][0]['id']).should.equal('1')
|
||||
(result['data'][0]['departments'][0]['name']).should.equal('Help and Support')
|
||||
(result['data'][0]['departments'][1]['id']).should.equal('2')
|
||||
(result['data'][0]['departments'][1]['name']).should.equal('useless private deapartment')
|
||||
(result['data'][0]['departments'][2]['id']).should.equal('3')
|
||||
(result['data'][0]['departments'][2]['name']).should.equal('Suggestions')
|
||||
(result['data'][0]['assignedTickets']).should.equal(10)
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
describe'/staff/add' do
|
||||
describe'/staff/invite' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
|
||||
it 'should add staff member' do
|
||||
result= request('/staff/add', {
|
||||
|
||||
result = request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Tyrion Lannister',
|
||||
email: 'tyrion@opensupports.com',
|
||||
password: 'testpassword',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'tyrion@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'tyrion@opensupports.com',
|
||||
password: 'testpassword',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
row = $database.getRow('staff', result['data']['id'], 'id')
|
||||
|
||||
(row['name']).should.equal('Tyrion Lannister')
|
||||
|
@ -27,16 +34,15 @@ describe'/staff/add' do
|
|||
(row['owners']).should.equal('4')
|
||||
|
||||
lastLog = $database.getLastRow('log')
|
||||
(lastLog['type']).should.equal('ADD_STAFF')
|
||||
(lastLog['type']).should.equal('INVITE')
|
||||
|
||||
end
|
||||
it 'should fail if staff member is alrady a staff' do
|
||||
result= request('/staff/add', {
|
||||
result = request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Tyrion Lannister',
|
||||
email: 'tyrion@opensupports.com',
|
||||
password: 'testpassword',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
|
@ -65,6 +65,35 @@ describe'system/disable-user-system' do
|
|||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should be able to comment on ticket as a non-logged user' do
|
||||
result = request('/ticket/create', {
|
||||
title: 'Doubt about Russian language',
|
||||
content: 'Stariy means old in Russian?',
|
||||
departmentId: 1,
|
||||
language: 'en',
|
||||
name: 'Abraham Einstein',
|
||||
email: 'abrahameinstein@opensupports.com'
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
ticketNumber = result['data']['ticketNumber']
|
||||
|
||||
result = request('/ticket/check', {
|
||||
ticketNumber: ticketNumber,
|
||||
email: 'abrahameinstein@opensupports.com',
|
||||
captcha: 'valid'
|
||||
})
|
||||
token = result['data']['token']
|
||||
(result['status']).should.equal('success');
|
||||
|
||||
result = request('/ticket/comment', {
|
||||
content: 'I actually think it is not like that, but anyways, thanks',
|
||||
ticketNumber: ticketNumber,
|
||||
csrf_token: token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should be able to assign and respond tickets' do
|
||||
Scripts.login($staff[:email], $staff[:password], true);
|
||||
ticket = $database.getLastRow('ticket');
|
||||
|
@ -84,6 +113,26 @@ describe'system/disable-user-system' do
|
|||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should be able to get the latest events as admin' do
|
||||
result = request('/staff/last-events', {
|
||||
page: 1,
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data'].size).should.equal(10)
|
||||
end
|
||||
|
||||
it 'should be able to get system logs as admin' do
|
||||
result = request('/system/get-logs', {
|
||||
page: 1,
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data'].size).should.equal(10)
|
||||
end
|
||||
|
||||
it 'should be be able to create a ticket as an admin' do
|
||||
result = request('/ticket/create', {
|
||||
title: 'created by staff with user system disabled',
|
||||
|
@ -128,7 +177,36 @@ describe'system/disable-user-system' do
|
|||
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_DISABLED')
|
||||
end
|
||||
|
||||
it 'should allow staff members to recover their passwords' do
|
||||
request('/user/logout')
|
||||
result = request('/user/send-recover-password', {
|
||||
email: 'jorah@opensupports.com',
|
||||
staff: true
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
token = $database.getLastRow('recoverpassword')['token'];
|
||||
|
||||
result = request('/user/recover-password', {
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 's3cur3p455w0rd',
|
||||
token: token
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data']['staff']).should.equal('1')
|
||||
|
||||
result = request('/user/login', {
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 's3cur3p455w0rd',
|
||||
staff: true
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
(result['data']['userEmail']).should.equal('jorah@opensupports.com')
|
||||
end
|
||||
|
||||
it 'should enable the user system' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
result = request('/system/enable-user-system', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
|
@ -143,7 +221,6 @@ describe'system/disable-user-system' do
|
|||
numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" )
|
||||
|
||||
(numberOftickets.num_rows).should.equal(53)
|
||||
|
||||
end
|
||||
|
||||
it 'should not enable the user system' do
|
||||
|
@ -155,6 +232,5 @@ describe'system/disable-user-system' do
|
|||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('SYSTEM_USER_IS_ALREADY_ENABLED')
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
describe '/system/get-settings' do
|
||||
it 'should return correct values' do
|
||||
result = request('/system/get-settings')
|
||||
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(result['data']['language']).should.equal('en')
|
||||
(result['data']['departments'][0]['name']).should.equal('Help and Support')
|
||||
|
|
|
@ -182,18 +182,28 @@ describe '/ticket/comment/' do
|
|||
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
request('/staff/add', {
|
||||
|
||||
result = request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Jorah mormont',
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 'testpassword',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
(result['status'].should.equal('success'))
|
||||
|
||||
request('/user/logout')
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'jorah@opensupports.com', 'email')
|
||||
request('/user/recover-password', {
|
||||
email: 'jorah@opensupports.com',
|
||||
password: 'testpassword',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
Scripts.login('jorah@opensupports.com', 'testpassword', true)
|
||||
result = request('/ticket/comment', {
|
||||
content: 'some comment content',
|
||||
|
|
|
@ -6,17 +6,24 @@ describe '/ticket/delete' do
|
|||
Scripts.createTicket('ticket_to_delete')
|
||||
ticket = $database.getRow('ticket', 'ticket_to_delete', 'title')
|
||||
|
||||
request('/staff/add', {
|
||||
request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Ned Stark',
|
||||
password: 'headless',
|
||||
email: 'ned@opensupports.com',
|
||||
level: 3,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'ned@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'ned@opensupports.com',
|
||||
password: 'headless',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.login('ned@opensupports.com', 'headless', true)
|
||||
|
||||
|
@ -80,16 +87,24 @@ describe '/ticket/delete' do
|
|||
|
||||
ticket = $database.getRow('ticket', 'ticket_to_delete_4', 'title');
|
||||
|
||||
request('/staff/add', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Joan Chris',
|
||||
password: 'theyaregonnafireme',
|
||||
email: 'uselessstaff@opensupports.com',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
request('/staff/invite', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'Joan Chris',
|
||||
email: 'uselessstaff@opensupports.com',
|
||||
level: 2,
|
||||
profilePic: '',
|
||||
departments: '[1]'
|
||||
})
|
||||
|
||||
recoverpassword = $database.getRow('recoverpassword', 'uselessstaff@opensupports.com', 'email')
|
||||
|
||||
request('/user/recover-password', {
|
||||
email: 'uselessstaff@opensupports.com',
|
||||
password: 'theyaregonnafireme',
|
||||
token: recoverpassword['token']
|
||||
})
|
||||
|
||||
request('/user/logout')
|
||||
|
||||
Scripts.login('uselessstaff@opensupports.com', 'theyaregonnafireme',true)
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
describe '/ticket/search' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
|
||||
|
||||
it 'should fail if the page is invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: -1
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_PAGE')
|
||||
end
|
||||
|
||||
it 'should fail if the tags are invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
tags: "[1,11,111,1111,11111,111111,1111111,11111111]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_TAG_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the closed value is invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
closed: 3
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_CLOSED_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the unreadStaff value is invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
unreadStaff: 3
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_UNREAD_STAFF_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the priority values are invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
priority: "[0,1,5,6]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_PRIORITY_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the priority' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
priority: "[0,1,),hi]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_PRIORITY_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the dateRange values are invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
dateRange: "[11,69,()) ]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_DATE_RANGE_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the departments are invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
departments: "[-1,-2,99]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_DEPARTMENT_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the authors are invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
authors: "[{id:30001, staff: 1},{id:30,staff: 3}]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_AUTHOR_FILTER')
|
||||
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
authors: "[{id:'delete all)', staff: 1},{id:30,staff: 3}]"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_AUTHOR_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the assigned value is invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
assigned: 3
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_ASSIGNED_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the assigned value is invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
assigned: 11113
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_ASSIGNED_FILTER')
|
||||
end
|
||||
|
||||
it 'should fail if the orderBy values are invalid' do
|
||||
result = request('/ticket/search', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
page: 1,
|
||||
orderBy: "{value: 'closeddd', asc: 11}"
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_ORDER_BY')
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue