commit
cc2fc2c295
|
@ -3,7 +3,7 @@ const TicketInfo = ReactMock();
|
|||
const Table = ReactMock();
|
||||
const Button = ReactMock();
|
||||
const Tooltip = ReactMock();
|
||||
const Dropdown = ReactMock();
|
||||
const DepartmentDropdown = ReactMock();
|
||||
const i18n = stub().returnsArg(0);
|
||||
|
||||
const TicketList = requireUnit('app-components/ticket-list', {
|
||||
|
@ -11,7 +11,7 @@ const TicketList = requireUnit('app-components/ticket-list', {
|
|||
'core-components/table': Table,
|
||||
'core-components/button': Button,
|
||||
'core-components/tooltip': Tooltip,
|
||||
'core-components/drop-down': Dropdown,
|
||||
'app-components/department-dropdown': DepartmentDropdown,
|
||||
'lib-app/i18n': i18n
|
||||
});
|
||||
|
||||
|
@ -54,7 +54,7 @@ describe('TicketList component', function () {
|
|||
);
|
||||
|
||||
table = TestUtils.scryRenderedComponentsWithType(ticketList, Table);
|
||||
dropdown = TestUtils.scryRenderedComponentsWithType(ticketList, Dropdown);
|
||||
dropdown = TestUtils.scryRenderedComponentsWithType(ticketList, DepartmentDropdown);
|
||||
}
|
||||
|
||||
it('should pass correct props to Table', function () {
|
||||
|
@ -84,12 +84,12 @@ describe('TicketList component', function () {
|
|||
}
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should pass loading to Table', function () {
|
||||
renderTicketList({loading: true});
|
||||
expect(table[0].props.loading).to.equal(true);
|
||||
});
|
||||
|
||||
|
||||
it('should pass correct compare function to Table', function () {
|
||||
let minCompare = table[0].props.comp;
|
||||
|
||||
|
@ -114,7 +114,7 @@ describe('TicketList component', function () {
|
|||
row2.date = '20160401';
|
||||
expect(minCompare(row1, row2)).to.equal(-1);
|
||||
});
|
||||
|
||||
|
||||
describe('when using secondary type', function () {
|
||||
beforeEach(function () {
|
||||
renderTicketList({
|
||||
|
@ -162,10 +162,10 @@ describe('TicketList component', function () {
|
|||
});
|
||||
|
||||
it('should pass correct props to dropdown', function () {
|
||||
expect(dropdown[0].props.items).to.deep.equal([
|
||||
{content: i18n('ALL_DEPARTMENTS')},
|
||||
{content: 'Sales Support'},
|
||||
{content: 'Tech Help'}
|
||||
expect(dropdown[0].props.departments).to.deep.equal([
|
||||
{name: i18n('ALL_DEPARTMENTS')},
|
||||
{name: 'Sales Support', id: 1},
|
||||
{name: 'Tech Help', id: 2}
|
||||
]);
|
||||
expect(dropdown[0].props.size).to.equal('medium');
|
||||
});
|
||||
|
@ -185,4 +185,4 @@ describe('TicketList component', function () {
|
|||
expect(table[0].props.rows.length).to.equal(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import DropDown from 'core-components/drop-down';
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class DepartmentDropdown extends React.Component {
|
||||
static propTypes = {
|
||||
value: React.PropTypes.number,
|
||||
onChange: React.PropTypes.func,
|
||||
departments: React.PropTypes.array
|
||||
}
|
||||
render(){
|
||||
return <DropDown {...this.props} items={this.getDepartments()} />
|
||||
}
|
||||
|
||||
getDepartments(){
|
||||
let departments = this.props.departments.map((department) => {
|
||||
if(department.private*1) {
|
||||
return {content: <span>{department.name} <Icon name='user-secret'/></span>};
|
||||
}else{
|
||||
return {content: department.name};
|
||||
}
|
||||
});
|
||||
|
||||
return departments;
|
||||
}
|
||||
}
|
||||
|
||||
export default DepartmentDropdown;
|
|
@ -5,10 +5,11 @@ import i18n from 'lib-app/i18n';
|
|||
import DateTransformer from 'lib-core/date-transformer';
|
||||
|
||||
import TicketInfo from 'app-components/ticket-info';
|
||||
import DepartmentDropdown from 'app-components/department-dropdown';
|
||||
import Table from 'core-components/table';
|
||||
import Button from 'core-components/button';
|
||||
import Tooltip from 'core-components/tooltip';
|
||||
import DropDown from 'core-components/drop-down';
|
||||
import Icon from 'core-components/icon';
|
||||
import Checkbox from 'core-components/checkbox';
|
||||
|
||||
class TicketList extends React.Component {
|
||||
|
@ -44,7 +45,7 @@ class TicketList extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="ticket-list">
|
||||
<div className="ticket-list__filters">
|
||||
<div className="ticket-list__filters">
|
||||
{(this.props.type === 'secondary' && this.props.showDepartmentDropdown) ? this.renderDepartmentsDropDown() : null}
|
||||
{this.props.onClosedTicketsShownChange ? this.renderFilterCheckbox() : null}
|
||||
</div>
|
||||
|
@ -61,14 +62,14 @@ class TicketList extends React.Component {
|
|||
renderDepartmentsDropDown() {
|
||||
return (
|
||||
<div className="ticket-list__department-selector">
|
||||
<DropDown {...this.getDepartmentDropdownProps()} />
|
||||
<DepartmentDropdown {...this.getDepartmentDropdownProps()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getDepartmentDropdownProps() {
|
||||
return {
|
||||
items: this.getDepartments(),
|
||||
departments: this.getDepartments(),
|
||||
onChange: (event) => {
|
||||
this.setState({
|
||||
selectedDepartment: event.index && this.props.departments[event.index - 1].id
|
||||
|
@ -92,12 +93,10 @@ class TicketList extends React.Component {
|
|||
}
|
||||
|
||||
getDepartments() {
|
||||
let departments = this.props.departments.map((department) => {
|
||||
return {content: department.name};
|
||||
});
|
||||
let departments = _.clone(this.props.departments);
|
||||
|
||||
departments.unshift({
|
||||
content: i18n('ALL_DEPARTMENTS')
|
||||
name: i18n('ALL_DEPARTMENTS')
|
||||
});
|
||||
|
||||
return departments;
|
||||
|
|
|
@ -21,6 +21,7 @@ import Message from 'core-components/message';
|
|||
import Icon from 'core-components/icon';
|
||||
import TextEditor from 'core-components/text-editor';
|
||||
import InfoTooltip from 'core-components/info-tooltip';
|
||||
import DepartmentDropdown from 'app-components/department-dropdown';
|
||||
|
||||
class TicketViewer extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -86,7 +87,8 @@ class TicketViewer extends React.Component {
|
|||
|
||||
renderEditableHeaders() {
|
||||
const ticket = this.props.ticket;
|
||||
const departments = SessionStore.getDepartments();
|
||||
const departments = (this.props.ticket.author.staff ? SessionStore.getDepartments() : this.getPublicDepartments() );
|
||||
|
||||
const priorities = {
|
||||
'low': 0,
|
||||
'medium': 1,
|
||||
|
@ -107,8 +109,8 @@ class TicketViewer extends React.Component {
|
|||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="col-md-4">
|
||||
<DropDown className="ticket-viewer__editable-dropdown"
|
||||
items={departments.map((department) => {return {content: department.name}})}
|
||||
<DepartmentDropdown className="ticket-viewer__editable-dropdown"
|
||||
departments={departments}
|
||||
selectedIndex={_.findIndex(departments, {id: this.props.ticket.department.id})}
|
||||
onChange={this.onDepartmentDropdownChanged.bind(this)} />
|
||||
</div>
|
||||
|
@ -303,6 +305,9 @@ class TicketViewer extends React.Component {
|
|||
}
|
||||
};
|
||||
}
|
||||
getPublicDepartments() {
|
||||
return _.filter(SessionStore.getDepartments(),d => !(d.private*1));
|
||||
}
|
||||
|
||||
onDepartmentDropdownChanged(event) {
|
||||
AreYouSure.openModal(null, this.changeDepartment.bind(this, event.index));
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import React from 'react';
|
||||
import store from 'app/store';
|
||||
|
||||
import ConfigActions from 'actions/config-actions';
|
||||
|
||||
import MainLayout from 'app/main/main-layout';
|
||||
import AdminPanelStaffWidget from 'app/admin/panel/admin-panel-staff-widget';
|
||||
|
@ -8,6 +11,10 @@ import Widget from 'core-components/widget';
|
|||
|
||||
class AdminPanel extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
store.dispatch(ConfigActions.updateData());
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<MainLayout>
|
||||
|
@ -33,4 +40,4 @@ class AdminPanel extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default AdminPanel;
|
||||
export default AdminPanel;
|
||||
|
|
|
@ -10,6 +10,7 @@ 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 AddStaffModal extends React.Component {
|
||||
|
||||
|
@ -63,7 +64,13 @@ class AddStaffModal extends React.Component {
|
|||
}
|
||||
|
||||
getDepartments() {
|
||||
return SessionStore.getDepartments().map(department => department.name);
|
||||
return SessionStore.getDepartments().map(department => {
|
||||
if(department.private*1){
|
||||
return <spam>{department.name} <Icon name='user-secret'/> </spam>
|
||||
}else {
|
||||
return department.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(form) {
|
||||
|
@ -112,4 +119,4 @@ class AddStaffModal extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default AddStaffModal;
|
||||
export default AddStaffModal;
|
||||
|
|
|
@ -16,6 +16,7 @@ import Form from 'core-components/form';
|
|||
import FormField from 'core-components/form-field';
|
||||
import SubmitButton from 'core-components/submit-button';
|
||||
import DropDown from 'core-components/drop-down';
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class AdminPanelDepartments extends React.Component {
|
||||
static defaultProps = {
|
||||
|
@ -44,7 +45,13 @@ class AdminPanelDepartments extends React.Component {
|
|||
</div>
|
||||
<div className="col-md-8">
|
||||
<Form {...this.getFormProps()}>
|
||||
<FormField label={i18n('NAME')} name="name" validation="NAME" required fieldProps={{size: 'large'}}/>
|
||||
<div>
|
||||
<FormField className="admin-panel-departments__name" label={i18n('NAME')} name="name" validation="NAME" required fieldProps={{size: 'large'}}/>
|
||||
<div className="admin-panel-departments__private-option">
|
||||
<FormField label={i18n('PRIVATE')} name="private" field="checkbox"/>
|
||||
<InfoTooltip className="admin-panel-departments__info-tooltip" text={i18n('PRIVATE_DEPARTMENT_DESCRIPTION')} />
|
||||
</div>
|
||||
</div>
|
||||
<SubmitButton size="medium" className="admin-panel-departments__update-name-button" type="secondary">
|
||||
{i18n((this.state.selectedIndex !== -1) ? 'UPDATE_DEPARTMENT' : 'ADD_DEPARTMENT')}
|
||||
</SubmitButton>
|
||||
|
@ -100,6 +107,7 @@ class AdminPanelDepartments extends React.Component {
|
|||
content: (
|
||||
<span>
|
||||
{department.name}
|
||||
{department.private*1 ? <Icon className="admin-panel-departments__private-icon" name='user-secret'/> : null }
|
||||
{(!department.owners) ? (
|
||||
<span className="admin-panel-departments__warning">
|
||||
<InfoTooltip type="warning" text={i18n('NO_STAFF_ASSIGNED')}/>
|
||||
|
@ -143,7 +151,8 @@ class AdminPanelDepartments extends React.Component {
|
|||
path: '/system/edit-department',
|
||||
data: {
|
||||
departmentId: this.getCurrentDepartment().id,
|
||||
name: form.name
|
||||
name: form.name,
|
||||
private: form.private ? 1 : 0
|
||||
}
|
||||
}).then(() => {
|
||||
this.setState({formLoading: false});
|
||||
|
@ -153,7 +162,8 @@ class AdminPanelDepartments extends React.Component {
|
|||
API.call({
|
||||
path: '/system/add-department',
|
||||
data: {
|
||||
name: form.name
|
||||
name: form.name,
|
||||
private: form.private ? 1 : 0
|
||||
}
|
||||
}).then(() => {
|
||||
this.retrieveDepartments();
|
||||
|
@ -222,4 +232,4 @@ export default connect((store) => {
|
|||
return {
|
||||
departments: store.config.departments
|
||||
};
|
||||
})(AdminPanelDepartments);
|
||||
})(AdminPanelDepartments);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
.admin-panel-departments {
|
||||
|
||||
|
||||
&__list {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -12,6 +11,14 @@
|
|||
min-width: 156px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
display:inline-block;margin-right: 101px;
|
||||
}
|
||||
&__private-option {
|
||||
display:inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&__optional-buttons {
|
||||
float: right;
|
||||
}
|
||||
|
@ -39,4 +46,10 @@
|
|||
&__transfer-tickets-drop-down {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
&__private-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
&__info-tooltip {
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import DropDown from 'core-components/drop-down';
|
|||
import Button from 'core-components/button';
|
||||
import Icon from 'core-components/icon';
|
||||
import Loading from 'core-components/loading';
|
||||
import DepartmentDropdown from 'app-components/department-dropdown';
|
||||
|
||||
class AdminPanelStaffMembers extends React.Component {
|
||||
|
||||
|
@ -45,7 +46,7 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
<div className="admin-panel-staff-members">
|
||||
<Header title={i18n('STAFF_MEMBERS')} description={i18n('STAFF_MEMBERS_DESCRIPTION')} />
|
||||
<div className="admin-panel-staff-members__wrapper">
|
||||
<DropDown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
||||
<DepartmentDropdown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
||||
<Button onClick={this.onAddNewStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||
<Icon name="user-plus" className=""/> {i18n('ADD_NEW_STAFF')}
|
||||
</Button>
|
||||
|
@ -61,7 +62,7 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
|
||||
getDepartmentDropdownProps() {
|
||||
return {
|
||||
items: this.getDepartments(),
|
||||
departments: this.getDepartments(),
|
||||
onChange: (event) => {
|
||||
let departments = SessionStore.getDepartments();
|
||||
this.setState({
|
||||
|
@ -97,12 +98,9 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
}
|
||||
|
||||
getDepartments() {
|
||||
let departments = SessionStore.getDepartments().map((department) => {
|
||||
return {content: department.name};
|
||||
});
|
||||
|
||||
let departments = _.clone(SessionStore.getDepartments())
|
||||
departments.unshift({
|
||||
content: i18n('ALL_DEPARTMENTS')
|
||||
name: i18n('ALL_DEPARTMENTS')
|
||||
});
|
||||
|
||||
return departments;
|
||||
|
|
|
@ -23,4 +23,7 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__private {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -256,7 +256,13 @@ class StaffEditor extends React.Component {
|
|||
}
|
||||
|
||||
getDepartments() {
|
||||
return SessionStore.getDepartments().map(department => department.name);
|
||||
return SessionStore.getDepartments().map(department => {
|
||||
if(department.private*1){
|
||||
return <spam> {department.name} <Icon name='user-secret'/> </spam>
|
||||
}else {
|
||||
return department.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getStaffLevelInfo() {
|
||||
|
|
|
@ -8,6 +8,7 @@ import API from 'lib-app/api-call';
|
|||
import SessionStore from 'lib-app/session-store';
|
||||
import LanguageSelector from 'app-components/language-selector';
|
||||
import Captcha from 'app/main/captcha';
|
||||
import DepartmentDropdown from 'app-components/department-dropdown';
|
||||
|
||||
import Header from 'core-components/header';
|
||||
import TextEditor from 'core-components/text-editor';
|
||||
|
@ -48,8 +49,8 @@ class CreateTicketForm extends React.Component {
|
|||
{(!this.props.userLogged) ? this.renderEmailAndName() : null}
|
||||
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}}/>
|
||||
<div className="row">
|
||||
<FormField className="col-md-5" label={i18n('DEPARTMENT')} name="departmentIndex" field="select" fieldProps={{
|
||||
items: SessionStore.getDepartments().map((department) => {return {content: department.name}}),
|
||||
<FormField className="col-md-5" label={i18n('DEPARTMENT')} name="departmentIndex" field="select" decorator={DepartmentDropdown} fieldProps={{
|
||||
departments: SessionStore.getDepartments(),
|
||||
size: 'medium'
|
||||
}} />
|
||||
<FormField className="col-md-5" label={i18n('LANGUAGE')} name="language" field="select" decorator={LanguageSelector} fieldProps={{
|
||||
|
|
|
@ -307,6 +307,7 @@ export default {
|
|||
'DISABLE_USER_DESCRIPTION': 'User will be disabled and will not be able to sign in and create tickets.',
|
||||
'PRIVATE_RESPONSE_DESCRIPTION': 'This response will only be seen by staff members',
|
||||
'PRIVATE_TOPIC_DESCRIPTION': 'This topic will only be seen by staff members',
|
||||
'PRIVATE_DEPARTMENT_DESCRIPTION': 'This department will only be seen by staff members',
|
||||
|
||||
//ERRORS
|
||||
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
||||
|
|
|
@ -103,7 +103,6 @@ class SessionReducer extends Reducer {
|
|||
|
||||
onUserDataRetrieved(state, payload) {
|
||||
let userData = payload.data;
|
||||
|
||||
sessionStore.storeUserData(payload.data);
|
||||
|
||||
return _.extend({}, state, {
|
||||
|
|
|
@ -15,7 +15,7 @@ DataValidator::with('CustomValidations', true);
|
|||
* @apiPermission staff1
|
||||
*
|
||||
* @apiParam {Number} staffId The id of the staff member to be searched.
|
||||
*
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
*
|
||||
* @apiSuccess {Object} data Information about a staff member
|
||||
|
@ -57,7 +57,8 @@ class GetStaffController extends Controller {
|
|||
foreach($departmentList as $department) {
|
||||
$parsedDepartmentList[] = [
|
||||
'id' => $department->id,
|
||||
'name' => $department->name
|
||||
'name' => $department->name,
|
||||
'private' => $department->private
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -72,4 +73,4 @@ class GetStaffController extends Controller {
|
|||
'sendEmailOnNewTicket' => $user->sendEmailOnNewTicket
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,12 @@ class UnAssignStaffController extends Controller {
|
|||
const PATH = '/un-assign-ticket';
|
||||
const METHOD = 'POST';
|
||||
|
||||
private $user;
|
||||
|
||||
public function __construct($user=null) {
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function validations() {
|
||||
return [
|
||||
'permission' => 'staff_1',
|
||||
|
@ -41,7 +47,7 @@ class UnAssignStaffController extends Controller {
|
|||
|
||||
public function handler() {
|
||||
$ticketNumber = Controller::request('ticketNumber');
|
||||
$user = Controller::getLoggedUser();
|
||||
$user = ($this->user? $this->user : Controller::getLoggedUser());
|
||||
$ticket = Ticket::getByTicketNumber($ticketNumber);
|
||||
$owner = $ticket->owner;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use Respect\Validation\Validator as DataValidator;
|
|||
* @apiPermission staff3
|
||||
*
|
||||
* @apiParam {String} name Name of the new department.
|
||||
* @apiParam {Boolean} private Indicates if the deparment is not shown to users.
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
*
|
||||
|
@ -28,17 +29,24 @@ class AddDepartmentController extends Controller {
|
|||
public function validations() {
|
||||
return [
|
||||
'permission' => 'staff_3',
|
||||
'requestData' => []
|
||||
'requestData' => [
|
||||
'name' => [
|
||||
'validation' => DataValidator::length(2, 100),
|
||||
'error' => ERRORS::INVALID_NAME
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function handler() {
|
||||
$name = Controller::request('name');
|
||||
$private = Controller::request('private');
|
||||
|
||||
$departmentInstance = new Department();
|
||||
|
||||
$departmentInstance->setProperties([
|
||||
'name' => $name,
|
||||
'name' => $name ,
|
||||
'private' => $private ? 1 : 0
|
||||
]);
|
||||
$departmentInstance->store();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ DataValidator::with('CustomValidations', true);
|
|||
*
|
||||
* @apiParam {String} name The new name of the department.
|
||||
* @apiParam {Number} departmentId The Id of the department.
|
||||
* @apiParam {Boolean} private Indicates if the department is shown to users;
|
||||
*
|
||||
* @apiUse NO_PERMISSION
|
||||
* @apiUse INVALID_NAME
|
||||
|
@ -33,10 +34,6 @@ class EditDepartmentController extends Controller {
|
|||
return [
|
||||
'permission' => 'staff_3',
|
||||
'requestData' => [
|
||||
'name' => [
|
||||
'validation' => DataValidator::alnum(),
|
||||
'error' => ERRORS::INVALID_NAME
|
||||
],
|
||||
'departmentId' => [
|
||||
'validation' => DataValidator::dataStoreId('department'),
|
||||
'error' => ERRORS::INVALID_DEPARTMENT
|
||||
|
@ -46,18 +43,20 @@ class EditDepartmentController extends Controller {
|
|||
}
|
||||
|
||||
public function handler() {
|
||||
|
||||
$newname = Controller::request('name');
|
||||
$departmentId = Controller::request('departmentId');
|
||||
$private = Controller::request('private');
|
||||
|
||||
$departmentInstance = Department::getDataStore($departmentId);
|
||||
|
||||
$departmentInstance->name = $newname ;
|
||||
|
||||
$newname ? $departmentInstance->name = $newname : null;
|
||||
$departmentInstance->private = $private ? 1 : 0;
|
||||
$departmentInstance->store();
|
||||
|
||||
Log::createLog('EDIT_DEPARTMENT', $departmentInstance->name);
|
||||
|
||||
|
||||
Response::respondSuccess();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class GetSettingsController extends Controller {
|
|||
'smtp-host' => Setting::getSetting('smtp-host')->getValue(),
|
||||
'smtp-user' => Setting::getSetting('smtp-user')->getValue(),
|
||||
'registration' => Setting::getSetting('registration')->getValue(),
|
||||
'departments' => Department::getDepartmentNames(),
|
||||
'departments' => Department::getAllDepartmentNames(),
|
||||
'supportedLanguages' => Language::getSupportedLanguages(),
|
||||
'allowedLanguages' => Language::getAllowedLanguages(),
|
||||
'session-prefix' => Setting::getSetting('session-prefix')->getValue(),
|
||||
|
@ -67,7 +67,7 @@ class GetSettingsController extends Controller {
|
|||
'max-size' => Setting::getSetting('max-size')->getValue(),
|
||||
'title' => Setting::getSetting('title')->getValue(),
|
||||
'registration' => Setting::getSetting('registration')->getValue(),
|
||||
'departments' => Department::getDepartmentNames(),
|
||||
'departments' => Controller::isStaffLogged() ? Department::getAllDepartmentNames() : Department::getPublicDepartmentNames(),
|
||||
'supportedLanguages' => Language::getSupportedLanguages(),
|
||||
'allowedLanguages' => Language::getAllowedLanguages(),
|
||||
'user-system-enabled' => intval(Setting::getSetting('user-system-enabled')->getValue()),
|
||||
|
|
|
@ -52,6 +52,10 @@ class ChangeDepartmentController extends Controller {
|
|||
$department = Department::getDataStore($departmentId);
|
||||
$user = Controller::getLoggedUser();
|
||||
|
||||
if(!$ticket->authorStaffId && $department->private){
|
||||
throw new Exception(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
||||
if($ticket->owner && $ticket->owner->id !== $user->id && $user->level == 1){
|
||||
throw new Exception(ERRORS::NO_PERMISSION);
|
||||
}
|
||||
|
@ -67,8 +71,8 @@ class ChangeDepartmentController extends Controller {
|
|||
$ticket->unread = !$ticket->isAuthor($user);
|
||||
$ticket->store();
|
||||
|
||||
if(!$user->sharedDepartmentList->includesId($department->id)) {
|
||||
$unAssignTicketController = new UnAssignStaffController();
|
||||
if($ticket->owner && !$ticket->owner->sharedDepartmentList->includesId($department->id)) {
|
||||
$unAssignTicketController = new UnAssignStaffController($ticket->owner);
|
||||
$unAssignTicketController->validate();
|
||||
$unAssignTicketController->handler();
|
||||
}
|
||||
|
|
|
@ -99,6 +99,9 @@ class CreateController extends Controller {
|
|||
$this->email = Controller::request('email');
|
||||
$this->name = Controller::request('name');
|
||||
|
||||
if(!Controller::isStaffLogged() && Department::getDataStore($this->departmentId)->private){
|
||||
throw new Exception(ERRORS::INVALID_DEPARTMENT);
|
||||
}
|
||||
$this->storeTicket();
|
||||
|
||||
if(!Controller::isUserSystemEnabled()) {
|
||||
|
|
|
@ -8,26 +8,28 @@ use RedBeanPHP\Facade as RedBean;
|
|||
* @apiParam {Number} id Id of the department.
|
||||
* @apiParam {String} name Name of the department.
|
||||
* @apiParam {[Staff](#api-Data_Structures-ObjectStaff)[]} owners List of owners of the department.
|
||||
* @apiParam {Boolean} private Indicates if the departmetn is not shown to users.
|
||||
*/
|
||||
|
||||
class Department extends DataStore {
|
||||
const TABLE = 'department';
|
||||
|
||||
|
||||
public static function getProps() {
|
||||
return [
|
||||
'name',
|
||||
'sharedTicketList',
|
||||
'owners'
|
||||
];
|
||||
}
|
||||
|
||||
public function getDefaultProps() {
|
||||
return [
|
||||
'owners' => 0
|
||||
'owners',
|
||||
'private'
|
||||
];
|
||||
}
|
||||
|
||||
public static function getDepartmentNames() {
|
||||
public function getDefaultProps() {
|
||||
return [
|
||||
'owners' => 0
|
||||
];
|
||||
}
|
||||
|
||||
public static function getAllDepartmentNames() {
|
||||
$departmentsList = RedBean::findAll(Department::TABLE);
|
||||
$departmentsNameList = [];
|
||||
|
||||
|
@ -35,12 +37,32 @@ class Department extends DataStore {
|
|||
$departmentsNameList[] = [
|
||||
'id' => $department->id,
|
||||
'name' => $department->name,
|
||||
'owners' => $department->owners
|
||||
'owners' => $department->owners,
|
||||
'private' => $department->private
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
return $departmentsNameList;
|
||||
}
|
||||
|
||||
public static function getPublicDepartmentNames() {
|
||||
$departmentsList = RedBean::findAll(Department::TABLE);
|
||||
$departmentsNameList = [];
|
||||
|
||||
foreach($departmentsList as $department) {
|
||||
if(!$department->private) {
|
||||
$departmentsNameList[] = [
|
||||
'id' => $department->id,
|
||||
'name' => $department->name,
|
||||
'owners' => $department->owners,
|
||||
'private' => $department->private
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $departmentsNameList;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
|
@ -48,4 +70,4 @@ class Department extends DataStore {
|
|||
'owners' => $this->owners
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ install:
|
|||
@bundle install
|
||||
|
||||
run: export MYSQL_HOST=127.0.0.1
|
||||
run: export MYSQL_PORT=4040
|
||||
run: export MYSQL_PORT=3306
|
||||
run:
|
||||
./run-tests.sh
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ describe'/staff/get-all' do
|
|||
(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('Suggestions')
|
||||
(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('Tech support')
|
||||
(result['data'][0]['departments'][2]['name']).should.equal('Suggestions')
|
||||
(result['data'][0]['assignedTickets']).should.equal(6)
|
||||
(result['data'][0]['closedTickets']).should.equal(0)
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ describe'system/add-department' do
|
|||
|
||||
row = $database.getRow('department', 4, 'id')
|
||||
|
||||
(row['name']).should.equal('new department')
|
||||
(row['name']).should.equal('Tech support')
|
||||
(row['private']).should.equal("0")
|
||||
|
||||
lastLog = $database.getLastRow('log')
|
||||
(lastLog['type']).should.equal('ADD_DEPARTMENT')
|
||||
|
@ -30,7 +31,25 @@ describe'system/add-department' do
|
|||
|
||||
row = $database.getRow('department', 5, 'id')
|
||||
|
||||
(row['name']).should.equal('<b>new department</b>')
|
||||
(row['name']).should.equal('new department')
|
||||
(row['private']).should.equal("0")
|
||||
|
||||
lastLog = $database.getLastRow('log')
|
||||
(lastLog['type']).should.equal('ADD_DEPARTMENT')
|
||||
end
|
||||
|
||||
it 'should add a private department' do
|
||||
result = request('/system/add-department', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'a private department',
|
||||
private: 1
|
||||
})
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
|
||||
row = $database.getRow('department', 'a private department', 'name')
|
||||
(row['private']).should.equal('1')
|
||||
|
||||
lastLog = $database.getLastRow('log')
|
||||
(lastLog['type']).should.equal('ADD_DEPARTMENT')
|
||||
|
|
|
@ -17,4 +17,4 @@ describe '/system/get-settings' do
|
|||
(result['data']['allowedLanguages'][9]).should.equal('tr')
|
||||
(result['data']['supportedLanguages'][0]).should.equal('en')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ describe '/ticket/change-department' do
|
|||
|
||||
result = request('/ticket/change-department', {
|
||||
ticketNumber: ticket['ticket_number'],
|
||||
departmentId: 2,
|
||||
departmentId: 3,
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})
|
||||
|
@ -38,7 +38,7 @@ describe '/ticket/change-department' do
|
|||
|
||||
ticket = $database.getRow('ticket', 1 , 'id')
|
||||
(ticket['unread']).should.equal('1')
|
||||
(ticket['department_id']).should.equal('2')
|
||||
(ticket['department_id']).should.equal('3')
|
||||
(ticket['owner_id']).should.equal('1')
|
||||
|
||||
lastLog = $database.getLastRow('log')
|
||||
|
|
|
@ -78,8 +78,41 @@ describe '/ticket/create' do
|
|||
(result['message']).should.equal('INVALID_DEPARTMENT')
|
||||
|
||||
end
|
||||
it 'should fail if an user tries to create a ticket with a private department' do
|
||||
request('/user/logout')
|
||||
Scripts.login('staff@opensupports.com', 'staff', true)
|
||||
|
||||
result = request('/system/add-department', {
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token,
|
||||
name: 'useless private deapartment',
|
||||
private: 1
|
||||
})
|
||||
|
||||
row = $database.getRow('department', 'useless private deapartment', 'name')
|
||||
|
||||
request('/user/logout')
|
||||
Scripts.createUser('user@os4.com', 'loginpass')
|
||||
Scripts.login('user@os4.com', 'loginpass')
|
||||
|
||||
result = request('/ticket/create', {
|
||||
title: 'Winter is here',
|
||||
content: 'The king in the north',
|
||||
departmentId: row['id'],
|
||||
language: 'en',
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
})
|
||||
|
||||
(result['status']).should.equal('fail')
|
||||
(result['message']).should.equal('INVALID_DEPARTMENT')
|
||||
|
||||
request('/user/logout')
|
||||
end
|
||||
|
||||
it 'should create ticket if pass data is valid' do
|
||||
Scripts.login('creator@os4.com','creator')
|
||||
|
||||
result = request('/ticket/create', {
|
||||
title: 'Winter is coming',
|
||||
content: 'The north remembers',
|
||||
|
|
|
@ -44,7 +44,7 @@ describe 'Ticket Events' do
|
|||
csrf_token: $csrf_token
|
||||
})
|
||||
request('/ticket/change-department', {
|
||||
departmentId: 2,
|
||||
departmentId: 3,
|
||||
ticketNumber: ticketNumber,
|
||||
csrf_userid: $csrf_userid,
|
||||
csrf_token: $csrf_token
|
||||
|
@ -115,4 +115,4 @@ describe 'Ticket Events' do
|
|||
(result['data']['events'][8]['author']['name']).should.equal('Tyrion Lannister')
|
||||
(result['data']['events'][8]['author']['staff']).should.equal(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,7 +36,7 @@ describe '/user/get-users' do
|
|||
})
|
||||
|
||||
(result['status']).should.equal('success')
|
||||
(result['data']['users'].size).should.equal(6)
|
||||
(result['data']['users'].size).should.equal(7)
|
||||
end
|
||||
|
||||
it 'should get users with order by tickets and asc' do
|
||||
|
|
Loading…
Reference in New Issue