Default department feature (#805)

* pt 2

* do not allow to delete default depto

* ticket craete changes and ruby tests

* set langauge to ticket  and ruby tests

* show departments/lenaguages dropdown logic frontend

* default department frontend

* fix github comments

* add logic edit-setting

* add settings to reducer

* resolve git hub comments logic frontend

* delete variables without use

* select default department dropdown

* fix ruby tabulation

* delete creaticketform console.log
This commit is contained in:
Guillermo Giuliana 2020-06-15 16:27:45 -03:00 committed by GitHub
parent 21290c600f
commit 1e7a3e2f4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 633 additions and 57 deletions

View File

@ -5,9 +5,10 @@ import {connect} from 'react-redux';
import i18n from 'lib-app/i18n';
import API from 'lib-app/api-call';
import ConfigActions from 'actions/config-actions';
import SessionStore from 'lib-app/session-store';
import AreYouSure from 'app-components/are-you-sure';
import DepartmentDropDown from 'app-components/department-dropdown';
import DepartmentDropdown from 'app-components/department-dropdown';
import InfoTooltip from 'core-components/info-tooltip';
import Button from 'core-components/button';
@ -16,10 +17,20 @@ import Listing from 'core-components/listing';
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';
import Message from 'core-components/message';
import Loading from 'core-components/loading'
function getPublicDepartmentList(){
return _.filter(SessionStore.getDepartments(),item => item.private != 1)
}
export const getPublicDepartmentIndexFromDepartmentId = (departmentId) => {
const departments = getPublicDepartmentList();
const departmentIndex = _.findIndex(departments, department => department.id == departmentId );
return (departmentIndex !== -1) ? departmentIndex : 0;
}
class AdminPanelDepartments extends React.Component {
static defaultProps = {
items: []
@ -32,14 +43,23 @@ class AdminPanelDepartments extends React.Component {
edited: false,
errorMessage: null,
errors: {},
defaultDepartmentError: null,
form: {
title: '',
language: 'en',
private: 0,
}
},
defaultDepartment: this.props.defaultDepartmentId,
defaultDepartmentLocked: this.props.defaultDepartmentLocked * 1,
};
render() {
const {
errorMessage,
formLoading,
selectedIndex
} = this.state;
return (
<div className="admin-panel-departments">
<Header title={i18n('DEPARTMENTS')} description={i18n('DEPARTMENTS_DESCRIPTION')} />
@ -48,7 +68,7 @@ class AdminPanelDepartments extends React.Component {
<Listing {...this.getListingProps()}/>
</div>
<div className="col-md-8">
{(this.state.errorMessage) ? <Message type="error">{i18n(this.state.errorMessage)}</Message> : null}
{(errorMessage) ? <Message type="error">{i18n(errorMessage)}</Message> : null}
<Form {...this.getFormProps()}>
<div>
<FormField className="admin-panel-departments__name" label={i18n('NAME')} name="name" validation="NAME" required fieldProps={{size: 'large'}}/>
@ -57,17 +77,64 @@ class AdminPanelDepartments extends React.Component {
<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
size="medium"
className="admin-panel-departments__update-name-button"
type="secondary">
{formLoading ?
<Loading /> :
i18n((selectedIndex !== -1) ?
'UPDATE_DEPARTMENT' :
'ADD_DEPARTMENT')}
</SubmitButton>
</Form>
{(this.state.selectedIndex !== -1 && this.props.departments.length) ? this.renderOptionalButtons() : null}
{(selectedIndex !== -1 && this.props.departments.length) ? this.renderOptionalButtons() : null}
</div>
</div>
{this.renderDefaultDepartmentForm()}
</div>
);
}
renderDefaultDepartmentForm() {
const {
defaultDepartmentError,
formLoading
} = this.state
return (
<div className="admin-panel-departments__default-departments-container">
<span className="separator" />
{(defaultDepartmentError !== null) ?
((!defaultDepartmentError) ?
<Message type="success">{i18n('SETTINGS_UPDATED')}</Message> :
<Message type="error">{i18n(defaultDepartmentError)}</Message>) :
null}
<Form {...this.getDefaultDepartmentFormProps()} className="admin-panel-departments__default-departments-container__form">
<div className="admin-panel-departments__default-departments-container__form__fields" >
<FormField
className="admin-panel-departments__default-departments-container__form__fields__select"
label={i18n('DEFAULT_DEPARTMENT')}
name="defaultDepartment"
field="select"
decorator={DepartmentDropdown}
fieldProps={{ departments: getPublicDepartmentList() , size: 'medium' }}
/>
<div className="admin-panel-departments__default-departments-container__form__fields__lock-option">
<FormField label={i18n('LOCK_DEPARTMENT_DESCRIPTION')} name="locked" field="checkbox"/>
</div>
</div>
<SubmitButton
className="admin-panel-departments__default-departments-container__form__button"
size="medium"
type="secondary" >
{formLoading ? <Loading /> : i18n('UPDATE_DEFAULT_DEPARTMENT')}
</SubmitButton>
</Form>
</div>
)
}
renderOptionalButtons() {
return (
<div className="admin-panel-departments__optional-buttons">
@ -93,22 +160,32 @@ class AdminPanelDepartments extends React.Component {
{i18n('WILL_DELETE_DEPARTMENT')}
<div className="admin-panel-departments__transfer-tickets">
<span className="admin-panel-departments__transfer-tickets-title">{i18n('TRANSFER_TICKETS_TO')}</span>
<DepartmentDropDown className="admin-panel-departments__transfer-tickets-drop-down" departments={this.getDropDownDepartments()} onChange={(event) => this.setState({selectedDropDownIndex: event.index})} size="medium"/>
<DepartmentDropdown
className="admin-panel-departments__transfer-tickets-drop-down"
departments={this.getDropDownDepartments()}
onChange={(event) => this.setState({selectedDropDownIndex: event.index})}
size="medium" />
</div>
</div>
);
}
getListingProps() {
const {
departments,
defaultDepartmentId
} = this.props;
return {
className: 'admin-panel-departments__list',
title: i18n('DEPARTMENTS'),
items: this.props.departments.map(department => {
items: departments.map(department => {
return {
content: (
<span>
{department.name}
{department.private*1 ? <Icon className="admin-panel-departments__private-icon" name='user-secret'/> : null }
{department.id == defaultDepartmentId ? <spam className="admin-panel-departments__default-icon"> {i18n('DEFAULT')} </spam> : null }
{(!department.owners) ? (
<span className="admin-panel-departments__warning">
<InfoTooltip type="warning" text={i18n('NO_STAFF_ASSIGNED')}/>
@ -126,13 +203,42 @@ class AdminPanelDepartments extends React.Component {
}
getFormProps() {
const {
form,
errors,
formLoading
} = this.state
return {
values: this.state.form,
errors: this.state.errors,
loading: this.state.formLoading,
values: form,
errors: errors,
onChange: (form) => {this.setState({form, edited: true})},
onValidateErrors: (errors) => {this.setState({errors})},
onSubmit: this.onFormSubmit.bind(this)
onSubmit: this.onFormSubmit.bind(this),
loading: formLoading
};
}
getDefaultDepartmentFormProps() {
const {
formLoading
} = this.state;
return {
values: {
defaultDepartment: getPublicDepartmentIndexFromDepartmentId(this.state.defaultDepartment),
locked: this.state.defaultDepartmentLocked,
},
onChange: (formValue) => {
this.setState({
edited: true,
defaultDepartmentError: null,
defaultDepartment: getPublicDepartmentList()[formValue.defaultDepartment].id,
defaultDepartmentLocked: formValue.locked,
});
},
onSubmit: this.onDefaultDepartmentFormSubmit.bind(this),
loading: formLoading
};
}
@ -144,6 +250,23 @@ class AdminPanelDepartments extends React.Component {
}
}
onDefaultDepartmentFormSubmit(formValue) {
let publicDepartments = getPublicDepartmentList();
this.setState({formLoading: true, edited: false});
API.call({
path: '/system/edit-settings',
data: {
'default-department-id': this.getCurrentDepartment(publicDepartments, formValue.defaultDepartment).id,
'default-is-locked': formValue.locked ? 1 : 0
}
}).then(() => {
this.retrieveDepartments();
this.setState({formLoading: false, errorMessage: false, defaultDepartmentError: false});
}).catch(result => this.setState({formLoading: false, defaultDepartmentError: result.message}));
}
onFormSubmit(form) {
this.setState({formLoading: true, edited: false});
@ -151,14 +274,14 @@ class AdminPanelDepartments extends React.Component {
API.call({
path: '/system/edit-department',
data: {
departmentId: this.getCurrentDepartment().id,
departmentId: this.getCurrentDepartment(this.props.departments).id,
name: form.name,
private: form.private ? 1 : 0
}
}).then(() => {
this.setState({formLoading: false});
this.setState({formLoading: false,errorMessage: false, defaultDepartmentError: null});
this.retrieveDepartments();
}).catch(result => this.setState({formLoading: false, errorMessage: result.message}));
}).catch(result => this.setState({formLoading: false, errorMessage: result.message, defaultDepartmentError: null}));
} else {
API.call({
path: '/system/add-department',
@ -167,9 +290,13 @@ class AdminPanelDepartments extends React.Component {
private: form.private ? 1 : 0
}
}).then(() => {
this.setState({formLoading: false,errorMessage: false, defaultDepartmentError: null});
this.retrieveDepartments();
this.onItemChange(-1);
}).catch(this.onItemChange.bind(this, -1));
}).catch(() => {
this.onItemChange.bind(this, -1)
this.setState({defaultDepartmentError: null});
});
}
}
@ -189,19 +316,20 @@ class AdminPanelDepartments extends React.Component {
API.call({
path: '/system/delete-department',
data: {
departmentId: this.getCurrentDepartment().id,
departmentId: this.getCurrentDepartment(this.props.departments).id,
transferDepartmentId: this.getDropDownItemId()
}
}).then(() => {
this.retrieveDepartments();
this.onItemChange(-1);
this.setState({defaultDepartmentError: null});
})
.catch(result => this.setState({errorMessage: result.message}));
.catch(result => this.setState({errorMessage: result.message, defaultDepartmentError: null}));
}
updateForm(index) {
let form = _.clone(this.state.form);
let department = this.getCurrentDepartment(index);
let department = this.getCurrentDepartment(this.props.departments,index);
form.name = (department && department.name) || '';
form.private = (department && department.private) || 0;
@ -212,7 +340,8 @@ class AdminPanelDepartments extends React.Component {
formLoading: false,
form,
errorMessage: null,
errors: {}
errors: {},
defaultDepartmentError: null
});
}
@ -223,12 +352,17 @@ class AdminPanelDepartments extends React.Component {
});
}
getCurrentDepartment(index) {
return this.props.departments[(index == undefined) ? this.state.selectedIndex : index];
getCurrentDepartment(list, index) {
return list[(index == undefined) ? this.state.selectedIndex : index];
}
getDropDownItemId() {
return this.props.departments.filter((department, index) => index !== this.state.selectedIndex)[this.state.selectedDropDownIndex].id;
const {
selectedIndex,
selectedDropDownIndex
} = this.state;
return this.props.departments.filter((department, index) => index !== selectedIndex)[selectedDropDownIndex].id;
}
getDropDownDepartments() {
@ -238,6 +372,8 @@ class AdminPanelDepartments extends React.Component {
export default connect((store) => {
return {
defaultDepartmentId: store.config['default-department-id']*1,
defaultDepartmentLocked: store.config['default-is-locked']*1,
departments: store.config.departments
};
})(AdminPanelDepartments);

View File

@ -18,6 +18,32 @@
display:inline-block;
margin-left: 10px;
}
&__default-departments-container {
&__form {
text-align: left;
&__fields {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: flex-start;
padding-bottom: 10px;
&__lock-option {
display: flex;
flex-direction: row;
align-items: center;
&__info-tooltip {
padding-left: 5px;
}
}
}
&__button {
min-width: 156px;
}
}
}
&__optional-buttons {
float: right;
@ -48,6 +74,12 @@
}
&__private-icon {
margin-left: 5px;
}
&__default-icon {
color: lightgrey;
font-style: italic;
text-transform: lowercase;
margin-left: 3px;
}
&__info-tooltip {
margin-left: 2px;

View File

@ -77,7 +77,7 @@ class AdminPanelMyTickets extends React.Component {
onCreateTicket() {
ModalContainer.openModal(
<div>
<CreateTicketForm onSuccess={this.onCreateTicketSuccess.bind(this)} />
<CreateTicketForm isStaff={true} onSuccess={this.onCreateTicketSuccess.bind(this)} />
<div style={{textAlign: 'center'}}>
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
</div>

View File

@ -7,8 +7,9 @@ import i18n from 'lib-app/i18n';
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 Captcha from 'app/main/captcha';
import {getPublicDepartmentIndexFromDepartmentId} from 'app/admin/panel/staff/admin-panel-departments';
import Header from 'core-components/header';
import TextEditor from 'core-components/text-editor';
@ -21,11 +22,13 @@ class CreateTicketForm extends React.Component {
static propTypes = {
userLogged: React.PropTypes.bool,
isStaff: React.PropTypes.bool,
onSuccess: React.PropTypes.func,
};
static defaultProps = {
userLogged: true
userLogged: true,
isStaff: false
};
state = {
@ -34,7 +37,7 @@ class CreateTicketForm extends React.Component {
form: {
title: '',
content: TextEditor.createEmpty(),
departmentIndex: 0,
departmentIndex: getPublicDepartmentIndexFromDepartmentId(this.props.defaultDepartmentId),
email: '',
name: '',
language: this.props.language
@ -49,14 +52,18 @@ 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">
{!(this.props.isDefaultDepartmentLocked*1) || this.props.isStaff ?
<FormField className="col-md-5" label={i18n('DEPARTMENT')} name="departmentIndex" field="select" decorator={DepartmentDropdown} fieldProps={{
departments: SessionStore.getDepartments(),
size: 'medium'
}} />
}} /> : null
}
{!this.props.onlyOneSupportedLanguage ?
<FormField className="col-md-5" label={i18n('LANGUAGE')} name="language" field="select" decorator={LanguageSelector} fieldProps={{
type: 'supported',
size: 'medium'
}}/>
}}/> : null
}
</div>
<FormField
label={i18n('CONTENT')}
@ -162,9 +169,11 @@ class CreateTicketForm extends React.Component {
export default connect((store) => {
const { language, supportedLanguages } = store.config;
return {
language: _.includes(supportedLanguages, language) ? language : supportedLanguages[0],
allowAttachments: store.config['allow-attachments']
onlyOneSupportedLanguage: supportedLanguages.length == 1 ? true : false,
isDefaultDepartmentLocked: store.config['default-is-locked'],
allowAttachments: store.config['allow-attachments'],
defaultDepartmentId: store.config['default-department-id']
};
})(CreateTicketForm);

View File

@ -5,6 +5,7 @@ export default {
'ACCOUNT': 'Account',
'SUBMIT': 'Submit',
'EMAIL': 'Email',
'DEFAULT': 'Default',
'PASSWORD': 'Password',
'REPEAT_PASSWORD': 'Repeat password',
'LOG_IN': 'Log in',
@ -26,6 +27,7 @@ export default {
'TICKET_LIST': 'Ticket List',
'SUPPORT_CENTER': 'Support Center',
'DEPARTMENT': 'Department',
'DEFAULT_DEPARTMENT': 'Default Department',
'AUTHOR': 'Author',
'DATE': 'Date',
'RESPOND': 'Respond',
@ -134,6 +136,7 @@ export default {
'EDIT_STAFF': 'Edit staff member',
'ADD_DEPARTMENT': 'Add department',
'UPDATE_DEPARTMENT': 'Update department',
'UPDATE_DEFAULT_DEPARTMENT': 'Update default department',
'TRANSFER_TICKETS_TO': 'Transfer tickets to',
'COMMENTS': 'Comments',
'DELETE_STAFF_MEMBER': 'Delete staff member',
@ -201,6 +204,7 @@ export default {
'INVITE_STAFF': 'Invite staff',
'UPLOAD_FILE': 'Upload file',
'PRIVATE': 'Private',
'LOCKED': 'Locked',
'ENABLE_USER': 'Enable User',
'DISABLE_USER': 'Disable User',
'SHOW_CLOSED_TICKETS': 'Show Closed Tickets',
@ -340,6 +344,7 @@ export default {
'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',
'LOCK_DEPARTMENT_DESCRIPTION': 'Allow users to create tickets only in the default department',
'EMAIL_SETTINGS_DESCRIPTION': 'Here you can edit the settings for receiving and sending email to your customers.',
'IMAP_POLLING_DESCRIPTION': 'Inbox checking will not be done automatically by OpenSupports. You have to make POST requests periodically to this url to process the emails: {url}',
'NEW_CUSTOM_FIELD_DESCRIPTION': 'Here you can create a custom field for an user, it can be a blank text box or a fixed set of options.',
@ -380,7 +385,10 @@ export default {
'USER_DISABLED': 'This account is disabled.',
'INVALID_SYNTAX': 'Invalid syntax.',
'DEPARTMENT_PRIVATE_TICKETS': 'This department has tickets created by non-staff and it can not be private',
'DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE': 'Default department can not be private',
'CURRENTLY_UNAVAILABLE': 'Currently unavailable',
'CAN_NOT_DELETE_DEFAULT_DEPARTMENT': 'Default department can not be deleted',
'INVALID_DEFAULT_DEPARTMENT': 'Default department choosen is invalid',
//MESSAGES
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',

View File

@ -69,7 +69,10 @@ class SessionStore {
this.setItem('maintenance-mode', configs['maintenance-mode']);
this.setItem('max-size', configs['max-size']);
this.setItem('tags', JSON.stringify(configs['tags']));
this.setItem('max-size', configs['max-size']);
this.setItem('tags', JSON.stringify(configs['tags']));
this.setItem('default-is-locked', configs['default-is-locked']);
this.setItem('default-department-id', configs['default-department-id']);
}
getConfigs() {
@ -86,7 +89,10 @@ class SessionStore {
'allow-attachments': (this.getItem('allow-attachments') * 1),
'maintenance-mode': (this.getItem('maintenance-mode') * 1),
'max-size': this.getItem('max-size'),
'tags': JSON.parse(this.getItem('tags'))
'tags': JSON.parse(this.getItem('tags')),
'default-is-locked': this.getItem('default-is-locked'),
'default-department-id': this.getItem('default-department-id')
};
}

View File

@ -38,7 +38,6 @@ class ConfigReducer extends Reducer {
if(payload.data.allowedLanguages && !_.includes(payload.data.allowedLanguages, currentLanguage)) {
currentLanguage = payload.data.language;
}
sessionStore.storeConfigs(_.extend({}, payload.data, {
language: currentLanguage || payload.data.language
}));
@ -50,7 +49,9 @@ class ConfigReducer extends Reducer {
'allow-attachments': !!(payload.data['allow-attachments']* 1),
'maintenance-mode': !!(payload.data['maintenance-mode']* 1),
departments: payload.data.departments && payload.data.departments.map(department => _.extend({}, department, {private: department.private * 1})),
initDone: true
initDone: true,
'default-department-id': payload.data['default-department-id'],
'default-is-locked': payload.data['default-is-locked'],
});
}

View File

@ -49,9 +49,12 @@ class DeleteDepartmentController extends Controller {
}
public function handler() {
$this->departmentId = Controller::request('departmentId');
$this->transferDepartmentId = Controller::request('transferDepartmentId');
$this->checkDepartmentIdDefault();
if ($this->departmentId === $this->transferDepartmentId) {
throw new RequestException(ERRORS::SAME_DEPARTMENT);
}
@ -108,4 +111,9 @@ class DeleteDepartmentController extends Controller {
$ticket->store();
}
}
public function checkDepartmentIdDefault() {
$defaultDepartment = Setting::getSetting('default-department-id');
if ($defaultDepartment && $this->departmentId == $defaultDepartment->value) throw new Exception(ERRORS::CAN_NOT_DELETE_DEFAULT_DEPARTMENT);
}
}

View File

@ -53,6 +53,10 @@ class EditDepartmentController extends Controller {
$departmentInstance = Department::getDataStore($departmentId);
if($private && $departmentId == Setting::getSetting('default-department-id')->getValue()){
throw new RequestException(ERRORS::DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE);
}
if($private && Ticket::count(' author_id IS NOT NULL AND department_id = ? ', [$departmentId])) {
throw new RequestException(ERRORS::DEPARTMENT_PRIVATE_TICKETS);
}

View File

@ -1,4 +1,6 @@
<?php
use Respect\Validation\Validator as DataValidator;
DataValidator::with('CustomValidations', true);
/**
* @api {post} /system/edit-settings Edit settings
@ -29,7 +31,12 @@ class EditSettingsController extends Controller {
public function validations() {
return [
'permission' => 'staff_3',
'requestData' => []
'requestData' => [
'default-department-id' => [
'validation' => DataValidator::oneOf(DataValidator::dataStoreId('department'),DataValidator::nullType()),
'error' => ERRORS::INVALID_DEFAULT_DEPARTMENT
],
]
];
}
@ -53,8 +60,11 @@ class EditSettingsController extends Controller {
'max-size',
'title',
'url',
'mail-template-header-image'
'mail-template-header-image',
'default-is-locked',
'default-department-id'
];
$this->checkDefaultDepartmentValid();
foreach($settings as $setting) {
if(Controller::request($setting)!==null) {
@ -64,6 +74,7 @@ class EditSettingsController extends Controller {
}
}
if(Controller::request('allowedLanguages') || Controller::request('supportedLanguages')) {
$this->handleLanguages();
}
@ -89,6 +100,25 @@ class EditSettingsController extends Controller {
$language->store();
}
}
public function checkDefaultDepartmentValid() {
$departmentId = Controller::request('default-department-id');
if($departmentId){
$Publicdepartments = Department::getPublicDepartmentNames();
$isValid = false;
foreach($Publicdepartments as $department) {
if($department['id'] == $departmentId){
$isValid = true;
}
}
if(!$isValid) throw new Exception(ERRORS::INVALID_DEFAULT_DEPARTMENT);
}
}
}
}

View File

@ -59,6 +59,8 @@ class GetSettingsController extends Controller {
'mail-template-header-image' => Setting::getSetting('mail-template-header-image')->getValue(),
'tags' => Tag::getAll()->toArray(),
'mandatory-login' => Setting::getSetting('mandatory-login')->getValue(),
'default-department-id' => Setting::getSetting('default-department-id')->getValue(),
'default-is-locked' => Setting::getSetting('default-is-locked')->getValue()
];
} else {
$settingsList = [
@ -77,6 +79,8 @@ class GetSettingsController extends Controller {
'session-prefix' => Setting::getSetting('session-prefix')->getValue(),
'tags' => Tag::getAll()->toArray(),
'mandatory-login' => Setting::getSetting('mandatory-login')->getValue(),
'default-department-id' => Setting::getSetting('default-department-id')->getValue(),
'default-is-locked' => Setting::getSetting('default-is-locked')->getValue()
];
}
}

View File

@ -26,7 +26,8 @@ DataValidator::with('CustomValidations', true);
* @apiParam {String} title Title of the support center
* @apiParam {String} url Url of the frontend client.
* @apiParam {Boolean} mandatory-login Indicates if the login is mandatory.
*
* @apiParam {Number} default-department-id Indicates the id of the default department
* @apiParam {Boolean} locked-department Indicates if the default department is locked or not
* @apiUse INVALID_LANGUAGE
* @apiUse INIT_SETTINGS_DONE
*
@ -90,8 +91,10 @@ class InitSettingsController extends Controller {
'ticket-first-number' => Hashing::generateRandomNumber(100000, 999999),
'session-prefix' => 'opensupports-'.Hashing::generateRandomToken().'_',
'mail-template-header-image' => 'https://s3.amazonaws.com/opensupports/logo.png',
'default-department-id' => 1,
'default-is-locked' => false,
'imap-token' => '',
'mandatory-login' => !!Controller::request('mandatory-login'),
'mandatory-login' => !!Controller::request('mandatory-login')
]);
}
@ -119,7 +122,6 @@ class InitSettingsController extends Controller {
private function storeSettings($settings) {
foreach ($settings as $settingName => $settingValue) {
$setting = new Setting();
$setting->setProperties([
'name' => $settingName,
'value' => $settingValue
@ -151,6 +153,7 @@ class InitSettingsController extends Controller {
foreach ($departments as $departmentName) {
$department = new Department();
$department->name = $departmentName;
$department->private = 0;
$department->store();
}
}

View File

@ -62,11 +62,11 @@ class CreateController extends Controller {
'error' => ERRORS::INVALID_CONTENT
],
'departmentId' => [
'validation' => DataValidator::dataStoreId('department'),
'validation' => DataValidator::oneOf(DataValidator::dataStoreId('department'), DataValidator::nullType()),
'error' => ERRORS::INVALID_DEPARTMENT
],
'language' => [
'validation' => DataValidator::in(Language::getSupportedLanguages()),
'validation' => DataValidator::oneOf(DataValidator::in(Language::getSupportedLanguages()), DataValidator::nullType()),
'error' => ERRORS::INVALID_LANGUAGE
]
]
@ -159,9 +159,12 @@ class CreateController extends Controller {
$signupController->validations();
$signupController->handler();
}
private function storeTicket() {
$department = Department::getDataStore($this->departmentId);
$department = Department::getDataStore($this->getCorrectDepartmentId());
$author = $this->getAuthor();
$this->language = $this->getCorrectLanguage();
$ticket = new Ticket();
$fileUploader = FileUploader::getInstance();
@ -200,6 +203,26 @@ class CreateController extends Controller {
$this->ticketNumber = $ticket->ticketNumber;
}
private function getCorrectLanguage() {
if($this->language){
return $this->language;
}else{
return Setting::getSetting('language')->getValue();
}
}
private function getCorrectDepartmentId(){
$defaultDepartmentId = Setting::getSetting('default-department-id')->getValue();
$isLocked = Setting::getSetting('default-is-locked')->getValue();
$validDepartment = Department::getDataStore($defaultDepartmentId)->id;
if (Controller::isStaffLogged()) {
if ($this->departmentId) $validDepartment = $this->departmentId;
} else {
if (!$isLocked && $this->departmentId) $validDepartment = $this->departmentId;
}
return $validDepartment;
}
private function getAuthor() {
if(Controller::getLoggedUser()->email) {
return Controller::getLoggedUser();

View File

@ -219,6 +219,14 @@
* @apiDefine INVALID_FILE
* @apiError {String} INVALID_FILE The file is invalid or max size exceeded.
*/
/**
* @apiDefine INVALID_DEFAULT_DEPARTMENT
* @apiError {String} INVALID_DEFAULT_DEPARTMENT The default department id is invalid.
*/
/**
* @apiDefine CAN_NOT_DELETE_DEFAULT_DEPARTMENT
* @apiError {String} CAN_NOT_DELETE_DEFAULT_DEPARTMENT The default department can not be deleted.
*/
/**
* @apiDefine DATABASE_CONNECTION
* @apiError {String} DATABASE_CONNECTION It's a database connection error.
@ -259,6 +267,10 @@
* @apiDefine DEPARTMENT_PRIVATE_TICKETS
* @apiError {String} DEPARTMENT_PRIVATE_TICKETS There are tickets for in department created by non-staff and it can't be private
*/
/**
* @apiDefine DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE
* @apiError {String} DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE Default Department can not be private
*/
/**
* @apiDefine EMAIL_POLLING
* @apiError {String} EMAIL_POLLING Email polling was unsuccesful
@ -365,6 +377,8 @@ class ERRORS {
const INVALID_PERIOD = 'INVALID_PERIOD';
const NAME_ALREADY_USED = 'NAME_ALREADY_USED';
const INVALID_FILE = 'INVALID_FILE';
const INVALID_DEFAULT_DEPARTMENT = 'INVALID_DEFAULT_DEPARTMENT';
const CAN_NOT_DELETE_DEFAULT_DEPARTMENT = 'CAN_NOT_DELETE_DEFAULT_DEPARTMENT';
const DATABASE_CONNECTION = 'DATABASE_CONNECTION';
const DATABASE_CREATION = 'DATABASE_CREATION';
const SMTP_CONNECTION = 'SMTP_CONNECTION';
@ -376,6 +390,7 @@ class ERRORS {
const INVALID_TEXT_2 = 'INVALID_TEXT_2';
const INVALID_TEXT_3 = 'INVALID_TEXT_3';
const DEPARTMENT_PRIVATE_TICKETS = 'DEPARTMENT_PRIVATE_TICKETS';
const DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE = 'DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE';
const EMAIL_POLLING = 'EMAIL_POLLING';
const CUSTOM_FIELD_ALREADY_EXISTS = 'CUSTOM_FIELD_ALREADY_EXISTS';
const INVALID_CUSTOM_FIELD = 'INVALID_CUSTOM_FIELD';

View File

@ -76,4 +76,5 @@ require './system/custom-fields.rb'
require './ticket/get-authors.rb'
require './ticket/search.rb'
require './system/mandatory-login.rb'
require './system/default-department.rb'
# require './system/get-stats.rb'

View File

@ -141,4 +141,20 @@ class Scripts
csrf_token: $csrf_token
})
end
def self.createDepartment(nameDepartment = 'validnameDepartment')
request('/system/add-department', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
name: nameDepartment
})
end
def self.updateLockedDepartmentSetting(value = 0)
request('/system/edit-settings', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
"default-is-locked" => value
})
end
end

View File

@ -0,0 +1,269 @@
describe '/system/default-department' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
it 'should fail if try to turn a private department default' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
privatedepartment = $database.getRow('department', 1, 'private')
result = request('/system/edit-settings', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"default-department-id" => privatedepartment['id']
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_DEFAULT_DEPARTMENT')
end
it 'should edit locked setting' do
result = request('/system/edit-settings', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"default-is-locked" => 1
})
(result['status']).should.equal('success')
row = $database.getRow('setting', 'default-is-locked', 'name')
(row['value']).should.equal('1')
end
it 'should fail if default-department-id does not exist' do
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
result= request('/system/edit-settings', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"default-department-id" => 1111
})
(result['status']).should.equal('fail')
(result['message']).should.equal('INVALID_DEFAULT_DEPARTMENT')
end
it 'should set a new default deparment' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
publicdepartment = $database.getRow('department', 'Suggestions', 'name')
result = request('/system/edit-settings', {
"csrf_userid" => $csrf_userid,
"csrf_token" => $csrf_token,
"default-department-id" => publicdepartment['id']
})
(result['status']).should.equal('success')
end
it 'should fail if try to delete the default department' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
defaultDepartment = $database.getRow('setting', 'default-department-id', 'name')
transferDepartment = $database.getRow('department','<b>new department</b>','name')
result = request('/system/delete-department', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
departmentId: defaultDepartment['value'],
transferDepartmentId: transferDepartment['id']
})
(result['status']).should.equal('fail')
(result['message']).should.equal('CAN_NOT_DELETE_DEFAULT_DEPARTMENT')
end
it 'should fail if try to edit default department into private' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
defaultDepartmentId = $database.getRow('setting', 'default-department-id', 'name')
department = $database.getRow('department',defaultDepartmentId['value'],'id')
result = request('/system/edit-department', {
csrf_userid: $csrf_userid,
csrf_token: $csrf_token,
departmentId: department['id'],
name: department['name'],
private: true
})
(result['status']).should.equal('fail')
(result['message']).should.equal('DEFAULT_DEPARTMENT_CAN_NOT_BE_PRIVATE')
end
it 'should create ticket in default department if Staff does not give department with locked on' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(1)
result = request('/ticket/create', {
title: 'Night King',
content: 'Arya sucks',
language: 'en',
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
row = $database.getRow('ticket', 'Night King', 'title')
setting = $database.getRow('setting', 'default-department-id','name')
(row['department_id']).should.equal(setting['value'].to_i)
end
it 'should create ticket in default department if staff does not give department with locked off'do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(0)
result = request('/ticket/create', {
title: 'Night King2',
content: 'Arya sucks2',
language: 'en',
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
row = $database.getRow('ticket', 'Night King2', 'title')
setting = $database.getRow('setting', 'default-department-id','name')
(row['department_id']).should.equal(setting['value'].to_i)
end
it 'should create ticket in selected department if staff give department and lockd is off'do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(0)
result = request('/ticket/create', {
title: 'Night King3',
content: 'Arya sucks3',
language: 'en',
departmentId: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
row = $database.getRow('ticket', 'Night King3', 'title')
(row['department_id']).should.equal(1)
end
it 'should create ticket in selected department if staff give department and locked is on' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(1)
result = request('/ticket/create', {
title: 'Night King4',
content: 'Arya sucks4',
language: 'en',
departmentId: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
row = $database.getRow('ticket', 'Night King4', 'title')
(row['department_id']).should.equal(1)
end
it 'should create ticket on default department if user does not give department and locked is on' do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(1)
request('/user/logout')
Scripts.login('user@os4.com', 'loginpass')
result = request('/ticket/create', {
title: 'Night King5',
content: 'Arya sucks5',
language: 'en',
departmentId: 5,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
(result['status']).should.equal('success')
row = $database.getRow('ticket', 'Night King5', 'title')
setting = $database.getRow('setting', 'default-department-id','name')
(row['department_id']).should.equal(setting['value'].to_i)
end
it 'should create ticket on default department if user does not give department and locked is off'do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(0)
request('/user/logout')
Scripts.login('user@os4.com', 'loginpass')
result = request('/ticket/create', {
title: 'Night King6',
content: 'Arya sucks6',
language: 'en',
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
row = $database.getRow('ticket', 'Night King6', 'title')
setting = $database.getRow('setting', 'default-department-id','name')
(row['department_id']).should.equal(setting['value'].to_i)
end
it 'should create ticket on selected department if user give department and locked is off'do
request('/user/logout')
Scripts.login('staff@opensupports.com', 'staff', true)
Scripts.updateLockedDepartmentSetting(0)
request('/user/logout')
Scripts.login('user@os4.com', 'loginpass')
result = request('/ticket/create', {
title: 'Night King16',
content: 'Arya sucks16',
language: 'en',
departmentId: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
row = $database.getRow('ticket', 'Night King16', 'title')
(row['department_id']).should.equal(1)
end
it 'should create ticket on default leanguage if user does not pass language'do
$database.query('update setting set value="ru" where name="language";')
request('/user/logout')
Scripts.login('user@os4.com', 'loginpass')
result = request('/ticket/create', {
title: 'Danny Dragon',
content: 'They do not get to choose',
departmentId: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
ticket = $database.getRow('ticket', 'Danny Dragon', 'title')
defaultLanguage = $database.getRow('setting', 'language', 'name')
(ticket['language']).should.equal(defaultLanguage['value'])
result['status'].should.equal('success')
$database.query('update setting set value="en" where name="language";')
end
end

View File

@ -12,7 +12,8 @@ describe'system/edit-settings' do
"allow-attachments" => 1,
"max-size" => 2,
"language" => 'en',
"server-email" => 'testemail@hotmail.com'
"server-email" => 'testemail@hotmail.com',
"default-is-locked" => 1
})
(result['status']).should.equal('success')
@ -34,7 +35,8 @@ describe'system/edit-settings' do
row = $database.getRow('setting', 'server-email', 'name')
(row['value']).should.equal('testemail@hotmail.com')
row = $database.getRow('setting', 'default-is-locked', 'name')
(row['value']).should.equal('1')
request('/user/logout')
end
it 'should fail if supported languages are invalid' do
@ -94,6 +96,8 @@ describe'system/edit-settings' do
lastLog = $database.getLastRow('log')
(lastLog['type']).should.equal('EDIT_SETTINGS')
Scripts.updateLockedDepartmentSetting(0);
request('/user/logout')
end
end

View File

@ -6,6 +6,8 @@ describe '/system/get-settings' do
(result['data']['language']).should.equal('en')
(result['data']['departments'][0]['name']).should.equal('Help and Support')
(result['data']['mandatory-login']).should.equal('1')
(result['data']['default-department-id']).should.equal('1')
(result['data']['default-is-locked']).should.equal('0')
(result['data']['allowedLanguages'][0]).should.equal('en')
(result['data']['allowedLanguages'][1]).should.equal('es')
(result['data']['allowedLanguages'][2]).should.equal('de')

View File

@ -29,6 +29,12 @@ describe '/system/init-settings' do
lang = $database.getRow('setting', 'language', 'name')
(lang['value']).should.equal('en')
default = $database.getRow('setting', 'default-department-id', 'name')
(default['value']).should.equal('1')
locked = $database.getRow('setting', 'default-is-locked', 'name')
(locked['value']).should.equal('0')
result = request('/system/init-admin', {
name: 'Emilia Clarke',
email: $staff[:email],

View File

@ -34,7 +34,6 @@ describe '/user/get' do
})
ticket = $database.getRow('ticket', @ticketNumber, 'ticket_number')
(result['status']).should.equal('success')
(result['data']['name']).should.equal('User Get')
(result['data']['email']).should.equal('user_get@os4.com')