mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-29 16:54:53 +02:00
Merge branch 'master' into master
This commit is contained in:
commit
179252303c
@ -19,6 +19,7 @@ class ActivityRow extends React.Component {
|
||||
'RE_OPEN',
|
||||
'DEPARTMENT_CHANGED',
|
||||
'PRIORITY_CHANGED',
|
||||
'EDIT_TITLE',
|
||||
'EDIT_COMMENT',
|
||||
|
||||
'EDIT_SETTINGS',
|
||||
@ -60,6 +61,8 @@ class ActivityRow extends React.Component {
|
||||
'DEPARTMENT_CHANGED',
|
||||
'PRIORITY_CHANGED',
|
||||
'COMMENT_EDITED',
|
||||
'EDIT_TITLE',
|
||||
'EDIT_COMMENT',
|
||||
];
|
||||
|
||||
return (
|
||||
@ -113,6 +116,7 @@ class ActivityRow extends React.Component {
|
||||
'RE_OPEN': 'unlock-alt',
|
||||
'DEPARTMENT_CHANGED': 'exchange',
|
||||
'PRIORITY_CHANGED': 'exclamation',
|
||||
'EDIT_TITLE': 'edit',
|
||||
'EDIT_COMMENT': 'edit',
|
||||
|
||||
'EDIT_SETTINGS': 'wrench',
|
||||
|
90
client/src/app-components/ticket-query-list.js
Normal file
90
client/src/app-components/ticket-query-list.js
Normal file
@ -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);
|
@ -8,6 +8,7 @@ import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
import SessionStore from 'lib-app/session-store';
|
||||
import MentionsParser from 'lib-app/mentions-parser';
|
||||
import history from 'lib-app/history';
|
||||
|
||||
import TicketEvent from 'app-components/ticket-event';
|
||||
import AreYouSure from 'app-components/are-you-sure';
|
||||
@ -60,6 +61,9 @@ class TicketViewer extends React.Component {
|
||||
edit: false,
|
||||
editId: 0,
|
||||
tagSelectorLoading: false,
|
||||
editTitle: false,
|
||||
newTitle: this.props.ticket.title,
|
||||
editTitleError: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -72,13 +76,7 @@ class TicketViewer extends React.Component {
|
||||
const ticket = this.props.ticket;
|
||||
return (
|
||||
<div className="ticket-viewer">
|
||||
<div className="ticket-viewer__header row">
|
||||
<span className="ticket-viewer__number">#{ticket.ticketNumber}</span>
|
||||
<span className="ticket-viewer__title">{ticket.title}</span>
|
||||
<span className="ticket-viewer__flag">
|
||||
<Icon name={(ticket.language === 'en') ? 'us' : ticket.language}/>
|
||||
</span>
|
||||
</div>
|
||||
{this.state.editTitle ? this.renderEditableTitle() : this.renderTitleHeader()}
|
||||
{this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
|
||||
<div className="ticket-viewer__content">
|
||||
<TicketEvent
|
||||
@ -105,6 +103,50 @@ class TicketViewer extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderTitleHeader() {
|
||||
const {ticket, userStaff, userId} = this.props;
|
||||
const {ticketNumber, title, author, editedTitle, language} = ticket;
|
||||
|
||||
return(
|
||||
<div className="ticket-viewer__header row">
|
||||
<span className="ticket-viewer__number">#{ticketNumber}</span>
|
||||
<span className="ticket-viewer__title">{title}</span>
|
||||
<span className="ticket-viewer__flag">
|
||||
<Icon name={(language === 'en') ? 'us' : language}/>
|
||||
</span>
|
||||
{((author.id == userId && author.staff == userStaff) || userStaff) ? this.renderEditTitleOption() : null}
|
||||
{editedTitle ? this.renderEditedTitleText() : null }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditedTitleText(){
|
||||
return(
|
||||
<div className="ticket-viewer__edited-title-text"> {i18n('TITLE_EDITED')} </div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditTitleOption() {
|
||||
return(
|
||||
<span className="ticket-viewer__edit-title-icon">
|
||||
<Icon name="pencil" onClick={() => this.setState({editTitle: true})} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditableTitle(){
|
||||
return(
|
||||
<div className="ticket-viewer__header row">
|
||||
<div className="ticket-viewer__edit-title-box">
|
||||
<FormField className="ticket-viewer___input-edit-title" error={this.state.editTitleError} value={this.state.newTitle} field='input' onChange={(e) => this.setState({newTitle: e.target.value })} />
|
||||
</div>
|
||||
<Button type='secondary' size="extra-small" onClick={this.changeTitle.bind(this)}>
|
||||
{i18n('EDIT_TITLE')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditableHeaders() {
|
||||
const ticket = this.props.ticket;
|
||||
const departments = this.getDepartmentsForTransfer();
|
||||
@ -403,6 +445,26 @@ class TicketViewer extends React.Component {
|
||||
AreYouSure.openModal(null, this.deleteTicket.bind(this));
|
||||
}
|
||||
|
||||
changeTitle(){
|
||||
API.call({
|
||||
path: '/ticket/edit-title',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber,
|
||||
title: this.state.newTitle
|
||||
}
|
||||
}).then(() => {
|
||||
this.setState({
|
||||
editTitle: false,
|
||||
editTitleError: false
|
||||
});
|
||||
this.onTicketModification();
|
||||
}).catch((result) => {
|
||||
this.setState({
|
||||
editTitleError: i18n(result.message)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
reopenTicket() {
|
||||
API.call({
|
||||
path: '/ticket/re-open',
|
||||
@ -427,7 +489,10 @@ class TicketViewer extends React.Component {
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this));
|
||||
}).then((result) => {
|
||||
this.onTicketModification(result);
|
||||
history.push('/admin/panel/tickets/my-tickets/');
|
||||
});
|
||||
}
|
||||
|
||||
changeDepartment(index) {
|
||||
@ -640,7 +705,6 @@ class TicketViewer extends React.Component {
|
||||
}
|
||||
|
||||
export default connect((store) => {
|
||||
|
||||
return {
|
||||
userId: store.session.userId,
|
||||
userStaff: store.session.staff,
|
||||
|
@ -9,6 +9,42 @@
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
padding: 6px 0;
|
||||
display: flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
position: relative;
|
||||
&:hover {
|
||||
.ticket-viewer__edit-title-icon {
|
||||
color: $grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__edited-title-text {
|
||||
font-style: italic;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&__edit-title-icon {
|
||||
position: absolute;
|
||||
color: #414A59;
|
||||
right: 12px;
|
||||
&:hover {
|
||||
cursor:pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&___input-edit-title {
|
||||
color: black;
|
||||
align-items:center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
margin-right: 6px;
|
||||
|
||||
.input__text {
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__number {
|
||||
|
@ -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,7 +90,23 @@ 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() {
|
||||
const customLists = this.getCustomlists();
|
||||
|
||||
return this.getItemsByFilteredByLevel(_.without([
|
||||
{
|
||||
groupName: i18n('DASHBOARD'),
|
||||
@ -135,7 +151,8 @@ class AdminPanelMenu extends React.Component {
|
||||
name: i18n('CUSTOM_RESPONSES'),
|
||||
path: '/admin/panel/tickets/custom-responses',
|
||||
level: 2
|
||||
}
|
||||
},
|
||||
...customLists
|
||||
])
|
||||
},
|
||||
this.props.config['user-system-enabled'] ? {
|
||||
|
@ -153,7 +153,7 @@ class AdminPanelAdvancedSettings extends React.Component {
|
||||
ModalContainer.closeModal();
|
||||
API.call({
|
||||
path: '/system/add-api-key',
|
||||
data: {name}
|
||||
data: {name, type: 'REGISTRATION'}
|
||||
}).then(this.getAllKeys.bind(this));
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@ class AdminPanelAdvancedSettings extends React.Component {
|
||||
|
||||
onRetrieveSuccess(result) {
|
||||
this.setState({
|
||||
APIKeys: result.data,
|
||||
APIKeys: result.data.filter(key => key['type'] === 'REGISTRATION'),
|
||||
selectedAPIKey: -1
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
@ -91,9 +91,11 @@ export default {
|
||||
'BAN_EMAIL': 'Ban email',
|
||||
'EDIT_EMAIL': 'Edit email',
|
||||
'EDIT_PASSWORD': 'Edit password',
|
||||
'EDIT_TITLE': 'Edit title',
|
||||
'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...',
|
||||
@ -232,7 +234,7 @@ export default {
|
||||
'ACTIVITY_DEPARTMENT_CHANGED': 'changed department of ticket',
|
||||
'ACTIVITY_PRIORITY_CHANGED': 'changed priority of ticket',
|
||||
'ACTIVITY_EDIT_COMMENT': 'edited a comment of ticket',
|
||||
|
||||
'ACTIVITY_EDIT_TITLE': 'edited title of ticket',
|
||||
'ACTIVITY_EDIT_SETTINGS': 'edited settings',
|
||||
'ACTIVITY_SIGNUP': 'signed up',
|
||||
'ACTIVITY_INVITE': 'invited user',
|
||||
@ -297,6 +299,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.',
|
||||
@ -358,6 +361,7 @@ export default {
|
||||
'TICKET_COMMENT_ERROR': 'An error occurred while trying to add the comment.',
|
||||
'NO_PERMISSION': 'You\'ve no permission to access to this page.',
|
||||
'INVALID_USER': 'User id is invalid',
|
||||
'INVALID_TITLE': 'invalid title',
|
||||
'ERROR_RETRIEVING_TICKETS': 'An error occurred while trying to retrieve tickets.',
|
||||
'ERROR_RETRIEVING_USERS': 'An error occurred while trying to retrieve users.',
|
||||
'ERROR_RETRIEVING_BAN_LIST': 'An error occurred while trying to retrieve the list of banned emails.',
|
||||
@ -406,6 +410,7 @@ export default {
|
||||
'SERVER_CREDENTIALS_WORKING': 'Server credentials are working correctly',
|
||||
'DELETE_CUSTOM_FIELD_SURE': 'Some users may be using this field. Are you sure you want to delete it?',
|
||||
|
||||
'TITLE_EDITED': '(title edited)',
|
||||
'COMMENT_EDITED': '(comment edited)',
|
||||
'LAST_7_DAYS': 'Last 7 days',
|
||||
'LAST_30_DAYS': 'Last 30 days',
|
||||
|
@ -29,8 +29,8 @@ export default {
|
||||
'DATE': 'Data',
|
||||
'RESPOND': 'Odpowiedz',
|
||||
'RESPOND_TICKET': 'Odpowiedź na zgłoszenie',
|
||||
'CLOSE_TICKET': 'blisko bilet',
|
||||
'DELETE_TICKET': 'Usuń bilet',
|
||||
'CLOSE_TICKET': 'Zamknij zgłoszenie',
|
||||
'DELETE_TICKET': 'Usuń zgłoszenie',
|
||||
'NO_ATTACHMENT': 'Bez załącznika',
|
||||
'STAFF': 'Personel',
|
||||
'CUSTOMER': 'Klient',
|
||||
|
@ -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>
|
||||
|
@ -14,10 +14,12 @@ use Respect\Validation\Validator as DataValidator;
|
||||
* @apiPermission staff3
|
||||
*
|
||||
* @apiParam {String} name Name of the new APIKey.
|
||||
* @apiParam {String} type Type of APIKey: "REGISTRATION" or "TICKET_CREATE"
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_NAME
|
||||
* @apiUse NAME_ALREADY_USED
|
||||
* @apiUse INVALID_API_KEY_TYPE
|
||||
*
|
||||
* @apiSuccess {String} data Token of the APIKey.
|
||||
*
|
||||
@ -34,6 +36,10 @@ class AddAPIKeyController extends Controller {
|
||||
'name' => [
|
||||
'validation' => DataValidator::length(2, 55)->alnum(),
|
||||
'error' => ERRORS::INVALID_NAME
|
||||
],
|
||||
'type' => [
|
||||
'validation' => DataValidator::in(APIKey::TYPES),
|
||||
'error' => ERRORS::INVALID_API_KEY_TYPE
|
||||
]
|
||||
]
|
||||
];
|
||||
@ -43,6 +49,7 @@ class AddAPIKeyController extends Controller {
|
||||
$apiInstance = new APIKey();
|
||||
|
||||
$name = Controller::request('name');
|
||||
$type = Controller::request('type');
|
||||
|
||||
$keyInstance = APIKey::getDataStore($name, 'name');
|
||||
|
||||
@ -51,7 +58,8 @@ class AddAPIKeyController extends Controller {
|
||||
|
||||
$apiInstance->setProperties([
|
||||
'name' => $name,
|
||||
'token' => $token
|
||||
'token' => $token,
|
||||
'type' => $type,
|
||||
]);
|
||||
|
||||
$apiInstance->store();
|
||||
|
@ -4,6 +4,7 @@ $ticketControllers->setGroupPath('/ticket');
|
||||
|
||||
$ticketControllers->addController(new CreateController);
|
||||
$ticketControllers->addController(new EditCommentController);
|
||||
$ticketControllers->addController(new EditTitleController);
|
||||
$ticketControllers->addController(new CommentController);
|
||||
$ticketControllers->addController(new TicketGetController);
|
||||
$ticketControllers->addController(new CheckTicketController);
|
||||
@ -23,5 +24,6 @@ $ticketControllers->addController(new DeleteTagController);
|
||||
$ticketControllers->addController(new GetTagsController);
|
||||
$ticketControllers->addController(new AddTagController);
|
||||
$ticketControllers->addController(new RemoveTagController);
|
||||
$ticketControllers->addController(new SearchController);
|
||||
|
||||
$ticketControllers->finalize();
|
||||
|
@ -75,7 +75,7 @@ class CreateController extends Controller {
|
||||
if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) {
|
||||
$validations['permission'] = 'any';
|
||||
$validations['requestData']['captcha'] = [
|
||||
'validation' => DataValidator::captcha(),
|
||||
'validation' => DataValidator::captcha(APIKey::TICKET_CREATE),
|
||||
'error' => ERRORS::INVALID_CAPTCHA
|
||||
];
|
||||
$validations['requestData']['email'] = [
|
||||
|
@ -20,6 +20,7 @@ DataValidator::with('CustomValidations', true);
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_CONTENT
|
||||
* @apiUse INVALID_TOKEN
|
||||
*
|
||||
* @apiSuccess {Object} data Empty object
|
||||
*
|
||||
@ -30,15 +31,39 @@ class EditCommentController extends Controller {
|
||||
const METHOD = 'POST';
|
||||
|
||||
public function validations() {
|
||||
if(Controller::isUserSystemEnabled()){
|
||||
return [
|
||||
'permission' => 'user',
|
||||
'requestData' => [
|
||||
'content' => [
|
||||
'validation' => DataValidator::length(10, 5000),
|
||||
'error' => ERRORS::INVALID_CONTENT
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
]
|
||||
]
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'permission' => 'any',
|
||||
'requestData' => [
|
||||
'content' => [
|
||||
'validation' => DataValidator::length(10, 5000),
|
||||
'error' => ERRORS::INVALID_CONTENT
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
],
|
||||
'csrf_token' => [
|
||||
'validation' => DataValidator::equals(Session::getInstance()->getToken()),
|
||||
'error' => ERRORS::INVALID_TOKEN
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
@ -49,7 +74,7 @@ class EditCommentController extends Controller {
|
||||
$ticketevent = Ticketevent::getTicketEvent(Controller::request('ticketEventId'));
|
||||
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
|
||||
|
||||
if(!Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
|
||||
if(Controller::isUserSystemEnabled() && !Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
|
86
server/controllers/ticket/edit-title.php
Normal file
86
server/controllers/ticket/edit-title.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
use Respect\Validation\Validator as DataValidator;
|
||||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
/**
|
||||
* @api {post} /ticket/edit-title Edit title of a ticket
|
||||
* @apiVersion 4.5.0
|
||||
*
|
||||
* @apiName Edit title
|
||||
*
|
||||
* @apiGroup Ticket
|
||||
*
|
||||
* @apiDescription This path edits the title of a ticket.
|
||||
*
|
||||
* @apiPermission user
|
||||
*
|
||||
* @apiParam {String} title The new title of the ticket.
|
||||
* @apiParam {Number} ticketNumber The number of the ticket.
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_TITLE
|
||||
* @apiUse INVALID_TOKEN
|
||||
*
|
||||
* @apiSuccess {Object} data Empty object
|
||||
*
|
||||
*/
|
||||
|
||||
class EditTitleController extends Controller {
|
||||
const PATH = '/edit-title';
|
||||
const METHOD = 'POST';
|
||||
|
||||
public function validations() {
|
||||
if(Controller::isUserSystemEnabled()){
|
||||
return [
|
||||
'permission' => 'user',
|
||||
'requestData' => [
|
||||
'title' => [
|
||||
'validation' => DataValidator::length(1, 200),
|
||||
'error' => ERRORS::INVALID_TITLE
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::validTicketNumber(),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
]
|
||||
]
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'permission' => 'any',
|
||||
'requestData' => [
|
||||
'title' => [
|
||||
'validation' => DataValidator::length(1, 200),
|
||||
'error' => ERRORS::INVALID_TITLE
|
||||
],
|
||||
'ticketNumber' => [
|
||||
'validation' => DataValidator::validTicketNumber(),
|
||||
'error' => ERRORS::INVALID_TICKET
|
||||
],
|
||||
'csrf_token' => [
|
||||
'validation' => DataValidator::equals(Session::getInstance()->getToken()),
|
||||
'error' => ERRORS::INVALID_TOKEN
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
$user = Controller::getLoggedUser();
|
||||
$newtitle = Controller::request('title');
|
||||
$ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber'));
|
||||
|
||||
if(Controller::isUserSystemEnabled() && !$user->canManageTicket($ticket)) {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
$ticket->title = $newtitle;
|
||||
$ticket->editedTitle = true;
|
||||
$ticket->store();
|
||||
|
||||
$ticketNumber = $ticket->ticketNumber;
|
||||
Log::createLog('EDIT_TITLE', $ticketNumber);
|
||||
|
||||
Response::respondSuccess();
|
||||
}
|
||||
}
|
348
server/controllers/ticket/search.php
Normal file
348
server/controllers/ticket/search.php
Normal file
@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -72,7 +72,7 @@ class SignUpController extends Controller {
|
||||
|
||||
if(!$this->csvImported) {
|
||||
$validations['requestData']['captcha'] = [
|
||||
'validation' => DataValidator::captcha(),
|
||||
'validation' => DataValidator::captcha(APIKey::REGISTRATION),
|
||||
'error' => ERRORS::INVALID_CAPTCHA
|
||||
];
|
||||
}
|
||||
@ -103,6 +103,10 @@ class SignUpController extends Controller {
|
||||
throw new RequestException(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
if(!$apiKey->isNull() && $apiKey->type !== APIKey::REGISTRATION) {
|
||||
throw new RequestException(ERRORS::INVALID_API_KEY_TYPE);
|
||||
}
|
||||
|
||||
$userId = $this->createNewUserAndRetrieveId();
|
||||
|
||||
if(MailSender::getInstance()->isConnected()) {
|
||||
|
@ -251,6 +251,10 @@
|
||||
* @apiDefine INVALID_COLOR
|
||||
* @apiError {String} INVALID_COLOR The color should be in hexadecimal, preceded by a '#'
|
||||
*/
|
||||
/**
|
||||
* @apiDefine INVALID_API_KEY_TYPE
|
||||
* @apiError {String} INVALID_API_KEY_TYPE Api key type is not one of the availables
|
||||
*/
|
||||
|
||||
class ERRORS {
|
||||
const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS';
|
||||
@ -277,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';
|
||||
@ -317,4 +330,5 @@ class ERRORS {
|
||||
const INVALID_CUSTOM_FIELD_OPTION = 'INVALID_CUSTOM_FIELD_OPTION';
|
||||
const UNAVAILABLE_STATS = 'UNAVAILABLE_STATS';
|
||||
const INVALID_COLOR = 'INVALID_COLOR';
|
||||
const INVALID_API_KEY_TYPE = 'INVALID_API_KEY_TYPE';
|
||||
}
|
||||
|
@ -5,12 +5,22 @@ namespace CustomValidations;
|
||||
use Respect\Validation\Rules\AbstractRule;
|
||||
|
||||
class Captcha extends AbstractRule {
|
||||
private $dataStoreName;
|
||||
|
||||
public function __construct($apiKeyType = '') {
|
||||
if (in_array($apiKeyType, \APIKey::TYPES)) {
|
||||
$this->apiKeyType = $apiKeyType;
|
||||
} else if($apiKeyType) {
|
||||
throw new \Exception(\ERRORS::INVALID_API_KEY_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
public function validate($reCaptchaResponse) {
|
||||
$reCaptchaPrivateKey = \Setting::getSetting('recaptcha-private')->getValue();
|
||||
$apiKey = \APIKey::getDataStore(\Controller::request('apiKey'), 'token');
|
||||
|
||||
if (!$reCaptchaPrivateKey || !$apiKey->isNull()) return true;
|
||||
if (!$reCaptchaPrivateKey) return true;
|
||||
if (!$apiKey->isNull() && $apiKey->type === $apiKeyType) return true;
|
||||
|
||||
$reCaptcha = new \ReCaptcha\ReCaptcha($reCaptchaPrivateKey);
|
||||
$reCaptchaValidation = $reCaptcha->verify($reCaptchaResponse, $_SERVER['REMOTE_ADDR']);
|
||||
|
23
server/libs/validations/validAuthorsId.php
Normal file
23
server/libs/validations/validAuthorsId.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
20
server/libs/validations/validDateRange.php
Normal file
20
server/libs/validations/validDateRange.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
20
server/libs/validations/validDepartmentsId.php
Normal file
20
server/libs/validations/validDepartmentsId.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
19
server/libs/validations/validOrderBy.php
Normal file
19
server/libs/validations/validOrderBy.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
19
server/libs/validations/validPriorities.php
Normal file
19
server/libs/validations/validPriorities.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
20
server/libs/validations/validTagsId.php
Normal file
20
server/libs/validations/validTagsId.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -9,18 +9,29 @@
|
||||
|
||||
class APIKey extends DataStore {
|
||||
const TABLE = 'apikey';
|
||||
const REGISTRATION = 'REGISTRATION';
|
||||
const TICKET_CREATE = 'TICKET_CREATE';
|
||||
const TYPES = [APIKey::REGISTRATION, APIKey::TICKET_CREATE];
|
||||
|
||||
public static function getProps() {
|
||||
return [
|
||||
'name',
|
||||
'token'
|
||||
'token',
|
||||
'type'
|
||||
];
|
||||
}
|
||||
|
||||
public function getDefaultProps() {
|
||||
return [
|
||||
'type' => APIKey::REGISTRATION
|
||||
];
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'token' => $this->token
|
||||
'token' => $this->token,
|
||||
'type' => $this->type
|
||||
];
|
||||
}
|
||||
}
|
@ -51,7 +51,8 @@ class Ticket extends DataStore {
|
||||
'authorEmail',
|
||||
'authorName',
|
||||
'sharedTagList',
|
||||
'editedContent'
|
||||
'editedContent',
|
||||
'editedTitle'
|
||||
);
|
||||
}
|
||||
|
||||
@ -132,7 +133,8 @@ class Ticket extends DataStore {
|
||||
'owner' => $this->ownerToArray(),
|
||||
'events' => $minimized ? [] : $this->eventsToArray(),
|
||||
'tags' => $this->sharedTagList->toArray(true),
|
||||
'edited' => $this->editedContent
|
||||
'edited' => $this->editedContent,
|
||||
'editedTitle' => $this->editedTitle
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,10 @@
|
||||
include_once 'tests/__mocks__/NullDataStoreMock.php';
|
||||
|
||||
class APIKey extends \Mock {
|
||||
const REGISTRATION = 'REGISTRATION';
|
||||
const TICKET_CREATE = 'TICKET_CREATE';
|
||||
const TYPES = [APIKey::REGISTRATION, APIKey::TICKET_CREATE];
|
||||
|
||||
public static $functionList = array();
|
||||
|
||||
public static function initStubs() {
|
||||
|
@ -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() {
|
||||
|
292
server/tests/controllers/ticket/searchTest.php
Normal file
292
server/tests/controllers/ticket/searchTest.php
Normal file
@ -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',
|
||||
|
@ -56,11 +56,11 @@ require './system/edit-department.rb'
|
||||
require './system/delete-department.rb'
|
||||
require './staff/last-events.rb'
|
||||
# require './system/mail-templates.rb'
|
||||
require './system/disable-registration.rb'
|
||||
require './system/enable-registration.rb'
|
||||
require './system/add-api-key.rb'
|
||||
require './system/delete-api-key.rb'
|
||||
require './system/get-api-keys.rb'
|
||||
require './system/disable-registration.rb'
|
||||
require './system/enable-registration.rb'
|
||||
require './system/file-upload-download.rb'
|
||||
require './system/csv-import.rb'
|
||||
require './ticket/create-tag.rb'
|
||||
@ -70,5 +70,7 @@ require './ticket/delete-tag.rb'
|
||||
require './ticket/add-tag.rb'
|
||||
require './ticket/delete-tag.rb'
|
||||
require './ticket/edit-comment.rb'
|
||||
require './ticket/edit-title.rb'
|
||||
require './system/disable-user-system.rb'
|
||||
require './ticket/search.rb'
|
||||
# require './system/get-stats.rb'
|
||||
|
@ -97,11 +97,12 @@ class Scripts
|
||||
result['data']
|
||||
end
|
||||
|
||||
def self.createAPIKey(name)
|
||||
def self.createAPIKey(name, type)
|
||||
request('/system/add-api-key', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: name
|
||||
name: name,
|
||||
type: type
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -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)
|
||||
|
@ -6,7 +6,8 @@ describe'system/add-api-key' do
|
||||
result= request('/system/add-api-key', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'new API'
|
||||
name: 'new API',
|
||||
type: 'REGISTRATION'
|
||||
})
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
@ -15,16 +16,29 @@ describe'system/add-api-key' do
|
||||
|
||||
(row['name']).should.equal('new API')
|
||||
(result['data']).should.equal(row['token'])
|
||||
|
||||
end
|
||||
it 'should not add API key' do
|
||||
|
||||
it 'should not add API key if name already used' do
|
||||
result= request('/system/add-api-key', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'new API'
|
||||
name: 'new API',
|
||||
type: 'REGISTRATION'
|
||||
})
|
||||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('NAME_ALREADY_USED')
|
||||
end
|
||||
|
||||
it 'should not add API key if invalid type is used' do
|
||||
result= request('/system/add-api-key', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'new API2',
|
||||
type: 'REGISTRATON'
|
||||
})
|
||||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_API_KEY_TYPE')
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,7 @@
|
||||
describe'/system/disable-registration' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
api_key = Scripts.createAPIKey('registrationKey', 'REGISTRATION')['data']
|
||||
|
||||
it 'should not disable registration if password is not correct' do
|
||||
result= request('/system/disable-registration', {
|
||||
@ -31,13 +32,23 @@ describe'/system/disable-registration' do
|
||||
end
|
||||
|
||||
it 'should not create user in database if registration is false' do
|
||||
response = request('/user/signup', {
|
||||
result = request('/user/signup', {
|
||||
:name => 'ponzio',
|
||||
:email => 'jc@ponziolandia.com',
|
||||
:password => 'tequila'
|
||||
})
|
||||
|
||||
(response['status']).should.equal('fail')
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('NO_PERMISSION')
|
||||
end
|
||||
|
||||
it 'should create user if using api key' do
|
||||
result = request('/user/signup', {
|
||||
:name => 'ponzio',
|
||||
:email => 'jc@ponziolandia.com',
|
||||
:password => 'tequila',
|
||||
:apiKey => api_key
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
end
|
||||
end
|
||||
|
@ -19,7 +19,7 @@ describe'system/disable-user-system' do
|
||||
|
||||
numberOftickets = $database.query("SELECT * FROM ticket WHERE author_id IS NULL AND author_email IS NOT NULL AND author_name IS NOT NULL")
|
||||
|
||||
(numberOftickets.num_rows).should.equal(51)
|
||||
(numberOftickets.num_rows).should.equal(52)
|
||||
|
||||
request('/user/logout')
|
||||
|
||||
@ -148,6 +148,21 @@ describe'system/disable-user-system' do
|
||||
(ticket['author_staff_id']).should.equal('1')
|
||||
end
|
||||
|
||||
it 'should be able to create a ticket using api' do
|
||||
api_key = Scripts.createAPIKey('ticketCreateKey', 'TICKET_CREATE')['data']
|
||||
request('/user/logout')
|
||||
result = request('/ticket/create', {
|
||||
email: 'fromapi@testemail.com',
|
||||
name: 'Random user',
|
||||
title: 'created by api',
|
||||
content: 'this ticket was created using anapi key while user system is disabled',
|
||||
departmentId: 1,
|
||||
language: 'en',
|
||||
apiKey: api_key
|
||||
})
|
||||
(result['status']).should.equal('success')
|
||||
end
|
||||
|
||||
it 'should not disable the user system if it is already disabled 'do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
@ -205,7 +220,7 @@ describe'system/disable-user-system' do
|
||||
|
||||
numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" )
|
||||
|
||||
(numberOftickets.num_rows).should.equal(53)
|
||||
(numberOftickets.num_rows).should.equal(55)
|
||||
end
|
||||
|
||||
it 'should not enable the user system' do
|
||||
|
@ -3,11 +3,11 @@ describe'system/get-api-keys' do
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
|
||||
it 'should get all API keys' do
|
||||
Scripts.createAPIKey('namekey1')
|
||||
Scripts.createAPIKey('namekey2')
|
||||
Scripts.createAPIKey('namekey3')
|
||||
Scripts.createAPIKey('namekey4')
|
||||
Scripts.createAPIKey('namekey5')
|
||||
Scripts.createAPIKey('namekey1', 'REGISTRATION')
|
||||
Scripts.createAPIKey('namekey2', 'REGISTRATION')
|
||||
Scripts.createAPIKey('namekey3', 'REGISTRATION')
|
||||
Scripts.createAPIKey('namekey4', 'REGISTRATION')
|
||||
Scripts.createAPIKey('namekey5', 'REGISTRATION')
|
||||
|
||||
result = request('/system/get-api-keys', {
|
||||
csrf_userid: $csrf_userid,
|
||||
@ -22,5 +22,4 @@ describe'system/get-api-keys' do
|
||||
(result['data'][4]['name']).should.equal('namekey5')
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
76
tests/ticket/edit-title.rb
Normal file
76
tests/ticket/edit-title.rb
Normal file
@ -0,0 +1,76 @@
|
||||
describe '/ticket/edit-title' do
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.login();
|
||||
Scripts.createTicket('Valar Morghulis','content of the ticket made by an user')
|
||||
ticket = $database.getRow('ticket', 'Valar Morghulis', 'title')
|
||||
ticketNumber = ticket['ticket_number']
|
||||
|
||||
it 'should fail change title of the ticket if the title is invalid' do
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: '',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
|
||||
ticket = $database.getRow('ticket', ticketNumber, 'ticket_number')
|
||||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_TITLE')
|
||||
end
|
||||
|
||||
it 'should change title of the ticket if the author user tries it' do
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: 'Valar dohaeris',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
|
||||
ticket = $database.getRow('ticket', ticketNumber, 'ticket_number')
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(ticket['title']).should.equal('Valar dohaeris')
|
||||
(ticket['edited_title']).should.equal('1')
|
||||
end
|
||||
|
||||
it 'should change the title of the ticket if staff is logged' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: 'Valar dohaeris by Staff',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
|
||||
ticket = $database.getRow('ticket', ticketNumber, 'ticket_number')
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(ticket['title']).should.equal('Valar dohaeris by Staff')
|
||||
(ticket['edited_title']).should.equal('1')
|
||||
|
||||
end
|
||||
|
||||
it 'should not change the title if the user is not the author' do
|
||||
request('/user/logout')
|
||||
Scripts.login($staff[:email], $staff[:password], true)
|
||||
Scripts.createTicket('Winterfell')
|
||||
ticket = $database.getRow('ticket', 'Winterfell', 'title')
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.login()
|
||||
|
||||
result = request('/ticket/edit-title', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
title: 'Casterly Rock',
|
||||
ticketNumber: ticket['ticket_number']
|
||||
})
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('NO_PERMISSION')
|
||||
end
|
||||
|
||||
end
|
145
tests/ticket/search.rb
Normal file
145
tests/ticket/search.rb
Normal file
@ -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…
x
Reference in New Issue
Block a user