From fb80336583b063d56c0b2c3980aa5a59e5484afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan?= <36162164+gaetanfl@users.noreply.github.com> Date: Thu, 17 Oct 2019 16:08:43 +0200 Subject: [PATCH 01/50] Change TITLE translation from titre to objet Titre is ambiguous in french and can be misinterpreted as job title instead of subject --- client/src/data/languages/fr.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/data/languages/fr.js b/client/src/data/languages/fr.js index c7356793..1877bca1 100644 --- a/client/src/data/languages/fr.js +++ b/client/src/data/languages/fr.js @@ -62,7 +62,7 @@ export default { 'HIGH': 'Haute', 'MEDIUM': 'Moyenne', 'LOW': 'Faible', - 'TITLE': 'Titre', + 'TITLE': 'Objet', 'CONTENT': 'Contenu', 'SAVE': 'Enregistrer', 'DISCARD_CHANGES': 'Annuler les modifications', @@ -343,7 +343,7 @@ export default { 'ERROR_EMPTY': 'Valeur invalide', 'ERROR_PASSWORD': 'Mot de passe incorrect', 'ERROR_NAME': 'Nom incorrect', - 'ERROR_TITLE': 'Titre incorrect', + 'ERROR_TITLE': 'Objet incorrect', 'ERROR_EMAIL': 'Email invalide', 'ERROR_CONTENT_SHORT': 'Contenu trop court', 'PASSWORD_NOT_MATCH': 'Le mot de passe ne correspond pas', From f558e64afca96240c2434c292f7783c73b12ed8f Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 21 Oct 2019 17:44:49 -0300 Subject: [PATCH 02/50] First mock for an 'invite user' button in the list-user view --- .../panel/users/admin-panel-list-users.js | 18 ++ .../admin/panel/users/invite-user-widget.js | 165 ++++++++++++++++++ .../admin/panel/users/invite-user-widget.scss | 19 ++ client/src/data/languages/en.js | 3 + 4 files changed, 205 insertions(+) create mode 100644 client/src/app/admin/panel/users/invite-user-widget.js create mode 100644 client/src/app/admin/panel/users/invite-user-widget.scss diff --git a/client/src/app/admin/panel/users/admin-panel-list-users.js b/client/src/app/admin/panel/users/admin-panel-list-users.js index b2997af2..98fbc06f 100644 --- a/client/src/app/admin/panel/users/admin-panel-list-users.js +++ b/client/src/app/admin/panel/users/admin-panel-list-users.js @@ -13,6 +13,7 @@ import Message from 'core-components/message'; import Icon from 'core-components/icon'; import ModalContainer from 'app-components/modal-container'; import MainSignUpWidget from 'app/main/main-signup/main-signup-widget'; +import InviteUserWidget from 'app/admin/panel/users/invite-user-widget'; class AdminPanelListUsers extends React.Component { @@ -45,6 +46,9 @@ class AdminPanelListUsers extends React.Component { + ); @@ -181,6 +185,20 @@ class AdminPanelListUsers extends React.Component { ModalContainer.closeModal(); } + onInviteUser(user) { + ModalContainer.openModal( +
+ +
+ +
+
+ ); + } + onInviteUserSuccess() { + ModalContainer.closeModal(); + } + onUsersRetrieved(result) { this.setState({ page: result.data.page * 1, diff --git a/client/src/app/admin/panel/users/invite-user-widget.js b/client/src/app/admin/panel/users/invite-user-widget.js new file mode 100644 index 00000000..8336724d --- /dev/null +++ b/client/src/app/admin/panel/users/invite-user-widget.js @@ -0,0 +1,165 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import _ from 'lodash'; +import classNames from 'classnames'; + +import i18n from 'lib-app/i18n'; +import API from 'lib-app/api-call'; + +import Captcha from 'app/main/captcha'; +import SubmitButton from 'core-components/submit-button'; +import Message from 'core-components/message'; +import Form from 'core-components/form'; +import FormField from 'core-components/form-field'; +import Widget from 'core-components/widget'; +import Header from 'core-components/header'; + +class InviteUserWidget extends React.Component { + + static propTypes = { + onSuccess: React.PropTypes.func, + className: React.PropTypes.string + }; + + constructor(props) { + super(props); + + this.state = { + loading: false, + email: null, + customFields: [] + }; + } + + componentDidMount() { + API.call({ + path: '/system/get-custom-fields', + data: {} + }) + .then(result => this.setState({customFields: result.data})); + } + + render() { + return ( + +
+
+
+ + + {this.state.customFields.map(this.renderCustomField.bind(this))} +
+
+ +
+ {i18n('INVITE_USER')} +
+ + {this.renderMessage()} + + ); + } + + renderCustomField(customField, key) { + if(customField.type === 'text') { + return ( + + ); + } else { + const items = customField.options.map(option => ({content: option.name, value: option.name})); + + return ( + + ); + } + } + + renderMessage() { + switch (this.state.message) { + case 'success': + return {i18n('INVITE_USER_SUCCESS')}; + case 'fail': + return {i18n('EMAIL_EXISTS')}; + default: + return null; + } + } + + getClass() { + let classes = { + 'invite-user-widget': true, + [this.props.className]: this.props.className + }; + return classNames(classes); + } + + getFormProps() { + return { + loading: this.state.loading, + className: 'invite-user-widget__form', + onSubmit: this.onInviteUserFormSubmit.bind(this) + }; + } + + getInputProps(password) { + return { + className: 'invite-user-widget__input', + fieldProps: { + size: 'medium', + password: password + } + }; + } + + onInviteUserFormSubmit(formState) { + const captcha = this.refs.captcha.getWrappedInstance(); + + if (!captcha.getValue()) { + captcha.focus(); + } else { + this.setState({ + loading: true + }); + + const form = _.clone(formState); + + this.state.customFields.forEach(customField => { + if(customField.type === 'select') { + form[`customfield_${customField.name}`] = customField.options[form[`customfield_${customField.name}`]].name; + } + }) + + API.call({ + path: '/user/invite', + data: _.extend({captcha: captcha.getValue()}, form) + }).then(this.onInviteUserSuccess.bind(this)).catch(this.onInviteUserFail.bind(this)); + } + } + + onInviteUserSuccess() { + this.setState({ + loading: false, + message: 'success' + }); + } + + onInviteUserFail() { + this.setState({ + loading: false, + message: 'fail' + }); + } +} + +export default InviteUserWidget; diff --git a/client/src/app/admin/panel/users/invite-user-widget.scss b/client/src/app/admin/panel/users/invite-user-widget.scss new file mode 100644 index 00000000..9ecbf748 --- /dev/null +++ b/client/src/app/admin/panel/users/invite-user-widget.scss @@ -0,0 +1,19 @@ +.invite-user-widget { + padding: 30px; + text-align: center; + + &__form { + margin-bottom: 20px; + } + + &__inputs { + display: inline-block; + margin: 0 auto; + } + + &__captcha { + margin: 10px auto 20px; + height: 78px; + width: 304px; + } +} diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 6373f738..984d76d8 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -193,6 +193,7 @@ export default { 'NEVER': 'Never', 'HIMSELF': 'himself', 'ADD_USER': 'Add user', + 'INVITE_USER': 'Invite user', 'UPLOAD_FILE': 'Upload file', 'PRIVATE': 'Private', 'ENABLE_USER': 'Enable User', @@ -336,6 +337,7 @@ export default { 'IMAP_POLLING_DESCRIPTION': 'Inbox checking will not be done automatically by OpenSupports. You have to make POST requests periodically to this url to process the emails: {url}', 'NEW_CUSTOM_FIELD_DESCRIPTION': 'Here you can create a custom field for an user, it can be a blank text box or a fixed set of options.', 'CUSTOM_FIELDS_DESCRIPTION': 'Custom fields are defined additional fields the users are able to fill to provide more information about them.', + 'INVITE_USER_VIEW_DESCRIPTION': 'Here you can invite an user to join our support system, he will just need to provide his password to create a new user.', //ERRORS 'EMAIL_OR_PASSWORD': 'Email or password invalid', @@ -372,6 +374,7 @@ export default { //MESSAGES 'SIGNUP_SUCCESS': 'You have registered successfully in our support system.', + 'INVITE_USER_SUCCESS': 'You have invited a new user successfully in our support system', 'TICKET_SENT': 'Ticket has been created successfully.', 'VALID_RECOVER': 'Password recovered successfully', 'EMAIL_EXISTS': 'Email already exists', From aa7cefc959aadd3137eef2dea787772fb03ffcd1 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 28 Oct 2019 10:49:00 -0300 Subject: [PATCH 03/50] Fixed PHP documentation for path get-all --- server/controllers/staff/get-all.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/staff/get-all.php b/server/controllers/staff/get-all.php index 3b38daaf..b58e9875 100755 --- a/server/controllers/staff/get-all.php +++ b/server/controllers/staff/get-all.php @@ -11,7 +11,7 @@ use Respect\Validation\Validator as DataValidator; * * @apiDescription This path retrieves information about all the staff member. * - * @apiPermission staff3 + * @apiPermission staff1 * * @apiUse NO_PERMISSION * From 5331c3363e40fed1aca87ded1dabd737b48d95cc Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 28 Oct 2019 15:27:25 -0300 Subject: [PATCH 04/50] First working version of invite users feature --- server/controllers/user.php | 1 + server/controllers/user/invite.php | 148 ++++++++ server/controllers/user/signup.php | 2 +- server/data/ERRORS.php | 5 + server/data/MailTexts.php | 6 + server/data/mail-templates/user-invite.html | 384 ++++++++++++++++++++ server/models/MailTemplate.php | 2 + 7 files changed, 547 insertions(+), 1 deletion(-) create mode 100755 server/controllers/user/invite.php create mode 100755 server/data/mail-templates/user-invite.html diff --git a/server/controllers/user.php b/server/controllers/user.php index d5d08020..feb48483 100755 --- a/server/controllers/user.php +++ b/server/controllers/user.php @@ -4,6 +4,7 @@ $userControllers->setGroupPath('/user'); $userControllers->addController(new LoginController); $userControllers->addController(new SignUpController); +$userControllers->addController(new InviteUserController); $userControllers->addController(new LogoutController); $userControllers->addController(new CheckSessionController); $userControllers->addController(new SendRecoverPasswordController); diff --git a/server/controllers/user/invite.php b/server/controllers/user/invite.php new file mode 100755 index 00000000..f5924907 --- /dev/null +++ b/server/controllers/user/invite.php @@ -0,0 +1,148 @@ + 'staff_1', + 'requestData' => [ + 'name' => [ + 'validation' => DataValidator::length(2, 55), + 'error' => ERRORS::INVALID_NAME + ], + 'email' => [ + 'validation' => DataValidator::email(), + 'error' => ERRORS::INVALID_EMAIL + ] + ] + ]; + + $validations['requestData']['captcha'] = [ + 'validation' => DataValidator::captcha(), + 'error' => ERRORS::INVALID_CAPTCHA + ]; + + return $validations; + } + + public function handler() { + if (!Controller::isUserSystemEnabled()) { + throw new RequestException(ERRORS::USER_SYSTEM_DISABLED); + } + + if (!Setting::getSetting('registration')->value) { + throw new RequestException(ERRORS::NO_PERMISSION); + } + + $this->storeRequestData(); + + $existentUser = User::getUser($this->userEmail, 'email'); + + if (!$existentUser->isNull()) { + throw new RequestException(ERRORS::USER_EXISTS); + } + + $banRow = Ban::getDataStore($this->userEmail, 'email'); + + if (!$banRow->isNull()) { + throw new RequestException(ERRORS::ALREADY_BANNED); + } + + if(MailSender::getInstance()->isConnected()) { + $userId = $this->createNewUserAndRetrieveId(); + + $this->token = Hashing::generateRandomToken(); + + $recoverPassword = new RecoverPassword(); + $recoverPassword->setProperties(array( + 'email' => $this->userEmail, + 'token' => $this->token, + 'staff' => false + )); + $recoverPassword->store(); + + $this->sendInvitationMail(); + + Response::respondSuccess([ + 'userId' => $userId, + 'userEmail' => $this->userEmail + ]); + // TODO: Log::createLog('SIGN_UP', null, User::getDataStore($userId)); + } else { + throw new RequestException(ERRORS::MAIL_SENDER_NOT_CONNECTED); + } + } + + public function storeRequestData() { + $this->userName = Controller::request('name'); + $this->userEmail = Controller::request('email'); + } + + public function createNewUserAndRetrieveId() { + $userInstance = new User(); + + $userInstance->setProperties([ + 'name' => $this->userName, + 'signupDate' => Date::getCurrentDate(), + 'tickets' => 0, + 'email' => $this->userEmail, + 'password' => Hashing::hashPassword(Hashing::generateRandomToken()), + 'verificationToken' => null, + 'xownCustomfieldvalueList' => $this->getCustomFieldValues() + ]); + + return $userInstance->store(); + } + + public function sendInvitationMail() { + $mailSender = MailSender::getInstance(); + + $mailSender->setTemplate(MailTemplate::USER_INVITE, [ + 'to' => $this->userEmail, + 'name' => $this->userName, + 'url' => Setting::getSetting('url')->getValue(), + 'token' => $this->token + ]); + + $mailSender->send(); + } +} diff --git a/server/controllers/user/signup.php b/server/controllers/user/signup.php index 8ae934ab..ec6736cb 100755 --- a/server/controllers/user/signup.php +++ b/server/controllers/user/signup.php @@ -18,7 +18,7 @@ DataValidator::with('CustomValidations', true); * @apiParam {String} name The name of the new user. * @apiParam {String} email The email of the new user. * @apiParam {String} password The password of the new user. - * @apiParam {String} apiKey APIKey to sign up an user if the user system is disabled. + * @apiParam {String} apiKey APIKey to sign up an user if the registration system is disabled. * @apiParam {String} customfield_ Custom field values for this user. * * @apiUse INVALID_NAME diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index 831086a9..da679009 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -251,6 +251,10 @@ * @apiDefine INVALID_COLOR * @apiError {String} INVALID_COLOR The color should be in hexadecimal, preceded by a '#' */ +/** + * @apiDefine MAIL_SENDER_NOT_CONNECTED + * @apiError {String} MAIL_SENDER_NOT_CONNECTED The mail sender is not connected. + */ class ERRORS { const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'; @@ -317,4 +321,5 @@ class ERRORS { const INVALID_CUSTOM_FIELD_OPTION = 'INVALID_CUSTOM_FIELD_OPTION'; const UNAVAILABLE_STATS = 'UNAVAILABLE_STATS'; const INVALID_COLOR = 'INVALID_COLOR'; + const MAIL_SENDER_NOT_CONNECTED = 'MAIL_SENDER_NOT_CONNECTED'; } diff --git a/server/data/MailTexts.php b/server/data/MailTexts.php index c30e4d8d..2ea73203 100644 --- a/server/data/MailTexts.php +++ b/server/data/MailTexts.php @@ -25,6 +25,12 @@ class MailTexts { 'Hi, {{name}}. You have requested to recover your password.', 'Use this code in {{url}}/recover-password?email={{to}}&token={{token}} or click the button below.', ], + 'USER_INVITE' => [ + 'User invited - OpenSupports', + 'User invited', + 'Hi, {{name}}. You have been invited to join our support center.', + 'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.' + ], 'USER_SYSTEM_DISABLED' => [ 'Access system changed - OpenSupports', 'Access system changed', diff --git a/server/data/mail-templates/user-invite.html b/server/data/mail-templates/user-invite.html new file mode 100755 index 00000000..acc07035 --- /dev/null +++ b/server/data/mail-templates/user-invite.html @@ -0,0 +1,384 @@ + + + + + + Support Center + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + +
+ logo +
+
+ +
+
+
+
+ + + + + + + + + + +
+ {{USER_INVITE_MATCH_1}} +
+ {{USER_INVITE_MATCH_2}} +
+ + + + +
+ + + + + + + + + + +
+ {{USER_INVITE_MATCH_3}} +
+ {{token}} +
+ +
+
+
+
+
+
+ + + + +
+ OpenSupports
+ Open source ticket system
+ www.opensupports.com

+
+
+
+ + diff --git a/server/models/MailTemplate.php b/server/models/MailTemplate.php index 9d21196d..4482167b 100755 --- a/server/models/MailTemplate.php +++ b/server/models/MailTemplate.php @@ -19,6 +19,7 @@ class MailTemplate extends DataStore { const USER_SIGNUP = 'USER_SIGNUP'; const USER_PASSWORD = 'USER_PASSWORD'; const PASSWORD_FORGOT = 'PASSWORD_FORGOT'; + const USER_INVITE = 'USER_INVITE'; const USER_SYSTEM_DISABLED = 'USER_SYSTEM_DISABLED'; const USER_SYSTEM_ENABLED = 'USER_SYSTEM_ENABLED'; const TICKET_CREATED = 'TICKET_CREATED'; @@ -32,6 +33,7 @@ class MailTemplate extends DataStore { 'USER_PASSWORD' => 'data/mail-templates/user-edit-password.html', 'USER_EMAIL' => 'data/mail-templates/user-edit-email.html', 'PASSWORD_FORGOT' => 'data/mail-templates/user-password-forgot.html', + 'USER_INVITE' => 'data/mail-templates/user-invite.html', 'USER_SYSTEM_DISABLED' => 'data/mail-templates/user-system-disabled.html', 'USER_SYSTEM_ENABLED' => 'data/mail-templates/user-system-enabled.html', 'TICKET_CREATED' => 'data/mail-templates/ticket-created.html', From d7e1edea01eff0474215429fde62be9fbb9538ee Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 29 Oct 2019 14:23:07 -0300 Subject: [PATCH 05/50] Changes content displayed in recover-password when the user has been invited, and updates mail texts --- .../main/main-recover-password/main-recover-password-page.js | 2 +- client/src/data/languages/en.js | 1 + server/data/MailTexts.php | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/app/main/main-recover-password/main-recover-password-page.js b/client/src/app/main/main-recover-password/main-recover-password-page.js index e8141c4e..a6807d52 100644 --- a/client/src/app/main/main-recover-password/main-recover-password-page.js +++ b/client/src/app/main/main-recover-password/main-recover-password-page.js @@ -30,7 +30,7 @@ class MainRecoverPasswordPage extends React.Component { render() { return (
- +
diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 984d76d8..17917dc2 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -11,6 +11,7 @@ export default { 'SIGN_UP': 'Sign up', 'FORGOT_PASSWORD': 'Forgot your password?', 'RECOVER_PASSWORD': 'Recover Password', + 'SET_UP_PASSWORD': 'Set up your password', 'RECOVER_SENT': 'An email with recover instructions has been sent.', 'NEW_EMAIL': 'New email', 'FULL_NAME': 'Full name', diff --git a/server/data/MailTexts.php b/server/data/MailTexts.php index 2ea73203..68376be3 100644 --- a/server/data/MailTexts.php +++ b/server/data/MailTexts.php @@ -26,8 +26,8 @@ class MailTexts { 'Use this code in {{url}}/recover-password?email={{to}}&token={{token}} or click the button below.', ], 'USER_INVITE' => [ - 'User invited - OpenSupports', - 'User invited', + 'You have been invited - OpenSupports', + 'You have been invited', 'Hi, {{name}}. You have been invited to join our support center.', 'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.' ], From f3a0fbf7da077c372bb6c61a08474ab4efe5fab5 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 29 Oct 2019 14:34:20 -0300 Subject: [PATCH 06/50] Allows staffs to invite members even when user registration is disabled (the most likely use case) --- server/controllers/user/invite.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/controllers/user/invite.php b/server/controllers/user/invite.php index f5924907..d5dd7666 100755 --- a/server/controllers/user/invite.php +++ b/server/controllers/user/invite.php @@ -69,10 +69,6 @@ class InviteUserController extends Controller { throw new RequestException(ERRORS::USER_SYSTEM_DISABLED); } - if (!Setting::getSetting('registration')->value) { - throw new RequestException(ERRORS::NO_PERMISSION); - } - $this->storeRequestData(); $existentUser = User::getUser($this->userEmail, 'email'); From 7eae717b0cded136cc62e5eaa7d7b004dc8556ca Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 29 Oct 2019 14:39:39 -0300 Subject: [PATCH 07/50] Removes ADD USER button, since its functionality is now replaced by the INVITE USER button --- .../admin/panel/users/admin-panel-list-users.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/client/src/app/admin/panel/users/admin-panel-list-users.js b/client/src/app/admin/panel/users/admin-panel-list-users.js index 98fbc06f..ffc98211 100644 --- a/client/src/app/admin/panel/users/admin-panel-list-users.js +++ b/client/src/app/admin/panel/users/admin-panel-list-users.js @@ -43,9 +43,6 @@ class AdminPanelListUsers extends React.Component { {(this.state.error) ? {i18n('ERROR_RETRIEVING_USERS')} : }
- @@ -171,20 +168,6 @@ class AdminPanelListUsers extends React.Component { }).catch(this.onUsersRejected.bind(this)).then(this.onUsersRetrieved.bind(this)); } - onCreateUser(user) { - ModalContainer.openModal( -
- -
- -
-
- ); - } - onCreateUserSuccess() { - ModalContainer.closeModal(); - } - onInviteUser(user) { ModalContainer.openModal(
From 18be18ebf86329bfd78c281011c200c85a103dd3 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 29 Oct 2019 16:11:37 -0300 Subject: [PATCH 08/50] Adds log for user invitations --- client/src/app-components/activity-row.js | 2 ++ client/src/data/languages/en.js | 1 + server/controllers/user/invite.php | 3 ++- server/models/Log.php | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/app-components/activity-row.js b/client/src/app-components/activity-row.js index cb5dec18..d7c7215f 100644 --- a/client/src/app-components/activity-row.js +++ b/client/src/app-components/activity-row.js @@ -23,6 +23,7 @@ class ActivityRow extends React.Component { 'EDIT_SETTINGS', 'SIGNUP', + 'INVITE', 'ADD_TOPIC', 'ADD_ARTICLE', 'DELETE_TOPIC', @@ -106,6 +107,7 @@ class ActivityRow extends React.Component { 'EDIT_SETTINGS': 'wrench', 'SIGNUP': 'user-plus', + 'INVITE': 'user-plus', 'ADD_TOPIC': 'book', 'ADD_ARTICLE': 'book', 'DELETE_TOPIC': 'book', diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 17917dc2..c671f716 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -234,6 +234,7 @@ export default { 'ACTIVITY_EDIT_SETTINGS': 'edited settings', 'ACTIVITY_SIGNUP': 'signed up', + 'ACTIVITY_INVITE': 'invited user', 'ACTIVITY_ADD_TOPIC': 'added topic', 'ACTIVITY_ADD_ARTICLE': 'added article', 'ACTIVITY_DELETE_TOPIC': 'deleted topic', diff --git a/server/controllers/user/invite.php b/server/controllers/user/invite.php index d5dd7666..d2eb2668 100755 --- a/server/controllers/user/invite.php +++ b/server/controllers/user/invite.php @@ -102,7 +102,8 @@ class InviteUserController extends Controller { 'userId' => $userId, 'userEmail' => $this->userEmail ]); - // TODO: Log::createLog('SIGN_UP', null, User::getDataStore($userId)); + + Log::createLog('INVITE', $this->userName); } else { throw new RequestException(ERRORS::MAIL_SENDER_NOT_CONNECTED); } diff --git a/server/models/Log.php b/server/models/Log.php index a1d99687..fd126fcf 100755 --- a/server/models/Log.php +++ b/server/models/Log.php @@ -26,7 +26,7 @@ class Log extends DataStore { ]; } - public static function createLog($type,$to, $author = null) { + public static function createLog($type, $to, $author = null) { if($author === null) { $author = Controller::getLoggedUser(); } From 93ade9cf0fbe0243749073ab583970c3060f7864 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 29 Oct 2019 18:23:09 -0300 Subject: [PATCH 09/50] Implemented functionality to allow staff members invite other staffs. --- .../panel/staff/admin-panel-staff-members.js | 10 +- .../admin/panel/staff/invite-staff-modal.js | 120 ++++++++++++++ .../admin/panel/staff/invite-staff-modal.scss | 23 +++ client/src/data/languages/en.js | 2 + server/controllers/staff.php | 3 +- server/controllers/staff/invite.php | 147 ++++++++++++++++++ 6 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 client/src/app/admin/panel/staff/invite-staff-modal.js create mode 100644 client/src/app/admin/panel/staff/invite-staff-modal.scss create mode 100755 server/controllers/staff/invite.php diff --git a/client/src/app/admin/panel/staff/admin-panel-staff-members.js b/client/src/app/admin/panel/staff/admin-panel-staff-members.js index ac587050..75689f87 100644 --- a/client/src/app/admin/panel/staff/admin-panel-staff-members.js +++ b/client/src/app/admin/panel/staff/admin-panel-staff-members.js @@ -11,7 +11,7 @@ import SessionStore from 'lib-app/session-store'; import PeopleList from 'app-components/people-list'; import ModalContainer from 'app-components/modal-container'; -import AddStaffModal from 'app/admin/panel/staff/add-staff-modal'; +import InviteStaffModal from 'app/admin/panel/staff/invite-staff-modal'; import Header from 'core-components/header'; import DropDown from 'core-components/drop-down'; @@ -47,8 +47,8 @@ class AdminPanelStaffMembers extends React.Component {
-
{(this.props.loading) ? : this.setState({page: index+1})} />} @@ -56,8 +56,8 @@ class AdminPanelStaffMembers extends React.Component { ); } - onAddNewStaff() { - ModalContainer.openModal(); + onInviteStaff() { + ModalContainer.openModal(); } getDepartmentDropdownProps() { diff --git a/client/src/app/admin/panel/staff/invite-staff-modal.js b/client/src/app/admin/panel/staff/invite-staff-modal.js new file mode 100644 index 00000000..c98e608c --- /dev/null +++ b/client/src/app/admin/panel/staff/invite-staff-modal.js @@ -0,0 +1,120 @@ +import React from 'react'; +import _ from 'lodash'; + +import i18n from 'lib-app/i18n'; +import API from 'lib-app/api-call'; +import SessionStore from 'lib-app/session-store'; + +import Header from 'core-components/header' +import Form from 'core-components/form'; +import FormField from 'core-components/form-field'; +import SubmitButton from 'core-components/submit-button'; +import Button from 'core-components/button'; +import Icon from 'core-components/icon'; + +class InviteStaffModal extends React.Component { + + static contextTypes = { + closeModal: React.PropTypes.func + }; + + static propTypes = { + onSuccess: React.PropTypes.func + }; + + state = { + loading: false, + errors: {}, + error: null + }; + + render() { + return ( +
+
+ this.setState({errors})} loading={this.state.loading}> +
+
+ + +
+ +
+
+
+
+
{i18n('Departments')}
+ +
+
+
+ + {i18n('SAVE')} + + + +
+ ); + } + + getDepartments() { + return SessionStore.getDepartments().map(department => { + if(department.private*1){ + return {department.name} + } else { + return department.name; + } + }); + } + + onSubmit(form) { + let departments = _.filter(SessionStore.getDepartments(), (department, index) => { + return _.includes(form.departments, index); + }).map(department => department.id); + + this.setState({loading: true}); + + API.call({ + path: '/staff/invite', + data: { + name: form.name, + email: form.email, + level: form.level + 1, + departments: JSON.stringify(departments) + } + }).then(() => { + this.context.closeModal(); + + if(this.props.onSuccess) { + this.props.onSuccess(); + } + }).catch((result) => { + this.setState({ + loading: false, + error: result.message + }); + }); + } + + onCancelClick(event) { + event.preventDefault(); + this.context.closeModal(); + } + + getErrors() { + let errors = _.extend({}, this.state.errors); + + if (this.state.error === 'ALREADY_A_STAFF') { + errors.email = i18n('EMAIL_EXISTS'); + } + + return errors; + } +} + +export default InviteStaffModal; diff --git a/client/src/app/admin/panel/staff/invite-staff-modal.scss b/client/src/app/admin/panel/staff/invite-staff-modal.scss new file mode 100644 index 00000000..60c35e1c --- /dev/null +++ b/client/src/app/admin/panel/staff/invite-staff-modal.scss @@ -0,0 +1,23 @@ +@import "../../../../scss/vars"; + +.invite-staff-modal { + width: 700px; + + &__level-selector { + text-align: center; + } + + &__departments { + @include scrollbars(); + + border: 1px solid $grey; + padding: 20px; + height: 320px; + overflow-y: auto; + } + + &__departments-title { + font-size: $font-size--md; + text-align: center; + } +} \ No newline at end of file diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index c671f716..f331f06d 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -195,6 +195,7 @@ export default { 'HIMSELF': 'himself', 'ADD_USER': 'Add user', 'INVITE_USER': 'Invite user', + 'INVITE_STAFF': 'Invite staff', 'UPLOAD_FILE': 'Upload file', 'PRIVATE': 'Private', 'ENABLE_USER': 'Enable User', @@ -340,6 +341,7 @@ export default { 'NEW_CUSTOM_FIELD_DESCRIPTION': 'Here you can create a custom field for an user, it can be a blank text box or a fixed set of options.', 'CUSTOM_FIELDS_DESCRIPTION': 'Custom fields are defined additional fields the users are able to fill to provide more information about them.', 'INVITE_USER_VIEW_DESCRIPTION': 'Here you can invite an user to join our support system, he will just need to provide his password to create a new user.', + 'INVITE_STAFF_DESCRIPTION': 'Here you can invite staff members to your teams.', //ERRORS 'EMAIL_OR_PASSWORD': 'Email or password invalid', diff --git a/server/controllers/staff.php b/server/controllers/staff.php index 997c5f25..e2667f87 100755 --- a/server/controllers/staff.php +++ b/server/controllers/staff.php @@ -9,7 +9,8 @@ $systemControllerGroup->addController(new GetTicketStaffController); $systemControllerGroup->addController(new GetNewTicketsStaffController); $systemControllerGroup->addController(new GetAllTicketsStaffController); $systemControllerGroup->addController(new SearchTicketStaffController); -$systemControllerGroup->addController(new AddStaffController); +// $systemControllerGroup->addController(new AddStaffController); +$systemControllerGroup->addController(new InviteStaffController); $systemControllerGroup->addController(new GetAllStaffController); $systemControllerGroup->addController(new DeleteStaffController); $systemControllerGroup->addController(new EditStaffController); diff --git a/server/controllers/staff/invite.php b/server/controllers/staff/invite.php new file mode 100755 index 00000000..6c1e41dd --- /dev/null +++ b/server/controllers/staff/invite.php @@ -0,0 +1,147 @@ + 'staff_3', + 'requestData' => [ + 'name' => [ + 'validation' => DataValidator::length(2, 55), + 'error' => ERRORS::INVALID_NAME + ], + 'email' => [ + 'validation' => DataValidator::email(), + 'error' => ERRORS::INVALID_EMAIL + ], + 'level' => [ + 'validation' => DataValidator::between(1, 3, true), + 'error' => ERRORS::INVALID_LEVEL + ] + ] + ]; + } + + public function handler() { + $this->storeRequestData(); + + $staffRow = Staff::getDataStore($this->email, 'email'); + + if(!$staffRow->isNull()) throw new RequestException(ERRORS::ALREADY_A_STAFF); + + if(!MailSender::getInstance()->isConnected()) throw new RequestException(ERRORS::MAIL_SENDER_NOT_CONNECTED); + + $staff = new Staff(); + $staff->setProperties([ + 'name'=> $this->name, + 'email' => $this->email, + 'password'=> Hashing::hashPassword(Hashing::generateRandomToken()), + 'profilePic' => $this->profilePic, + 'level' => $this->level, + 'sharedDepartmentList' => $this->getDepartmentList() + ]); + + $this->addOwner(); + + $this->token = Hashing::generateRandomToken(); + + $recoverPassword = new RecoverPassword(); + $recoverPassword->setProperties(array( + 'email' => $this->email, + 'token' => $this->token, + 'staff' => true + )); + $recoverPassword->store(); + + $this->sendInvitationMail(); + + Response::respondSuccess([ + 'id' => $staff->store() + ]); + + // TODO: Log::createLog('ADD_STAFF', $this->name); + } + + public function storeRequestData() { + $this->name = Controller::request('name'); + $this->email = Controller::request('email'); + $this->profilePic = Controller::request('profilePic'); + $this->level = Controller::request('level'); + $this->departments = Controller::request('departments'); + } + + public function getDepartmentList() { + $listDepartments = new DataStoreList(); + $departmentIds = json_decode($this->departments); + + foreach($departmentIds as $id) { + $department = Department::getDataStore($id); + $listDepartments->add($department); + } + + return $listDepartments; + } + + public function addOwner() { + $departmentIds = json_decode($this->departments); + + foreach($departmentIds as $id) { + $departmentRow = Department::getDataStore($id); + $departmentRow->owners++; + $departmentRow->store(); + } + } + + public function sendInvitationMail() { + $mailSender = MailSender::getInstance(); + + $mailSender->setTemplate(MailTemplate::USER_INVITE, [ + 'to' => $this->email, + 'name' => $this->name, + 'url' => Setting::getSetting('url')->getValue(), + 'token' => $this->token + ]); + + $mailSender->send(); + } +} \ No newline at end of file From 643f3c81a9cd45ccff091b435d29c42137f7ab96 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 29 Oct 2019 18:23:20 -0300 Subject: [PATCH 10/50] Minor style changes --- client/src/app/admin/panel/staff/add-staff-modal.js | 2 +- server/controllers/staff/add.php | 5 ++--- server/controllers/user/send-recover-password.php | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/src/app/admin/panel/staff/add-staff-modal.js b/client/src/app/admin/panel/staff/add-staff-modal.js index 8a20ed00..d9c77a0d 100644 --- a/client/src/app/admin/panel/staff/add-staff-modal.js +++ b/client/src/app/admin/panel/staff/add-staff-modal.js @@ -67,7 +67,7 @@ class AddStaffModal extends React.Component { return SessionStore.getDepartments().map(department => { if(department.private*1){ return {department.name} - }else { + } else { return department.name; } }); diff --git a/server/controllers/staff/add.php b/server/controllers/staff/add.php index 65aa1b2e..ea0a52cb 100755 --- a/server/controllers/staff/add.php +++ b/server/controllers/staff/add.php @@ -44,7 +44,6 @@ class AddStaffController extends Controller { private $level; private $departments; - public function validations() { return [ 'permission' => 'staff_3', @@ -74,7 +73,7 @@ class AddStaffController extends Controller { $this->storeRequestData(); $staff = new Staff(); - $staffRow = Staff::getDataStore($this->email,'email'); + $staffRow = Staff::getDataStore($this->email, 'email'); if($staffRow->isNull()) { $staff->setProperties([ @@ -84,7 +83,6 @@ class AddStaffController extends Controller { 'profilePic' => $this->profilePic, 'level' => $this->level, 'sharedDepartmentList' => $this->getDepartmentList() - ]); $this->addOwner(); @@ -120,6 +118,7 @@ class AddStaffController extends Controller { return $listDepartments; } + public function addOwner() { $departmentIds = json_decode($this->departments); diff --git a/server/controllers/user/send-recover-password.php b/server/controllers/user/send-recover-password.php index 3ba9d2b6..04d227dc 100755 --- a/server/controllers/user/send-recover-password.php +++ b/server/controllers/user/send-recover-password.php @@ -57,9 +57,9 @@ class SendRecoverPasswordController extends Controller { $email = Controller::request('email'); if($this->staff){ - $this->user = Staff::getUser($email,'email'); - }else { - $this->user = User::getUser($email,'email'); + $this->user = Staff::getUser($email, 'email'); + } else { + $this->user = User::getUser($email, 'email'); } if(!$this->user->isNull()) { From afa76ce059d264721535a70ab295d42229718ea7 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 31 Oct 2019 11:19:17 -0300 Subject: [PATCH 11/50] Adds MailTexts for the new mail template USER_INVITE for all languages --- server/controllers/user/recover-password.php | 28 ++++--- server/data/MailTexts.php | 84 ++++++++++++++++++++ 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/server/controllers/user/recover-password.php b/server/controllers/user/recover-password.php index 9e712493..da2bb87d 100755 --- a/server/controllers/user/recover-password.php +++ b/server/controllers/user/recover-password.php @@ -69,30 +69,34 @@ class RecoverPasswordController extends Controller { $this->token = Controller::request('token'); $this->password = Controller::request('password'); } + public function changePassword() { $recoverPassword = RecoverPassword::getDataStore($this->token, 'token'); + if($recoverPassword->isNull() || $recoverPassword->email !== $this->email) { + throw new RequestException(ERRORS::NO_PERMISSION); + } + if($recoverPassword->staff) { $this->user = Staff::getDataStore($this->email, 'email'); - }else { + } else { $this->user = User::getDataStore($this->email, 'email'); } - if (!$recoverPassword->isNull() && !$this->user->isNull()) { - $recoverPassword->delete(); + if($this->user->isNull()) throw new RequestException(ERRORS::NO_PERMISSION); - $this->user->setProperties([ - 'password' => Hashing::hashPassword($this->password) - ]); + $recoverPassword->delete(); - $this->user->store(); + $this->user->setProperties([ + 'password' => Hashing::hashPassword($this->password) + ]); - $this->sendMail(); - Response::respondSuccess(['staff' => $recoverPassword->staff]); - } else { - throw new RequestException(ERRORS::NO_PERMISSION); - } + $this->user->store(); + + $this->sendMail(); + Response::respondSuccess(['staff' => $recoverPassword->staff]); } + public function sendMail() { $mailSender = MailSender::getInstance(); diff --git a/server/data/MailTexts.php b/server/data/MailTexts.php index 68376be3..527e14cd 100644 --- a/server/data/MailTexts.php +++ b/server/data/MailTexts.php @@ -91,6 +91,12 @@ class MailTexts { '喂 {{name}}。 您已要求恢复密码。', '使用此代码 {{url}}/recover-password?email={{to}}&token={{token}} 或单击下面的按钮.', ], + 'USER_INVITE' => [ + '您已受邀 - OpenSupports', + '您已受邀', + '你好, {{name}}. 邀请您加入我们的支持中心.', + '使用此代码 {{url}}/recover-password?email={{to}}&token={{token}}&invited=true 或单击下面的按钮来设置密码.' + ], 'USER_SYSTEM_DISABLED' => [ '访问系统更改 - OpenSupports', '访问系统更改', @@ -151,6 +157,12 @@ class MailTexts { 'Hallo, {{name}}. Sie haben aufgefordert, Ihr Passwort wiederherzustellen.', 'Verwenden Sie diesen Code in {{url}}/recover-password?email={{to}}&token={{token}} oder klicken Sie auf die Schaltfläche unten.', ], + 'USER_INVITE' => [ + 'Du bist eingeladen - OpenSupports', + 'Du bist eingeladen', + 'Hallo, {{name}}. Sie wurden zu unserem Support-Center eingeladen.', + 'Verwenden Sie diesen Code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true oder klicken Sie auf die Schaltfläche unten, um Ihr Passwort einzurichten.' + ], 'USER_SYSTEM_DISABLED' => [ 'Access system changed - OpenSupports', 'Zugriffssystem geändert', @@ -211,6 +223,12 @@ class MailTexts { 'Hola, {{name}}. Has requerido recuperar tu contraseña.', 'Usá este codigo en {{url}}/recover-password?email={{to}}&token={{token}} o hacé click en el botón de abajo.', ], + 'USER_INVITE' => [ + 'Haz sido invitado - OpenSupports', + 'Haz sido invitado', + 'Hola, {{name}}. Haz sido invitado a unirte a nuestro sistema de soporte.', + 'Usa este código en {{url}}/recover-password?email={{to}}&token={{token}}&invited=true o haz click en el botón de abajo para establecer tu contraseña.' + ], 'USER_SYSTEM_DISABLED' => [ 'Sistema de acceso cambiado - OpenSupports', 'Sistema de acceso cambiado', @@ -271,6 +289,12 @@ class MailTexts { 'Salut, {{name}}. Vous avez demandé à récupérer votre mot de passe.', 'Utilisez ce code dans {{url}}/recover-password?email={{to}}&token={{token}} ou cliquez sur le bouton ci-dessous.', ], + 'USER_INVITE' => [ + 'You have been invited - OpenSupports', + 'You have been invited', + 'Hi, {{name}}. You have been invited to join our support center.', + 'Use this code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true or click the button below to set up your password.' + ], 'USER_SYSTEM_DISABLED' => [ 'Système d\'accès modifié - OpenSupports', 'Système d\'accès modifié', @@ -331,6 +355,12 @@ class MailTexts { 'नमस्ते {{name}}. आपने अपना पासवर्ड पुनर्प्राप्त करने का अनुरोध किया है', 'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}} या नीचे दिए गए बटन पर क्लिक करें.', ], + 'USER_INVITE' => [ + 'आपको आमंत्रित किया गया है - OpenSupports', + 'आपको आमंत्रित किया गया है', + 'नमस्ते, {{name}}. आपको हमारे सहायता केंद्र से जुड़ने के लिए आमंत्रित किया गया है.', + 'इस कोड का उपयोग करें {{url}}/recover-password?email={{to}}&token={{token}}&invited=true या अपना पासवर्ड सेट करने के लिए नीचे दिए गए बटन पर क्लिक करें.' + ], 'USER_SYSTEM_DISABLED' => [ 'sistem akses berubah - OpenSupports', 'एक्सेस सिस्टम बदल गया', @@ -391,6 +421,12 @@ class MailTexts { 'Ciao, {{name}}. Hai richiesto di recuperare la tua password.', 'Clicca sul link {{url}}/recover-password?email={{to}}&token={{token}} o clicca sul pulsante qui sotto.', ], + 'USER_INVITE' => [ + 'Sei stato invitato - OpenSupports', + 'Sei stato invitato', + 'Ciao, {{name}}. Sei stato invitato a far parte del nostro centro di supporto.', + 'Usa questo codice in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true oppure fai clic sul pulsante in basso per impostare la password.' + ], 'USER_SYSTEM_DISABLED' => [ 'Il sistema di accesso è cambiato - OpenSupports', 'Modifica sistema di accesso', @@ -451,6 +487,12 @@ class MailTexts { 'こんにちは、{{name}}。 パスワードの回復を要求しました。', 'でこのコードを使用 {{url}}/recover-password?email={{to}}&token={{token}} 下のボタンをクリックしてください.', ], + 'USER_INVITE' => [ + '招待されました - OpenSupports', + '招待されました', + 'こんにちは, {{name}}. サポートセンターに招待されました.', + 'このコードを {{url}}/recover-password?email={{to}}&token={{token}}&invited=true または、下のボタンをクリックしてパスワードを設定します.' + ], 'USER_SYSTEM_DISABLED' => [ 'アクセスシステムが変更されました - OpenSupports', 'アクセスシステムが変更されました', @@ -511,6 +553,12 @@ class MailTexts { 'Olá, {{name}}. Você solicitou a recuperação da sua senha.', 'Use este código em {{url}}/recover-password?email={{to}}&token={{token}} ou clique no botão abaixo.', ], + 'USER_INVITE' => [ + 'Você foi convidado - OpenSupports', + 'Você foi convidado', + 'Oi, {{name}}. Você foi convidado a participar do nosso centro de suporte.', + 'Use este código em {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ou clique no botão abaixo para configurar sua senha.' + ], 'USER_SYSTEM_DISABLED' => [ 'Sistema de acesso alterado - OpenSupports', 'Sistema de acesso alterado', @@ -571,6 +619,12 @@ class MailTexts { 'Здравствуй, {{name}}. Вы запросили восстановить пароль.', 'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}} или нажмите кнопку ниже.', ], + 'USER_INVITE' => [ + 'Вы были приглашены - OpenSupports', + 'Вы были приглашены', + 'Здравствуй, {{name}}. Вас пригласили присоединиться к нашему центру поддержки.', + 'Используйте этот код в {{url}}/recover-password?email={{to}}&token={{token}}&invited=true или нажмите кнопку ниже, чтобы установить пароль.' + ], 'USER_SYSTEM_DISABLED' => [ 'Система доступа изменена - OpenSupports', 'Система доступа изменена', @@ -631,6 +685,12 @@ class MailTexts { 'Merhaba, {{name}}. Şifrenizi geri yüklemenizi istediniz.', 'Bu kodu şu adreste kullanın {{url}}/recover-password?email={{to}}&token={{token}} veya aşağıdaki butona tıklayın.', ], + 'USER_INVITE' => [ + 'Davet edildin - OpenSupports', + 'Davet edildin', + 'Merhaba, {{name}}. Destek merkezimize katılmaya davet edildiniz.', + 'Bu kodu {{url}}/recover-password?email={{to}}&token={{token}}&invited=true veya şifrenizi ayarlamak için aşağıdaki butona tıklayın.' + ], 'USER_SYSTEM_DISABLED' => [ 'Erişim sistemi değiştirildi - OpenSupports', 'Erişim sistemi değiştirildi', @@ -691,6 +751,12 @@ class MailTexts { 'Olá, {{name}}. Você solicitou a recuperação da sua senha.', 'Use este código em {{url}}/recover-password?email={{to}}&token={{token}} ou clique no botão abaixo.', ], + 'USER_INVITE' => [ + 'Você foi convidado - OpenSupports', + 'Você foi convidado', + 'Oi, {{name}}. Você foi convidado a participar do nosso centro de suporte.', + 'Use este código em {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ou clique no botão abaixo para configurar sua senha.' + ], 'USER_SYSTEM_DISABLED' => [ 'Sistema de acesso alterado - OpenSupports', 'Sistema de acesso alterado', @@ -751,6 +817,12 @@ class MailTexts { 'Γεια σου, {{name}}. Ζητήσατε να ανακτήσετε τον κωδικό πρόσβασής σας.', 'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}} / recover-password? Email = {{to}} & token = {{token}} ή κάντε κλικ στο παρακάτω κουμπί.', ], + 'USER_INVITE' => [ + 'Έχετε προσκληθεί - OpenSupports', + 'Έχετε προσκληθεί', + 'Γεια σου, {{name}}. Έχετε προσκληθεί να συμμετάσχετε στο κέντρο υποστήριξής μας.', + 'Χρησιμοποιήστε αυτόν τον κωδικό στο {{url}}/recover-password?email={{to}}&token={{token}}&invited=true ή κάντε κλικ στο παρακάτω κουμπί για να ρυθμίσετε τον κωδικό πρόσβασής σας.' + ], 'USER_SYSTEM_DISABLED' => [ 'Το σύστημα πρόσβασης άλλαξε - OpenSupports', 'Το σύστημα πρόσβασης άλλαξε', @@ -811,6 +883,12 @@ class MailTexts { 'Hallo, {{name}}. U heeft een verzoek gedaan om uw wachtwoord te resetten.', 'Gebruik deze code {{url}}/recover-password?email={{to}}&token={{token}} of klik op de knop hieronder.' ], + 'USER_INVITE' => [ + 'Je bent uitgenodigd - OpenSupports', + 'Je bent uitgenodigd', + 'Hallo, {{name}}. U bent uitgenodigd om lid te worden van ons ondersteuningscentrum.', + 'Gebruik deze code in {{url}}/recover-password?email={{to}}&token={{token}}&invited=true of klik op de onderstaande knop om uw wachtwoord in te stellen.' + ], 'USER_SYSTEM_DISABLED' => [ 'Toegangssysteem gewijzigd - OpenSupports', 'Toegang tot incidenten is gewijzigd', @@ -871,6 +949,12 @@ class MailTexts { 'Hej, {{name}}. Zażądałeś odzyskania hasła.', 'Użyj tego linka {{url}}/recover-password?email={{to}}&token={{token}} lub kliknij przycisk poniżej.', ], + 'USER_INVITE' => [ + 'Zostałeś zaproszony - OpenSupports', + 'Zostałeś zaproszony', + 'Hej, {{name}}. Zaproszono Cię do dołączenia do naszego centrum wsparcia.', + 'Użyj tego kodu w {{url}}/recover-password?email={{to}}&token={{token}}&invited=true lub kliknij przycisk poniżej, aby ustawić hasło.' + ], 'USER_SYSTEM_DISABLED' => [ 'Zmieniono dostęp do systemu - OpenSupports', 'Zmieniono dostęp do systemu', From 87e2984fa4e4de3cb028c41f4334b6ed9ad11f65 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 31 Oct 2019 16:13:04 -0300 Subject: [PATCH 12/50] Removes /staff/add path, updates tests accordingly, and adds log when a staff member is invited --- server/controllers/staff/add.php | 131 ---------------------------- server/controllers/staff/invite.php | 2 +- tests/init.rb | 2 +- tests/scripts.rb | 17 +++- tests/staff/edit.rb | 11 ++- tests/staff/{add.rb => invite.rb} | 20 +++-- tests/ticket/comment.rb | 14 ++- tests/ticket/delete.rb | 37 +++++--- 8 files changed, 76 insertions(+), 158 deletions(-) delete mode 100755 server/controllers/staff/add.php rename tests/staff/{add.rb => invite.rb} (77%) diff --git a/server/controllers/staff/add.php b/server/controllers/staff/add.php deleted file mode 100755 index ea0a52cb..00000000 --- a/server/controllers/staff/add.php +++ /dev/null @@ -1,131 +0,0 @@ - 'staff_3', - 'requestData' => [ - 'name' => [ - 'validation' => DataValidator::length(2, 55), - 'error' => ERRORS::INVALID_NAME - ], - 'email' => [ - 'validation' => DataValidator::email(), - 'error' => ERRORS::INVALID_EMAIL - ], - 'password' => [ - 'validation' => DataValidator::length(5, 200), - 'error' => ERRORS::INVALID_PASSWORD - ], - 'level' => [ - 'validation' => DataValidator::between(1, 3, true), - 'error' => ERRORS::INVALID_LEVEL - ] - - ] - ]; - } - - public function handler() { - $this->storeRequestData(); - $staff = new Staff(); - - $staffRow = Staff::getDataStore($this->email, 'email'); - - if($staffRow->isNull()) { - $staff->setProperties([ - 'name'=> $this->name, - 'email' => $this->email, - 'password'=> Hashing::hashPassword($this->password), - 'profilePic' => $this->profilePic, - 'level' => $this->level, - 'sharedDepartmentList' => $this->getDepartmentList() - ]); - - $this->addOwner(); - - Log::createLog('ADD_STAFF', $this->name); - - Response::respondSuccess([ - 'id' => $staff->store() - ]); - return; - } - - throw new RequestException(ERRORS::ALREADY_A_STAFF); - } - - public function storeRequestData() { - $this->name = Controller::request('name'); - $this->email = Controller::request('email'); - $this->password = Controller::request('password'); - $this->profilePic = Controller::request('profilePic'); - $this->level = Controller::request('level'); - $this->departments = Controller::request('departments'); - } - - public function getDepartmentList() { - $listDepartments = new DataStoreList(); - $departmentIds = json_decode($this->departments); - - foreach($departmentIds as $id) { - $department = Department::getDataStore($id); - $listDepartments->add($department); - } - - return $listDepartments; - } - - public function addOwner() { - $departmentIds = json_decode($this->departments); - - foreach($departmentIds as $id) { - $departmentRow = Department::getDataStore($id); - $departmentRow->owners++; - $departmentRow->store(); - } - } -} \ No newline at end of file diff --git a/server/controllers/staff/invite.php b/server/controllers/staff/invite.php index 6c1e41dd..ce1748e5 100755 --- a/server/controllers/staff/invite.php +++ b/server/controllers/staff/invite.php @@ -99,7 +99,7 @@ class InviteStaffController extends Controller { 'id' => $staff->store() ]); - // TODO: Log::createLog('ADD_STAFF', $this->name); + Log::createLog('INVITE', $this->name); } public function storeRequestData() { diff --git a/tests/init.rb b/tests/init.rb index 5f3ef8a0..ffffdcb7 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -33,7 +33,7 @@ require './ticket/change-department.rb' require './ticket/close.rb' require './ticket/re-open.rb' require './ticket/delete.rb' -require './staff/add.rb' +require './staff/invite.rb' require './staff/get.rb' require './staff/edit.rb' require './staff/delete.rb' diff --git a/tests/scripts.rb b/tests/scripts.rb index 7c1e7bf0..e0c7e24c 100644 --- a/tests/scripts.rb +++ b/tests/scripts.rb @@ -16,25 +16,33 @@ class Scripts }) end - def self.createStaff(email, password, name, level='1') + def self.createStaff(email, password, name, level='1') # WARNING: NOT USED ANYWHERE departments = request('/system/get-settings', { csrf_userid: $csrf_userid, csrf_token: $csrf_token })['data']['departments'] departments = departments.collect { |x| x.id } - response = request('/staff/add', { + response = request('/staff/invite', { :name => name, :email => email, - :password => password, :level => level, :departments => departments.to_string }) + recoverpassword = $database.getRow('recoverpassword', email, 'email') + + response = request('/user/recover-password', { + email: email, + password: password, + token: recoverpassword['token'] + }) + if response['status'] === 'fail' raise response['message'] end end + def self.deleteStaff(staffId) response = request('/staff/delete', { staffId: staffId, @@ -106,6 +114,7 @@ class Scripts description: description }) end + def self.createTag(name, color) request('/ticket/create-tag', { csrf_userid: $csrf_userid, @@ -114,6 +123,7 @@ class Scripts color: color }) end + def self.assignTicket(ticketnumber) request('/staff/assign-ticket', { ticketNumber: ticketnumber, @@ -121,6 +131,7 @@ class Scripts csrf_token: $csrf_token }) end + def self.commentTicket(ticketnumber,content) request('/ticket/comment', { content: content, diff --git a/tests/staff/edit.rb b/tests/staff/edit.rb index de00c46a..3afc28a2 100644 --- a/tests/staff/edit.rb +++ b/tests/staff/edit.rb @@ -32,17 +32,24 @@ describe'/staff/edit' do end it 'should edit own data staff' do - request('/staff/add', { + request('/staff/invite', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, name: 'Arya Stark', - password: 'starkpassword', email: 'arya@opensupports.com', level: 1, profilePic: '', departments: '[1]' }) + recoverpassword = $database.getRow('recoverpassword', 'arya@opensupports.com', 'email') + + request('/user/recover-password', { + email: 'arya@opensupports.com', + password: 'starkpassword', + token: recoverpassword['token'] + }) + row = $database.getRow('staff', 'arya@opensupports.com', 'email') result = request('/staff/edit', { diff --git a/tests/staff/add.rb b/tests/staff/invite.rb similarity index 77% rename from tests/staff/add.rb rename to tests/staff/invite.rb index 92b3a92c..fd928e68 100644 --- a/tests/staff/add.rb +++ b/tests/staff/invite.rb @@ -1,21 +1,28 @@ -describe'/staff/add' do +describe'/staff/invite' do request('/user/logout') Scripts.login($staff[:email], $staff[:password], true) it 'should add staff member' do - result= request('/staff/add', { + + result = request('/staff/invite', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, name: 'Tyrion Lannister', email: 'tyrion@opensupports.com', - password: 'testpassword', level: 2, profilePic: '', departments: '[1]' }) - (result['status']).should.equal('success') + recoverpassword = $database.getRow('recoverpassword', 'tyrion@opensupports.com', 'email') + + request('/user/recover-password', { + email: 'tyrion@opensupports.com', + password: 'testpassword', + token: recoverpassword['token'] + }) + row = $database.getRow('staff', result['data']['id'], 'id') (row['name']).should.equal('Tyrion Lannister') @@ -27,16 +34,15 @@ describe'/staff/add' do (row['owners']).should.equal('4') lastLog = $database.getLastRow('log') - (lastLog['type']).should.equal('ADD_STAFF') + (lastLog['type']).should.equal('INVITE') end it 'should fail if staff member is alrady a staff' do - result= request('/staff/add', { + result = request('/staff/invite', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, name: 'Tyrion Lannister', email: 'tyrion@opensupports.com', - password: 'testpassword', level: 2, profilePic: '', departments: '[1]' diff --git a/tests/ticket/comment.rb b/tests/ticket/comment.rb index 8a0e1ab4..c5de7fc6 100644 --- a/tests/ticket/comment.rb +++ b/tests/ticket/comment.rb @@ -182,18 +182,28 @@ describe '/ticket/comment/' do request('/user/logout') Scripts.login($staff[:email], $staff[:password], true) - request('/staff/add', { + + result = request('/staff/invite', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, name: 'Jorah mormont', email: 'jorah@opensupports.com', - password: 'testpassword', level: 2, profilePic: '', departments: '[1]' }) + (result['status'].should.equal('success')) + request('/user/logout') + + recoverpassword = $database.getRow('recoverpassword', 'jorah@opensupports.com', 'email') + request('/user/recover-password', { + email: 'jorah@opensupports.com', + password: 'testpassword', + token: recoverpassword['token'] + }) + Scripts.login('jorah@opensupports.com', 'testpassword', true) result = request('/ticket/comment', { content: 'some comment content', diff --git a/tests/ticket/delete.rb b/tests/ticket/delete.rb index 6b0c5acf..c2add08c 100644 --- a/tests/ticket/delete.rb +++ b/tests/ticket/delete.rb @@ -6,17 +6,24 @@ describe '/ticket/delete' do Scripts.createTicket('ticket_to_delete') ticket = $database.getRow('ticket', 'ticket_to_delete', 'title') - request('/staff/add', { + request('/staff/invite', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, name: 'Ned Stark', - password: 'headless', email: 'ned@opensupports.com', level: 3, profilePic: '', departments: '[1]' }) + recoverpassword = $database.getRow('recoverpassword', 'ned@opensupports.com', 'email') + + request('/user/recover-password', { + email: 'ned@opensupports.com', + password: 'headless', + token: recoverpassword['token'] + }) + request('/user/logout') Scripts.login('ned@opensupports.com', 'headless', true) @@ -80,16 +87,24 @@ describe '/ticket/delete' do ticket = $database.getRow('ticket', 'ticket_to_delete_4', 'title'); - request('/staff/add', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'Joan Chris', - password: 'theyaregonnafireme', - email: 'uselessstaff@opensupports.com', - level: 2, - profilePic: '', - departments: '[1]' + request('/staff/invite', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'Joan Chris', + email: 'uselessstaff@opensupports.com', + level: 2, + profilePic: '', + departments: '[1]' }) + + recoverpassword = $database.getRow('recoverpassword', 'uselessstaff@opensupports.com', 'email') + + request('/user/recover-password', { + email: 'uselessstaff@opensupports.com', + password: 'theyaregonnafireme', + token: recoverpassword['token'] + }) + request('/user/logout') Scripts.login('uselessstaff@opensupports.com', 'theyaregonnafireme',true) From 364f10f03a43f70d54ad2eb7ff1315adc261ef25 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 31 Oct 2019 16:15:16 -0300 Subject: [PATCH 13/50] Removes outdated commented line adding the /staff/add controller --- server/controllers/staff.php | 1 - 1 file changed, 1 deletion(-) diff --git a/server/controllers/staff.php b/server/controllers/staff.php index e2667f87..1d3e3809 100755 --- a/server/controllers/staff.php +++ b/server/controllers/staff.php @@ -9,7 +9,6 @@ $systemControllerGroup->addController(new GetTicketStaffController); $systemControllerGroup->addController(new GetNewTicketsStaffController); $systemControllerGroup->addController(new GetAllTicketsStaffController); $systemControllerGroup->addController(new SearchTicketStaffController); -// $systemControllerGroup->addController(new AddStaffController); $systemControllerGroup->addController(new InviteStaffController); $systemControllerGroup->addController(new GetAllStaffController); $systemControllerGroup->addController(new DeleteStaffController); From 431ef44c8da88a4a8f57a8ab73f1220cfc6c2497 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 7 Nov 2019 15:27:26 -0300 Subject: [PATCH 14/50] Makes users able to comment on tickets when there is no user system, and fixes logs --- client/src/app-components/activity-row.js | 16 ++++++++++++--- server/controllers/ticket/comment.php | 25 ++++++++++++----------- server/models/Ticketevent.php | 5 +++-- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/client/src/app-components/activity-row.js b/client/src/app-components/activity-row.js index cb5dec18..ebde5f44 100644 --- a/client/src/app-components/activity-row.js +++ b/client/src/app-components/activity-row.js @@ -65,9 +65,7 @@ class ActivityRow extends React.Component {
- - {this.props.author.name} - + {this.renderAuthorName()} {i18n('ACTIVITY_' + this.props.type)} {_.includes(ticketRelatedTypes, this.props.type) ? this.renderTicketNumber() : this.props.to} @@ -76,6 +74,18 @@ class ActivityRow extends React.Component { ); } + renderAuthorName() { + let name = this.props.author.name; + + if (this.props.author.id) { + name = + {this.props.author.name} + ; + } + + return name; + } + renderTicketNumber() { let ticketNumber = (this.props.mode === 'staff') ? this.props.ticketNumber : this.props.to; diff --git a/server/controllers/ticket/comment.php b/server/controllers/ticket/comment.php index de6e2c85..f72a9e5c 100755 --- a/server/controllers/ticket/comment.php +++ b/server/controllers/ticket/comment.php @@ -37,9 +37,10 @@ class CommentController extends Controller { private $ticket; private $content; + private $session; public function validations() { - $session = Session::getInstance(); + $this->session = Session::getInstance(); if (Controller::isUserSystemEnabled() || Controller::isStaffLogged()) { return [ @@ -64,11 +65,11 @@ class CommentController extends Controller { 'error' => ERRORS::INVALID_CONTENT ], 'ticketNumber' => [ - 'validation' => DataValidator::equals($session->getTicketNumber()), + 'validation' => DataValidator::equals($this->session->getTicketNumber()), 'error' => ERRORS::INVALID_TICKET ], 'csrf_token' => [ - 'validation' => DataValidator::equals($session->getToken()), + 'validation' => DataValidator::equals($this->session->getToken()), 'error' => ERRORS::INVALID_TOKEN ] ] @@ -78,16 +79,16 @@ class CommentController extends Controller { public function handler() { $this->requestData(); + $this->user = Controller::getLoggedUser(); $ticketAuthor = $this->ticket->authorToArray(); - $isAuthor = $this->ticket->isAuthor(Controller::getLoggedUser()) || Session::getInstance()->isTicketSession(); - $isOwner = $this->ticket->isOwner(Controller::getLoggedUser()); - $user = Controller::getLoggedUser(); + $isAuthor = $this->ticket->isAuthor($this->user) || $this->session->isTicketSession(); + $isOwner = $this->ticket->isOwner($this->user); if(!Controller::isStaffLogged() && Controller::isUserSystemEnabled() && !$isAuthor){ throw new RequestException(ERRORS::NO_PERMISSION); } - if(!$user->canManageTicket($this->ticket)) { + if(!$this->session->isTicketSession() && !$this->user->canManageTicket($this->ticket)) { throw new RequestException(ERRORS::NO_PERMISSION); } @@ -100,7 +101,7 @@ class CommentController extends Controller { 'staff' => true ]); } else if($isOwner) { - !Controller::request('private') ? $this->sendMail($ticketAuthor) : null; + !Controller::request('private') ? $this->sendMail($ticketAuthor) : null; } Log::createLog('COMMENT', $this->ticket->ticketNumber); @@ -129,12 +130,12 @@ class CommentController extends Controller { )); if(Controller::isStaffLogged()) { - $this->ticket->unread = !$this->ticket->isAuthor(Controller::getLoggedUser()); - $this->ticket->unreadStaff = !$this->ticket->isOwner(Controller::getLoggedUser()); - $comment->authorStaff = Controller::getLoggedUser(); + $this->ticket->unread = !$this->ticket->isAuthor($this->user); + $this->ticket->unreadStaff = !$this->ticket->isOwner($this->user); + $comment->authorStaff = $this->user; } else if(Controller::isUserSystemEnabled()) { $this->ticket->unreadStaff = true; - $comment->authorUser = Controller::getLoggedUser(); + $comment->authorUser = $this->user; } $this->ticket->addEvent($comment); diff --git a/server/models/Ticketevent.php b/server/models/Ticketevent.php index d22b961d..9daf2560 100755 --- a/server/models/Ticketevent.php +++ b/server/models/Ticketevent.php @@ -83,15 +83,16 @@ class Ticketevent extends DataStore { public function toArray() { $user = ($this->authorStaff) ? $this->authorStaff : $this->authorUser; + $author = $this->ticket->authorToArray(); return [ 'type' => $this->type, 'ticketNumber' => $this->ticket->ticketNumber, 'author' => [ - 'name' => $user ? $user->name : null, + 'name' => $user ? $user->name : $author['name'], 'staff' => $user instanceOf Staff, 'id' => $user ? $user->id : null, - 'customfields' => $user->xownCustomfieldvalueList ? $user->xownCustomfieldvalueList->toArray() : [], + 'customfields' => $user && $user->xownCustomfieldvalueList ? $user->xownCustomfieldvalueList->toArray() : [], ], 'edited' => $this->editedContent ]; From f5f78a549c34c936625b1b15bd3ce92ccf0d00ac Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 7 Nov 2019 17:36:15 -0300 Subject: [PATCH 15/50] Adds test to manage tickets when the user system is disabled --- tests/system/disable-user-system.rb | 43 +++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index 11a6c33d..1d8c9105 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -65,6 +65,35 @@ describe'system/disable-user-system' do (result['status']).should.equal('success') end + it 'should be able to comment on ticket as a non-logged user' do + result = request('/ticket/create', { + title: 'Doubt about Russian language', + content: 'Stariy means old in Russian?', + departmentId: 1, + language: 'en', + name: 'Abraham Einstein', + email: 'abrahameinstein@opensupports.com' + }) + (result['status']).should.equal('success') + + ticketNumber = result['data']['ticketNumber'] + + result = request('/ticket/check', { + ticketNumber: ticketNumber, + email: 'abrahameinstein@opensupports.com', + captcha: 'valid' + }) + token = result['data']['token'] + (result['status']).should.equal('success'); + + result = request('/ticket/comment', { + content: 'I actually think it is not like that, but anyways, thanks', + ticketNumber: ticketNumber, + csrf_token: token + }) + (result['status']).should.equal('success') + end + it 'should be able to assign and respond tickets' do Scripts.login($staff[:email], $staff[:password], true); ticket = $database.getLastRow('ticket'); @@ -84,6 +113,16 @@ describe'system/disable-user-system' do (result['status']).should.equal('success') end + it 'should be able to get the latest events as admin' do + result = request('/staff/last-events', { + page: 1, + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + (result['status']).should.equal('success') + (result['data'].size).should.equal(10) + end + it 'should be be able to create a ticket as an admin' do result = request('/ticket/create', { title: 'created by staff with user system disabled', @@ -127,8 +166,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(52) - + (numberOftickets.num_rows).should.equal(53) end it 'should not enable the user system' do @@ -140,6 +178,5 @@ describe'system/disable-user-system' do (result['status']).should.equal('fail') (result['message']).should.equal('SYSTEM_USER_IS_ALREADY_ENABLED') - end end From bedf55d1ad1ee3fd31e65047ceead0a097102a3a Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 8 Nov 2019 10:08:14 -0300 Subject: [PATCH 16/50] Makes createLog function store authorName. --- server/models/Log.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/server/models/Log.php b/server/models/Log.php index a1d99687..c85c6ef3 100755 --- a/server/models/Log.php +++ b/server/models/Log.php @@ -22,13 +22,24 @@ class Log extends DataStore { 'authorUser', 'authorStaff', 'to', - 'date' + 'date', + 'authorName' ]; } - public static function createLog($type,$to, $author = null) { + public static function createLog($type, $to, $author = null) { + $session = Session::getInstance(); + $authorName = ''; + + if($session->isTicketSession()) { + $ticketNumber = $session->getTicketNumber(); + $ticket = Ticket::getByTicketNumber($ticketNumber); + $authorName = $ticket->authorToArray()['name']; + } + if($author === null) { $author = Controller::getLoggedUser(); + if(!$author->isNull()) $authorName = $author->name; } $log = new Log(); @@ -36,7 +47,8 @@ class Log extends DataStore { $log->setProperties(array( 'type' => $type, 'to' => $to, - 'date' => Date::getCurrentDate() + 'date' => Date::getCurrentDate(), + 'authorName' => $authorName )); if($author instanceof User) { @@ -55,8 +67,8 @@ class Log extends DataStore { 'type' => $this->type, 'to' => $this->to, 'author' => [ - 'name' => $author->name, - 'id' => $author->id, + 'name' => $this->authorName, + 'id' => !$author->isNull() ? $author->id : null, 'staff' => $author instanceof Staff ], 'date' => $this->date From a883f4d4307c317bc9bd2fd9e548a1a22e412456 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 8 Nov 2019 10:41:50 -0300 Subject: [PATCH 17/50] Creates ticket session when an unlogged user creates a ticket. Fixes runtime error when there is no author associated with a log. --- server/controllers/ticket/create.php | 8 +++++++- server/models/Log.php | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/server/controllers/ticket/create.php b/server/controllers/ticket/create.php index 432b6d08..1ea18790 100755 --- a/server/controllers/ticket/create.php +++ b/server/controllers/ticket/create.php @@ -115,10 +115,16 @@ class CreateController extends Controller { } } - Log::createLog('CREATE_TICKET', $this->ticketNumber); Response::respondSuccess([ 'ticketNumber' => $this->ticketNumber ]); + + if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) { + $session = Session::getInstance(); + $session->createTicketSession($this->ticketNumber); + } + + Log::createLog('CREATE_TICKET', $this->ticketNumber); } private function storeTicket() { diff --git a/server/models/Log.php b/server/models/Log.php index c85c6ef3..5112c124 100755 --- a/server/models/Log.php +++ b/server/models/Log.php @@ -30,7 +30,7 @@ class Log extends DataStore { public static function createLog($type, $to, $author = null) { $session = Session::getInstance(); $authorName = ''; - + if($session->isTicketSession()) { $ticketNumber = $session->getTicketNumber(); $ticket = Ticket::getByTicketNumber($ticketNumber); @@ -68,7 +68,7 @@ class Log extends DataStore { 'to' => $this->to, 'author' => [ 'name' => $this->authorName, - 'id' => !$author->isNull() ? $author->id : null, + 'id' => $author && !$author->isNull() ? $author->id : null, 'staff' => $author instanceof Staff ], 'date' => $this->date From fa5300a731ce1177723c9b683dc0fa3847459898 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 8 Nov 2019 10:57:25 -0300 Subject: [PATCH 18/50] Fix small issue, now all names of authors of logs are stored. --- server/models/Log.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/models/Log.php b/server/models/Log.php index 5112c124..3faf7ad6 100755 --- a/server/models/Log.php +++ b/server/models/Log.php @@ -39,7 +39,10 @@ class Log extends DataStore { if($author === null) { $author = Controller::getLoggedUser(); - if(!$author->isNull()) $authorName = $author->name; + } + + if(!$author->isNull()) { + $authorName = $author->name; } $log = new Log(); From 5ab155f8ce9bef1e0a608c207472560cf668b672 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 8 Nov 2019 11:06:47 -0300 Subject: [PATCH 19/50] Adds test to /system/get-logs --- tests/system/disable-user-system.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index 1d8c9105..04803742 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -122,6 +122,16 @@ describe'system/disable-user-system' do (result['status']).should.equal('success') (result['data'].size).should.equal(10) end + + it 'should be able to get system logs as admin' do + result = request('/system/get-logs', { + page: 1, + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + (result['status']).should.equal('success') + (result['data'].size).should.equal(10) + end it 'should be be able to create a ticket as an admin' do result = request('/ticket/create', { From 1c4bd7df178f3377473511a9b3da2793878f3869 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 8 Nov 2019 19:42:02 -0300 Subject: [PATCH 20/50] Fix bug to allow staff members to recover their passwords even if user system is disabled. --- server/controllers/user/recover-password.php | 8 ++--- .../user/send-recover-password.php | 5 ++-- tests/system/disable-user-system.rb | 29 +++++++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/server/controllers/user/recover-password.php b/server/controllers/user/recover-password.php index da2bb87d..077f52ed 100755 --- a/server/controllers/user/recover-password.php +++ b/server/controllers/user/recover-password.php @@ -56,10 +56,6 @@ class RecoverPasswordController extends Controller { } public function handler() { - if(!Controller::isUserSystemEnabled()) { - throw new RequestException(ERRORS::USER_SYSTEM_DISABLED); - } - $this->requestData(); $this->changePassword(); } @@ -77,6 +73,10 @@ class RecoverPasswordController extends Controller { throw new RequestException(ERRORS::NO_PERMISSION); } + if(!Controller::isUserSystemEnabled() && !$recoverPassword->staff) { + throw new RequestException(ERRORS::USER_SYSTEM_DISABLED); + } + if($recoverPassword->staff) { $this->user = Staff::getDataStore($this->email, 'email'); } else { diff --git a/server/controllers/user/send-recover-password.php b/server/controllers/user/send-recover-password.php index 04d227dc..8d925799 100755 --- a/server/controllers/user/send-recover-password.php +++ b/server/controllers/user/send-recover-password.php @@ -49,11 +49,12 @@ class SendRecoverPasswordController extends Controller { } public function handler() { - if(!Controller::isUserSystemEnabled()) { + $this->staff = Controller::request('staff'); + + if(!Controller::isUserSystemEnabled() && !$this->staff) { throw new RequestException(ERRORS::USER_SYSTEM_DISABLED); } - $this->staff = Controller::request('staff'); $email = Controller::request('email'); if($this->staff){ diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index 11a6c33d..42b5b0e7 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -113,7 +113,36 @@ describe'system/disable-user-system' do (result['message']).should.equal('SYSTEM_USER_IS_ALREADY_DISABLED') end + it 'should allow staff members to recover their passwords' do + request('/user/logout') + result = request('/user/send-recover-password', { + email: 'jorah@opensupports.com', + staff: true + }) + (result['status']).should.equal('success') + + token = $database.getLastRow('recoverpassword')['token']; + + result = request('/user/recover-password', { + email: 'jorah@opensupports.com', + password: 's3cur3p455w0rd', + token: token + }) + (result['status']).should.equal('success') + (result['data']['staff']).should.equal('1') + + result = request('/user/login', { + email: 'jorah@opensupports.com', + password: 's3cur3p455w0rd', + staff: true + }) + (result['status']).should.equal('success') + (result['data']['userEmail']).should.equal('jorah@opensupports.com') + end + it 'should enable the user system' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) result = request('/system/enable-user-system', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, From 1d84aa488c5220f840f1440734e61806cb593292 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 8 Nov 2019 20:39:28 -0300 Subject: [PATCH 21/50] Removes checks for mailsender since it should always be connected. --- server/controllers/staff/invite.php | 2 -- server/controllers/user/invite.php | 35 +++++++++++++---------------- server/data/ERRORS.php | 5 ----- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/server/controllers/staff/invite.php b/server/controllers/staff/invite.php index ce1748e5..28231ba4 100755 --- a/server/controllers/staff/invite.php +++ b/server/controllers/staff/invite.php @@ -69,8 +69,6 @@ class InviteStaffController extends Controller { if(!$staffRow->isNull()) throw new RequestException(ERRORS::ALREADY_A_STAFF); - if(!MailSender::getInstance()->isConnected()) throw new RequestException(ERRORS::MAIL_SENDER_NOT_CONNECTED); - $staff = new Staff(); $staff->setProperties([ 'name'=> $this->name, diff --git a/server/controllers/user/invite.php b/server/controllers/user/invite.php index d2eb2668..469829d5 100755 --- a/server/controllers/user/invite.php +++ b/server/controllers/user/invite.php @@ -26,7 +26,6 @@ DataValidator::with('CustomValidations', true); * @apiUse ALREADY_BANNED * @apiUse NO_PERMISSION * @apiUse INVALID_CUSTOM_FIELD_OPTION - * @apiUse MAIL_SENDER_NOT_CONNECTED * * @apiSuccess {Object} data Information about invited user * @apiSuccess {Number} data.userId Id of the invited user @@ -83,30 +82,26 @@ class InviteUserController extends Controller { throw new RequestException(ERRORS::ALREADY_BANNED); } - if(MailSender::getInstance()->isConnected()) { - $userId = $this->createNewUserAndRetrieveId(); + $userId = $this->createNewUserAndRetrieveId(); - $this->token = Hashing::generateRandomToken(); + $this->token = Hashing::generateRandomToken(); - $recoverPassword = new RecoverPassword(); - $recoverPassword->setProperties(array( - 'email' => $this->userEmail, - 'token' => $this->token, - 'staff' => false - )); - $recoverPassword->store(); + $recoverPassword = new RecoverPassword(); + $recoverPassword->setProperties(array( + 'email' => $this->userEmail, + 'token' => $this->token, + 'staff' => false + )); + $recoverPassword->store(); - $this->sendInvitationMail(); + $this->sendInvitationMail(); - Response::respondSuccess([ - 'userId' => $userId, - 'userEmail' => $this->userEmail - ]); + Response::respondSuccess([ + 'userId' => $userId, + 'userEmail' => $this->userEmail + ]); - Log::createLog('INVITE', $this->userName); - } else { - throw new RequestException(ERRORS::MAIL_SENDER_NOT_CONNECTED); - } + Log::createLog('INVITE', $this->userName); } public function storeRequestData() { diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index da679009..831086a9 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -251,10 +251,6 @@ * @apiDefine INVALID_COLOR * @apiError {String} INVALID_COLOR The color should be in hexadecimal, preceded by a '#' */ -/** - * @apiDefine MAIL_SENDER_NOT_CONNECTED - * @apiError {String} MAIL_SENDER_NOT_CONNECTED The mail sender is not connected. - */ class ERRORS { const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'; @@ -321,5 +317,4 @@ class ERRORS { const INVALID_CUSTOM_FIELD_OPTION = 'INVALID_CUSTOM_FIELD_OPTION'; const UNAVAILABLE_STATS = 'UNAVAILABLE_STATS'; const INVALID_COLOR = 'INVALID_COLOR'; - const MAIL_SENDER_NOT_CONNECTED = 'MAIL_SENDER_NOT_CONNECTED'; } From b81c628faa1ac936f35c1efac54464d5ab962180 Mon Sep 17 00:00:00 2001 From: Guillermo Giuliana Date: Sun, 10 Nov 2019 16:14:30 -0300 Subject: [PATCH 22/50] Search ticket path backend --- server/controllers/staff/get-new-tickets.php | 2 +- server/controllers/ticket.php | 1 + server/controllers/ticket/search.php | 290 ++++++++++++++++++ server/data/ERRORS.php | 9 + server/libs/validations/validAuthorsId.php | 23 ++ server/libs/validations/validDateRange.php | 21 ++ .../libs/validations/validDepartmentsId.php | 20 ++ server/libs/validations/validOrderBy.php | 22 ++ server/libs/validations/validPrioritys.php | 19 ++ server/libs/validations/validTagsId.php | 20 ++ 10 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 server/controllers/ticket/search.php create mode 100644 server/libs/validations/validAuthorsId.php create mode 100644 server/libs/validations/validDateRange.php create mode 100644 server/libs/validations/validDepartmentsId.php create mode 100644 server/libs/validations/validOrderBy.php create mode 100644 server/libs/validations/validPrioritys.php create mode 100644 server/libs/validations/validTagsId.php diff --git a/server/controllers/staff/get-new-tickets.php b/server/controllers/staff/get-new-tickets.php index e9986740..9407ee54 100755 --- a/server/controllers/staff/get-new-tickets.php +++ b/server/controllers/staff/get-new-tickets.php @@ -32,7 +32,7 @@ class GetNewTicketsStaffController extends Controller { public function validations() { return[ - 'permission' => 'staff_1', + 'permission' => 'any', 'requestData' => [ 'page' => [ 'validation' => DataValidator::numeric(), diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index 5afbf8d5..e60c253e 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -23,5 +23,6 @@ $ticketControllers->addController(new DeleteTagController); $ticketControllers->addController(new GetTagsController); $ticketControllers->addController(new AddTagController); $ticketControllers->addController(new RemoveTagController); +$ticketControllers->addController(new SearchController); $ticketControllers->finalize(); diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php new file mode 100644 index 00000000..b4c86a08 --- /dev/null +++ b/server/controllers/ticket/search.php @@ -0,0 +1,290 @@ + 'any', + '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::validPrioritys(),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() { + $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; + $filters = ""; + $order = ""; + Controller::request("page") ? $page = Controller::request("page") : $page = 1 ; + + $this->setQueryFilters($filters); + $query .= $filters . " GROUP BY ticket.id"; + + $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; + error_log(print_r("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2", true)); + error_log(print_r($totalCount, true)); + + $query = "SELECT ticket.id,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $query; + + $this->setQueryOrder($order); + $query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10); + + $ticketList = RedBean::getAll($query); + + Response::respondSuccess([ + 'tickets' => $ticketList, + 'pages' => ceil($totalCount / 10), + 'page' => Controller::request('page') + ]); + + } + //FILTER + private function setQueryFilters(&$filters){ + $this->setTagFilter($filters); + $this->setClosedFilter($filters); + $this->setAssignedFilter($filters); + $this->setSeenFilter($filters); + $this->setPriorityFilter($filters); + $this->setDateFilter($filters); + $this->setDepartmentFilter($filters); + $this->setAuthorFilter($filters); + $this->setStringFilter($filters); + + if($filters != "") $filters = " WHERE " . $filters; + } + + private function setTagFilter(&$filters){ + $tagList = json_decode(Controller::request('tags')); + + if($tagList){ + $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 .= ")"; + } + } + private function setClosedFilter(&$filters){ + $closed = Controller::request('closed'); + + if ($closed != null) { + if ($filters != "") $filters .= " and "; + $filters .= "ticket.closed = " . $closed ; + } + } + private function setSeenFilter(&$filters){ + $unreadStaff = Controller::request('unreadStaff'); + if ($unreadStaff != null) { + if ($filters != "") $filters .= " and "; + $filters .= "ticket.unread_staff = " . $unreadStaff; + } + } + private function setPriorityFilter(&$filters){ + $prioritys = json_decode(Controller::request('priority')); + if($prioritys != null){ + if ($filters != "") $filters .= " and "; + foreach(array_unique($prioritys) as $key => $priority) { + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($prioritys)) ? $filters .= " or " : null; + + if($priority == 0){ + $filters .= "ticket.priority = " . "'low'"; + }elseif($priority == 1){ + $filters .= "ticket.priority = " . "'medium'"; + }elseif($priority == 2){ + $filters .= "ticket.priority = " . "'high'"; + } + + $key == sizeof($prioritys) ? $filters .= " ) " : null ; + } + $prioritys != "" ? $filters .= ") " : null; + } + } + + private function setDateFilter(&$filters){ + $dateRange = json_decode(Controller::request('dateRange')); + 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(&$filters){ + + $departments = json_decode(Controller::request('departments')); + + if($departments != null){ + if ($filters != "") $filters .= " and "; + + foreach($departments as $key => $department) { + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($departments)) ? $filters .= " or " : null; + + $filters .= "ticket.department_id = " . $department ; + } + $filters .= ")"; + } + } + + private function setAuthorFilter(&$filters){ + $authors = json_decode(Controller::request('authors')); + + if($authors != null){ + + if ($filters != "") $filters .= " and "; + + foreach($authors as $key => $author){ + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($authors)) ? $filters .= " or " : null; + + if($author->staff){ + $filters .= "ticket.author_staff_id = " . $author->id; + } else { + $filters .= "ticket.author_id = " . $author->id; + } + } + + $filters .= ")"; + + } + } + + private function setAssignedFilter(&$filters){ + $assigned = Controller::request('assigned'); + 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(&$filters){ + $string = Controller::request('query'); + if($string != null){ + if ($filters != "") $filters .= " and "; + $filters .= " (ticket.title LIKE '%" . $string . "%' or ticket.content LIKE '%" . $string . "%' or ticket.ticket_number LIKE '%" . $string . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $string ."%'))"; + }; + } + + //ORDER + private function setQueryOrder(&$order){ + $order = " ORDER BY "; + $this->setStringOrder($order); + $this->setEspecificOrder($order); + $order .= "ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc "; + } + private function setEspecificOrder(&$order){ + $orderBy = json_decode(Controller::request('orderBy')); + if($orderBy != null){ + $orientation = ($orderBy->asc ? " asc" : " desc" ); + $order .= "ticket." . $orderBy->value . $orientation . ","; + }; + } + private function setStringOrder(&$order){ + $string = Controller::request('query'); + if($string != null){ + $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $string ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $string ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $string ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$string."%') THEN ticketevent.content END desc," ; + } + } + +} + diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index 831086a9..2e56b685 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -277,6 +277,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'; diff --git a/server/libs/validations/validAuthorsId.php b/server/libs/validations/validAuthorsId.php new file mode 100644 index 00000000..3cda8229 --- /dev/null +++ b/server/libs/validations/validAuthorsId.php @@ -0,0 +1,23 @@ +staff){ + $author = \Staff::getDataStore($authorObject->id); + }else{ + $author = \User::getDataStore($authorObject->id); + } + if($author->isNull()) return false; + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/server/libs/validations/validDateRange.php b/server/libs/validations/validDateRange.php new file mode 100644 index 00000000..05f6a3c2 --- /dev/null +++ b/server/libs/validations/validDateRange.php @@ -0,0 +1,21 @@ +isNull()) return false; + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/server/libs/validations/validOrderBy.php b/server/libs/validations/validOrderBy.php new file mode 100644 index 00000000..4fed26c1 --- /dev/null +++ b/server/libs/validations/validOrderBy.php @@ -0,0 +1,22 @@ +asc !== true && $object->asc !== false) return false; + + foreach ($values as $value) { + if($object->value == $value) $isTrue = true; + } + return $isTrue; + } + } +} \ No newline at end of file diff --git a/server/libs/validations/validPrioritys.php b/server/libs/validations/validPrioritys.php new file mode 100644 index 00000000..53bf9505 --- /dev/null +++ b/server/libs/validations/validPrioritys.php @@ -0,0 +1,19 @@ +isNull()) return false; + } + return true; + } + return false; + } +} \ No newline at end of file From 545c910d5e4c45beb7e9c6da822a6eafa042e128 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 11 Nov 2019 12:55:14 -0300 Subject: [PATCH 23/50] Fixes languages according to issue 619 --- client/src/data/languages/br.js | 2 +- client/src/data/languages/de.js | 2 +- client/src/data/languages/es.js | 2 +- client/src/data/languages/gr.js | 2 +- client/src/data/languages/it.js | 2 +- client/src/data/languages/pt.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/data/languages/br.js b/client/src/data/languages/br.js index 05dbb11f..12906cbf 100644 --- a/client/src/data/languages/br.js +++ b/client/src/data/languages/br.js @@ -300,7 +300,7 @@ export default { 'DELETE_USER_DESCRIPTION': 'O usuário não será capaz de entrar no sistema e todos os seus chamados serão apagados. Além disso, o e-mail não poderá mais ser usado.', 'DELETE_TOPIC_DESCRIPTION': 'Ao excluir o tópico, todos os artigos dele serão apagados.', 'EDIT_TOPIC_DESCRIPTION': 'Aqui você pode alterar o nome, o ícone ea cor do ícone do tópico.', - 'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {categoria}.', + 'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {category}.', 'LIST_ARTICLES_DESCRIPTION': 'Esta é uma lista de artigos que inclui informações sobre nossos serviços.', 'ADD_TOPIC_DESCRIPTION': 'Aqui você pode adicionar um tópico que funciona como uma categoria para artigos.', 'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.', diff --git a/client/src/data/languages/de.js b/client/src/data/languages/de.js index 8ddfa7c9..cf126ee5 100644 --- a/client/src/data/languages/de.js +++ b/client/src/data/languages/de.js @@ -272,7 +272,7 @@ export default { 'INSTALLATION_COMPLETED': 'Installation abgeschlossen.', 'INSTALLATION_COMPLETED_DESCRIPTION': 'Die Installation von OpenSupports ist abgeschlossen. Umleitung zum Admin-Panel...', - 'STEP_TITLE': 'Schritt {aktuell} von {total} - {title}', + 'STEP_TITLE': 'Schritt {current} von {total} - {title}', 'STEP_1_DESCRIPTION': 'Wählen Sie Ihre bevorzugte Sprache für den Installationsassistenten aus.', 'STEP_2_DESCRIPTION': 'Hier sind die Voraussetzungen für das Ausführen von OpenSupports aufgelistet. Bitte stellen Sie sicher, dass alle Anforderungen erfüllt sind.', 'STEP_3_DESCRIPTION': 'Bitte füllen Sie die MySQL-Datenbankkonfiguration aus.', diff --git a/client/src/data/languages/es.js b/client/src/data/languages/es.js index 35ad7f3d..89ddcf8f 100644 --- a/client/src/data/languages/es.js +++ b/client/src/data/languages/es.js @@ -206,7 +206,7 @@ export default { 'TYPE': 'Tipo', 'SELECT_INPUT': 'Seleccionar entrada', 'TEXT_INPUT': 'Entrada de texto', - 'OPTION': 'Opción {índice}', + 'OPTION': 'Opción {index}', 'OPTIONS': 'Opciones', 'FIELD_DESCRIPTION': 'Descripción del campo (opcional)', 'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puedes agregar una nueva etiqueta personalizada', diff --git a/client/src/data/languages/gr.js b/client/src/data/languages/gr.js index 5b66ffbc..939ab0b8 100644 --- a/client/src/data/languages/gr.js +++ b/client/src/data/languages/gr.js @@ -300,7 +300,7 @@ 'DELETE_USER_DESCRIPTION': 'Ο χρήστης δεν θα μπορέσει να συνδεθεί με τη γήρανση και όλα τα εισιτήρια του θα διαγραφούν. Επίσης, το ηλεκτρονικό ταχυδρομείο δεν μπορεί πλέον να χρησιμοποιηθεί.', 'DELETE_TOPIC_DESCRIPTION': 'Διαγράφοντας το θέμα, όλα τα άρθρα σε αυτό θα διαγραφούν.', 'EDIT_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να αλλάξετε το όνομα, το εικονίδιο και το χρώμα του εικονιδίου του θέματος.', - 'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {κατηγορία}.', + 'ADD_ARTICLE_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα άρθρο που θα είναι διαθέσιμο για κάθε χρήστη. Θα προστεθεί μέσα στην κατηγορία {category}.', 'LIST_ARTICLES_DESCRIPTION': 'Αυτή είναι μια λίστα με άρθρα που περιλαμβάνουν πληροφορίες σχετικά με τις υπηρεσίες μας.', 'ADD_TOPIC_DESCRIPTION': 'Εδώ μπορείτε να προσθέσετε ένα θέμα που λειτουργεί ως κατηγορία για άρθρα.', 'DELETE_ARTICLE_DESCRIPTION': 'Πρόκειται να διαγράψετε αυτό το άρθρο για πάντα.', diff --git a/client/src/data/languages/it.js b/client/src/data/languages/it.js index 53ba2917..3bc92519 100644 --- a/client/src/data/languages/it.js +++ b/client/src/data/languages/it.js @@ -206,7 +206,7 @@ export default { 'TYPE': 'genere', 'SELECT_INPUT': 'Seleziona input', 'TEXT_INPUT': 'L\'immissione di testo', - 'OPTION': 'Opzione {indice}', + 'OPTION': 'Opzione {index}', 'OPTIONS': 'Opzioni', 'FIELD_DESCRIPTION': 'Descrizione del campo (facoltativo)', 'DESCRIPTION_ADD_CUSTOM_TAG': 'qui puoi aggiungere un nuovo tag personalizzato', diff --git a/client/src/data/languages/pt.js b/client/src/data/languages/pt.js index 304dc99f..5ceff7f4 100644 --- a/client/src/data/languages/pt.js +++ b/client/src/data/languages/pt.js @@ -300,7 +300,7 @@ export default { 'DELETE_USER_DESCRIPTION': 'O usuário não será capaz de entrar no envelhecimento e todos os seus bilhetes serão apagados. Além disso, o e-mail não pode mais ser usado.', 'DELETE_TOPIC_DESCRIPTION': 'Ao excluir o tópico, todos os artigos dele serão apagados.', 'EDIT_TOPIC_DESCRIPTION': 'Aqui você pode alterar o nome, o ícone ea cor do ícone do tópico.', - 'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {categoria}.', + 'ADD_ARTICLE_DESCRIPTION': 'Aqui você pode adicionar um artigo que estará disponível para cada usuário. Ele será adicionado dentro da categoria {category}.', 'LIST_ARTICLES_DESCRIPTION': 'Esta é uma lista de artigos que inclui informações sobre nossos serviços.', 'ADD_TOPIC_DESCRIPTION': 'Aqui você pode adicionar um tópico que funciona como uma categoria para artigos.', 'DELETE_ARTICLE_DESCRIPTION': 'Você vai excluir este artigo para sempre.', From 995d302a49a5ed51537d0a072a017da1c2469d9f Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 14 Nov 2019 11:34:20 -0300 Subject: [PATCH 24/50] Improves code according to Ivan's comments --- server/controllers/ticket/comment.php | 4 ++-- server/models/Log.php | 2 +- server/models/Ticketevent.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/controllers/ticket/comment.php b/server/controllers/ticket/comment.php index f72a9e5c..fd904375 100755 --- a/server/controllers/ticket/comment.php +++ b/server/controllers/ticket/comment.php @@ -100,8 +100,8 @@ class CommentController extends Controller { 'name' => $this->ticket->owner->name, 'staff' => true ]); - } else if($isOwner) { - !Controller::request('private') ? $this->sendMail($ticketAuthor) : null; + } else if($isOwner && !Controller::request('private')) { + $this->sendMail($ticketAuthor); } Log::createLog('COMMENT', $this->ticket->ticketNumber); diff --git a/server/models/Log.php b/server/models/Log.php index 3faf7ad6..d9a234bc 100755 --- a/server/models/Log.php +++ b/server/models/Log.php @@ -71,7 +71,7 @@ class Log extends DataStore { 'to' => $this->to, 'author' => [ 'name' => $this->authorName, - 'id' => $author && !$author->isNull() ? $author->id : null, + 'id' => ($author && !$author->isNull()) ? $author->id : null, 'staff' => $author instanceof Staff ], 'date' => $this->date diff --git a/server/models/Ticketevent.php b/server/models/Ticketevent.php index 9daf2560..a8ea9b96 100755 --- a/server/models/Ticketevent.php +++ b/server/models/Ticketevent.php @@ -92,7 +92,7 @@ class Ticketevent extends DataStore { 'name' => $user ? $user->name : $author['name'], 'staff' => $user instanceOf Staff, 'id' => $user ? $user->id : null, - 'customfields' => $user && $user->xownCustomfieldvalueList ? $user->xownCustomfieldvalueList->toArray() : [], + 'customfields' => ($user && $user->xownCustomfieldvalueList) ? $user->xownCustomfieldvalueList->toArray() : [], ], 'edited' => $this->editedContent ]; From 57d3766c9d84bbf1a9ca6ff90ab682f0caabead7 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 14 Nov 2019 17:17:45 -0300 Subject: [PATCH 25/50] unit test for Search ticket --- server/controllers/staff/get-new-tickets.php | 2 +- server/controllers/ticket/search.php | 165 +++++++++--------- server/libs/validations/validOrderBy.php | 10 +- server/tests/__mocks__/ControllerMock.php | 10 +- server/tests/controllers/user/loginTest.php | 2 +- .../libs/LinearCongruentialGeneratorTest.php | 1 + tests/staff/get-all.rb | 8 +- tests/ticket/search.rb | 22 +++ 8 files changed, 126 insertions(+), 94 deletions(-) create mode 100644 tests/ticket/search.rb diff --git a/server/controllers/staff/get-new-tickets.php b/server/controllers/staff/get-new-tickets.php index 9407ee54..e9986740 100755 --- a/server/controllers/staff/get-new-tickets.php +++ b/server/controllers/staff/get-new-tickets.php @@ -32,7 +32,7 @@ class GetNewTicketsStaffController extends Controller { public function validations() { return[ - 'permission' => 'any', + 'permission' => 'staff_1', 'requestData' => [ 'page' => [ 'validation' => DataValidator::numeric(), diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index b4c86a08..0f9f9f1e 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -26,8 +26,8 @@ DataValidator::with('CustomValidations', true); * @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. - * + * @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 @@ -99,24 +99,25 @@ class SearchController extends Controller { } public function handler() { - $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; - $filters = ""; - $order = ""; - Controller::request("page") ? $page = Controller::request("page") : $page = 1 ; - - $this->setQueryFilters($filters); - $query .= $filters . " GROUP BY ticket.id"; + $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') + ]; + $query = $this->getSQLQuery($inputs); + $queryWithOrder = $this->getSQLQueryWithOrder($inputs); + throw new Exception("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2"); $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; - error_log(print_r("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2", true)); - error_log(print_r($totalCount, true)); - - $query = "SELECT ticket.id,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $query; - - $this->setQueryOrder($order); - $query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10); - - $ticketList = RedBean::getAll($query); + $ticketList = RedBean::getAll($queryWithOrder); Response::respondSuccess([ 'tickets' => $ticketList, @@ -125,61 +126,74 @@ class SearchController extends Controller { ]); } - //FILTER - private function setQueryFilters(&$filters){ - $this->setTagFilter($filters); - $this->setClosedFilter($filters); - $this->setAssignedFilter($filters); - $this->setSeenFilter($filters); - $this->setPriorityFilter($filters); - $this->setDateFilter($filters); - $this->setDepartmentFilter($filters); - $this->setAuthorFilter($filters); - $this->setStringFilter($filters); + public function getSQLQuery($inputs) { + $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; + $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,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $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'], $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(&$filters){ - $tagList = json_decode(Controller::request('tags')); + private function setTagFilter($tagList, &$filters){ if($tagList){ $filters != "" ? $filters .= " and " : null; - foreach($tagList as $key => $tag) { + foreach($tagList as $key => $tag) { $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($tagList)) ? $filters .= " or " : null; - + $filters .= "tag_ticket.tag_id = " . $tag ; } $filters .= ")"; } } - private function setClosedFilter(&$filters){ - $closed = Controller::request('closed'); - + public function setClosedFilter($closed, &$filters){ if ($closed != null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.closed = " . $closed ; - } + } } - private function setSeenFilter(&$filters){ - $unreadStaff = Controller::request('unreadStaff'); + private function setSeenFilter($unreadStaff, &$filters){ if ($unreadStaff != null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.unread_staff = " . $unreadStaff; - } + } } - private function setPriorityFilter(&$filters){ - $prioritys = json_decode(Controller::request('priority')); + private function setPriorityFilter($prioritys, &$filters){ if($prioritys != null){ if ($filters != "") $filters .= " and "; foreach(array_unique($prioritys) as $key => $priority) { $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($prioritys)) ? $filters .= " or " : null; - + if($priority == 0){ $filters .= "ticket.priority = " . "'low'"; }elseif($priority == 1){ @@ -193,62 +207,55 @@ class SearchController extends Controller { $prioritys != "" ? $filters .= ") " : null; } } - - private function setDateFilter(&$filters){ - $dateRange = json_decode(Controller::request('dateRange')); + + 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(&$filters){ - - $departments = json_decode(Controller::request('departments')); - + private function setDepartmentFilter($departments, &$filters){ if($departments != null){ if ($filters != "") $filters .= " and "; - foreach($departments as $key => $department) { + foreach($departments as $key => $department) { $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($departments)) ? $filters .= " or " : null; - + $filters .= "ticket.department_id = " . $department ; } $filters .= ")"; } } - private function setAuthorFilter(&$filters){ - $authors = json_decode(Controller::request('authors')); - + private function setAuthorFilter($authors, &$filters){ if($authors != null){ if ($filters != "") $filters .= " and "; - + foreach($authors as $key => $author){ $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($authors)) ? $filters .= " or " : null; - - if($author->staff){ - $filters .= "ticket.author_staff_id = " . $author->id; + + if($author['staff']){ + $filters .= "ticket.author_staff_id = " . $author['id']; } else { - $filters .= "ticket.author_id = " . $author->id; + $filters .= "ticket.author_id = " . $author['id']; } } $filters .= ")"; - + } } - private function setAssignedFilter(&$filters){ - $assigned = Controller::request('assigned'); + private function setAssignedFilter($assigned, &$filters){ if($assigned != null){ if ($filters != "") $filters .= " and "; $key = ""; @@ -257,34 +264,30 @@ class SearchController extends Controller { } } - private function setStringFilter(&$filters){ - $string = Controller::request('query'); - if($string != null){ + private function setStringFilter($search, &$filters){ + if($search != null){ if ($filters != "") $filters .= " and "; - $filters .= " (ticket.title LIKE '%" . $string . "%' or ticket.content LIKE '%" . $string . "%' or ticket.ticket_number LIKE '%" . $string . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $string ."%'))"; + $filters .= " (ticket.title LIKE '%" . $search . "%' or ticket.content LIKE '%" . $search . "%' or ticket.ticket_number LIKE '%" . $search . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $search ."%'))"; }; } //ORDER - private function setQueryOrder(&$order){ + private function setQueryOrder($inputs, &$order){ $order = " ORDER BY "; - $this->setStringOrder($order); - $this->setEspecificOrder($order); + 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(&$order){ - $orderBy = json_decode(Controller::request('orderBy')); + private function setEspecificOrder($orderBy, &$order){ if($orderBy != null){ - $orientation = ($orderBy->asc ? " asc" : " desc" ); - $order .= "ticket." . $orderBy->value . $orientation . ","; + $orientation = ($orderBy['asc'] ? " asc" : " desc" ); + $order .= "ticket." . $orderBy['value'] . $orientation . ","; }; } - private function setStringOrder(&$order){ - $string = Controller::request('query'); - if($string != null){ - $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $string ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $string ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $string ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$string."%') THEN ticketevent.content END desc," ; + private function setStringOrder($querysearch, &$order){ + if($querysearch != null){ + $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $querysearch ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $querysearch ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $querysearch ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$querysearch."%') THEN ticketevent.content END desc," ; } } } - diff --git a/server/libs/validations/validOrderBy.php b/server/libs/validations/validOrderBy.php index 4fed26c1..44466805 100644 --- a/server/libs/validations/validOrderBy.php +++ b/server/libs/validations/validOrderBy.php @@ -10,13 +10,13 @@ class ValidOrderBy extends AbstractRule { $values =["closed","owner_id","unread_staff","priority","date"]; $isTrue = false; $object = json_decode($orderBy); - - if($object->asc !== true && $object->asc !== false) return false; - + + if($object->asc !== 1 && $object->asc !== 0) return false; + foreach ($values as $value) { if($object->value == $value) $isTrue = true; - } + } return $isTrue; } } -} \ No newline at end of file +} diff --git a/server/tests/__mocks__/ControllerMock.php b/server/tests/__mocks__/ControllerMock.php index 7a4e8149..fa24452a 100755 --- a/server/tests/__mocks__/ControllerMock.php +++ b/server/tests/__mocks__/ControllerMock.php @@ -1,13 +1,19 @@ 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', diff --git a/server/tests/libs/LinearCongruentialGeneratorTest.php b/server/tests/libs/LinearCongruentialGeneratorTest.php index 5d5f671f..b716612d 100644 --- a/server/tests/libs/LinearCongruentialGeneratorTest.php +++ b/server/tests/libs/LinearCongruentialGeneratorTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; class LinearCongruentialGeneratorTest extends TestCase { public function testAvoidCollisions() { + return; $TEST_TIMES = 10; $GENERATE_TIMES = 500000; $min = 100000; diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index dec7fdc5..5ba054cb 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -14,10 +14,10 @@ describe'/staff/get-all' do (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) diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb new file mode 100644 index 00000000..c1c4d53f --- /dev/null +++ b/tests/ticket/search.rb @@ -0,0 +1,22 @@ +describe '/user/get' do + request('/user/logout') + + + #Scripts.createUser('user_get@os4.com', 'user_get','User Get') + + #Scripts.login('user_get@os4.com', 'user_get') + #result = request('/ticket/create', { + title: 'Should we pay?', + content: 'A Lannister always pays his debts.', + departmentId: 1, + language: 'en', + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + request('/user/logout') + #$database.query("INSERT INTO ticket(id,ticket_number) VALUES(58,123456,1,'low',1,'titulo','contentlargo','en',null,201911101213,1,'hola@os4.com','nameuatuhor',1,1,1,null);") + + + #@ticketNumber = result['data']['ticketNumber'] + request('/user/logout') +end From 8ea9c6ee9bbf8198961e6ee2ae3e9d007dfc49f3 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 15 Nov 2019 18:50:35 -0300 Subject: [PATCH 26/50] Fixes redirection when creating a ticket without user system enabled --- .../dashboard/dashboard-create-ticket/create-ticket-form.js | 2 +- .../dashboard-create-ticket/dashboard-create-ticket-page.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js index 6d30bae1..6fa35f4f 100644 --- a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js +++ b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js @@ -146,7 +146,7 @@ class CreateTicketForm extends React.Component { message: 'success' }, () => { if(this.props.onSuccess) { - this.props.onSuccess(); + this.props.onSuccess(result, email); } }); } diff --git a/client/src/app/main/dashboard/dashboard-create-ticket/dashboard-create-ticket-page.js b/client/src/app/main/dashboard/dashboard-create-ticket/dashboard-create-ticket-page.js index 57e50d6c..a875f1fb 100644 --- a/client/src/app/main/dashboard/dashboard-create-ticket/dashboard-create-ticket-page.js +++ b/client/src/app/main/dashboard/dashboard-create-ticket/dashboard-create-ticket-page.js @@ -32,7 +32,7 @@ class DashboardCreateTicketPage extends React.Component { ); } - onCreateTicketSuccess() { + onCreateTicketSuccess(result, email) { if((this.props.location.pathname !== '/create-ticket')) { setTimeout(() => {history.push('/dashboard')}, 2000); } else { From 44cab041575f3b815814e9dde570692ab760cfaf Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 15 Nov 2019 19:14:12 -0300 Subject: [PATCH 27/50] Hides invite-user button when user system is disabled in admin panel --- .../panel/users/admin-panel-list-users.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/client/src/app/admin/panel/users/admin-panel-list-users.js b/client/src/app/admin/panel/users/admin-panel-list-users.js index ffc98211..e32e23ca 100644 --- a/client/src/app/admin/panel/users/admin-panel-list-users.js +++ b/client/src/app/admin/panel/users/admin-panel-list-users.js @@ -1,4 +1,5 @@ import React from 'react'; +import {connect} from 'react-redux'; import i18n from 'lib-app/i18n'; import API from 'lib-app/api-call'; @@ -16,7 +17,6 @@ import MainSignUpWidget from 'app/main/main-signup/main-signup-widget'; import InviteUserWidget from 'app/admin/panel/users/invite-user-widget'; class AdminPanelListUsers extends React.Component { - state = { loading: true, users: [], @@ -40,8 +40,16 @@ class AdminPanelListUsers extends React.Component { return (
+ {(this.state.error) ? {i18n('ERROR_RETRIEVING_USERS')} : this.renderTableAndInviteButton()} +
+ ); + } + + renderTableAndInviteButton() { + return ( +
- {(this.state.error) ? {i18n('ERROR_RETRIEVING_USERS')} :
} +
); } @@ -139,15 +139,15 @@ class Table extends React.Component { this.props.onPageChange({target: {value: index}}); } } - + getRowClass(row) { let classes = { 'table__row': true, 'table__row-highlighted': row.highlighted }; - + classes[row.className] = (row.className); - + return classNames(classes); } @@ -167,4 +167,4 @@ class Table extends React.Component { } } -export default Table; \ No newline at end of file +export default Table; diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 6373f738..b29c60d1 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -93,6 +93,7 @@ export default { 'CHANGE_EMAIL': 'Change email', 'CHANGE_PASSWORD': 'Change password', 'NAME': 'Name', + 'SEARCH': 'Search', 'SIGNUP_DATE': 'Sign up date', 'SEARCH_USERS': 'Search users...', 'SEARCH_EMAIL': 'Search email...', @@ -293,6 +294,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 specifics 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.', diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index f271acf2..a1530e8f 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -50,6 +50,7 @@ class GetAllTicketsStaffController extends Controller { ]); return; } + Response::respondSuccess([ 'tickets' => $this->getTicketList()->toArray(true), 'pages' => $this->getTotalPages() diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index a53c7334..62b49ada 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -116,17 +116,21 @@ class SearchController extends Controller { $query = $this->getSQLQuery($inputs); $queryWithOrder = $this->getSQLQueryWithOrder($inputs); - $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; - $ticketList = RedBean::getAll($queryWithOrder); + $ticketIdList = RedBean::getAll($queryWithOrder); + $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 + 'page' => $inputs['page'] ? ($inputs['page']*1) : 1 ]); }else{ Response::respondSuccess([]); @@ -151,7 +155,7 @@ class SearchController extends Controller { public function getSQLQueryWithOrder($inputs) { $query = $this->getSQLQuery($inputs); $order = ""; - $query = "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date " . $query; + $query = "SELECT" . " ticket.id " . $query; $this->setQueryOrder($inputs, $order); $inputs['page'] ? $page = $inputs['page'] : $page = 1 ; diff --git a/server/tests/controllers/ticket/searchTest.php b/server/tests/controllers/ticket/searchTest.php new file mode 100644 index 00000000..b7f72f46 --- /dev/null +++ b/server/tests/controllers/ticket/searchTest.php @@ -0,0 +1,258 @@ + \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 + ]), + '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([ + 'departments' => [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.department_id = 1) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'departments' => [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 ( 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 '%hello world%' or ticket.content LIKE '%hello world%' or ticket.ticket_number LIKE '%hello world%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%hello world%') ) GROUP BY ticket.id" + ); + } + public function testQueryWithOrder() { + $this->assertEquals( + $this->searchController->getSQLQueryWithOrder([ + 'page' => 1 + ]), + "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date 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,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date 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 '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') 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,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date 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" + ); + } +} diff --git a/tests/init.rb b/tests/init.rb index 5f3ef8a0..73651ad3 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -22,10 +22,11 @@ require './user/send-recover-password.rb' require './user/recover-password.rb' require './user/edit-password.rb' require './user/edit-email.rb' +#require './ticket/search.rb' require './user/get.rb' require './user/enable-disable.rb' -require './ticket/create.rb' require './ticket/seen.rb' +require './ticket/create.rb' require './ticket/comment.rb' require './ticket/get.rb' require './ticket/custom-response.rb' From bac138757eeca1dd566ffeb4df16ab9e73dbe3ff Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 09:04:49 -0300 Subject: [PATCH 33/50] fix search php test --- server/tests/controllers/ticket/searchTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/tests/controllers/ticket/searchTest.php b/server/tests/controllers/ticket/searchTest.php index b7f72f46..ad08fb5f 100644 --- a/server/tests/controllers/ticket/searchTest.php +++ b/server/tests/controllers/ticket/searchTest.php @@ -236,7 +236,7 @@ class SearchControllerTest extends TestCase { $this->searchController->getSQLQueryWithOrder([ 'page' => 1 ]), - "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date 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" + "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( @@ -244,7 +244,7 @@ class SearchControllerTest extends TestCase { 'page' => 1, 'query' => 'stark' ]), - "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date 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 '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') 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" + "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 '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') 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( @@ -252,7 +252,7 @@ class SearchControllerTest extends TestCase { 'page' => 1, 'orderBy' => ['value' => 'closed', 'asc' => 1] ]), - "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date 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" + "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" ); } } From 58be6127fba8bf8d46be9706e34accbee2beb0b3 Mon Sep 17 00:00:00 2001 From: Guillermo Giuliana Date: Tue, 17 Dec 2019 09:15:52 -0300 Subject: [PATCH 34/50] Delete search.rb --- tests/ticket/search.rb | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 tests/ticket/search.rb diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb deleted file mode 100644 index c1c4d53f..00000000 --- a/tests/ticket/search.rb +++ /dev/null @@ -1,22 +0,0 @@ -describe '/user/get' do - request('/user/logout') - - - #Scripts.createUser('user_get@os4.com', 'user_get','User Get') - - #Scripts.login('user_get@os4.com', 'user_get') - #result = request('/ticket/create', { - title: 'Should we pay?', - content: 'A Lannister always pays his debts.', - departmentId: 1, - language: 'en', - csrf_userid: $csrf_userid, - csrf_token: $csrf_token - }) - request('/user/logout') - #$database.query("INSERT INTO ticket(id,ticket_number) VALUES(58,123456,1,'low',1,'titulo','contentlargo','en',null,201911101213,1,'hola@os4.com','nameuatuhor',1,1,1,null);") - - - #@ticketNumber = result['data']['ticketNumber'] - request('/user/logout') -end From 2099af5f3a2eff4ef1b6cb169f917232206d88cc Mon Sep 17 00:00:00 2001 From: Guillermo Giuliana Date: Tue, 17 Dec 2019 10:15:19 -0300 Subject: [PATCH 35/50] Update LinearCongruentialGeneratorTest.php --- server/tests/libs/LinearCongruentialGeneratorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/server/tests/libs/LinearCongruentialGeneratorTest.php b/server/tests/libs/LinearCongruentialGeneratorTest.php index b716612d..5d5f671f 100644 --- a/server/tests/libs/LinearCongruentialGeneratorTest.php +++ b/server/tests/libs/LinearCongruentialGeneratorTest.php @@ -4,7 +4,6 @@ use PHPUnit\Framework\TestCase; class LinearCongruentialGeneratorTest extends TestCase { public function testAvoidCollisions() { - return; $TEST_TIMES = 10; $GENERATE_TIMES = 500000; $min = 100000; From 870f5fea46766cb3bbf50fe63ae29d607bc8f08e Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 11:05:22 -0300 Subject: [PATCH 36/50] puts get all ruby test --- tests/staff/get-all.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index 5ba054cb..23e089ae 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -8,6 +8,8 @@ describe'/staff/get-all' do csrf_token: $csrf_token }) + puts result['data'] + (result['status']).should.equal('success') (result['data'][0]['name']).should.equal('Emilia Clarke') From 61ce46d5c7eecbb219b7808b446cef77203f769b Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 15:25:45 -0300 Subject: [PATCH 37/50] change name Validation priorities --- server/controllers/ticket/search.php | 10 +++++----- server/libs/validations/validPriorities.php | 19 +++++++++++++++++++ tests/init.rb | 3 +-- tests/system/get-settings.rb | 2 +- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 server/libs/validations/validPriorities.php diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index 62b49ada..a3ea08fe 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -71,7 +71,7 @@ class SearchController extends Controller { 'error' => ERRORS::INVALID_UNREAD_STAFF_FILTER ], 'priority' => [ - 'validation' => DataValidator::oneOf(DataValidator::validPrioritys(),DataValidator::nullType()), + 'validation' => DataValidator::oneOf(DataValidator::validPriorities(),DataValidator::nullType()), 'error' => ERRORS::INVALID_PRIORITY_FILTER ], 'dateRange' => [ @@ -205,11 +205,11 @@ class SearchController extends Controller { $filters .= "ticket.unread_staff = " . $unreadStaff; } } - private function setPriorityFilter($prioritys, &$filters){ - if($prioritys != null){ + private function setPriorityFilter($priorities, &$filters){ + if($priorities != null){ $first = TRUE; if ($filters != "") $filters .= " and "; - foreach(array_unique($prioritys) as $priority) { + foreach(array_unique($priorities) as $priority) { if($first){ $filters .= " ( "; @@ -228,7 +228,7 @@ class SearchController extends Controller { } - $prioritys != "" ? $filters .= ") " : null; + $priorities != "" ? $filters .= ") " : null; } } diff --git a/server/libs/validations/validPriorities.php b/server/libs/validations/validPriorities.php new file mode 100644 index 00000000..7313deec --- /dev/null +++ b/server/libs/validations/validPriorities.php @@ -0,0 +1,19 @@ + Date: Tue, 17 Dec 2019 17:02:46 -0300 Subject: [PATCH 38/50] order departments get all ruby test --- tests/staff/get-all.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index 23e089ae..ed3753ad 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -8,10 +8,13 @@ describe'/staff/get-all' do csrf_token: $csrf_token }) - puts result['data'] - (result['status']).should.equal('success') + result['data'][0]['departments'].sort_by do |department| + department['id'].to_i + end + puts result['data'] + (result['data'][0]['name']).should.equal('Emilia Clarke') (result['data'][0]['email']).should.equal('staff@opensupports.com') (result['data'][0]['profilePic']).should.equal('') From c8634d457cd7bbc5bc9f26a4d30d90f08c18cbe1 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 17:28:02 -0300 Subject: [PATCH 39/50] order departments get all ruby test2 --- tests/staff/get-all.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index ed3753ad..432fa3cd 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -10,7 +10,7 @@ describe'/staff/get-all' do (result['status']).should.equal('success') - result['data'][0]['departments'].sort_by do |department| + result['data'][0]['departments'] = result['data'][0]['departments'].sort_by do |department| department['id'].to_i end puts result['data'] From 0bd099d05ac8f296d24df556e4fcd1ee7ba10001 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 19 Dec 2019 17:06:12 -0300 Subject: [PATCH 40/50] add support for custom ticket lists --- .../src/app-components/ticket-query-list.js | 8 ++++++- .../src/app/admin/panel/admin-panel-menu.js | 21 +++++++++++++++++-- .../tickets/admin-panel-search-tickets.js | 4 +++- client/src/index.html | 1 - tests/staff/get-all.rb | 1 - 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/client/src/app-components/ticket-query-list.js b/client/src/app-components/ticket-query-list.js index a5c7edbc..f28dbc3b 100644 --- a/client/src/app-components/ticket-query-list.js +++ b/client/src/app-components/ticket-query-list.js @@ -22,6 +22,12 @@ class TicketQueryList extends React.Component { this.getTickets(); } + componentDidUpdate(prevProps) { + if (this.props.customList.title !== prevProps.customList.title) { + this.getTickets(); + } + } + render() { return (
@@ -38,7 +44,7 @@ class TicketQueryList extends React.Component { path: '/ticket/search', data: { page : this.state.page, - ...this.props.filters + ...this.props.customList.filters } }).then((result) => { this.setState({ diff --git a/client/src/app/admin/panel/admin-panel-menu.js b/client/src/app/admin/panel/admin-panel-menu.js index da34ccb7..20734df3 100644 --- a/client/src/app/admin/panel/admin-panel-menu.js +++ b/client/src/app/admin/panel/admin-panel-menu.js @@ -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([ { 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 ]) }, { diff --git a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js index b9f4842d..2dd4f530 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js @@ -14,13 +14,15 @@ class AdminPanelSearchTickets extends React.Component { return (
- {(this.props.error) ? {i18n('ERROR_RETRIEVING_TICKETS')} : } + {(this.props.error) ? {i18n('ERROR_RETRIEVING_TICKETS')} : }
); } getFilters() { + let customList = window.customTicketList[this.props.location.query.custom*1] ? window.customTicketList[this.props.location.query.custom*1] : null return { + ...customList }; } } diff --git a/client/src/index.html b/client/src/index.html index 90c8c2e2..a00af5b1 100755 --- a/client/src/index.html +++ b/client/src/index.html @@ -12,7 +12,6 @@
- diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index 432fa3cd..f8571c71 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -13,7 +13,6 @@ describe'/staff/get-all' do result['data'][0]['departments'] = result['data'][0]['departments'].sort_by do |department| department['id'].to_i end - puts result['data'] (result['data'][0]['name']).should.equal('Emilia Clarke') (result['data'][0]['email']).should.equal('staff@opensupports.com') From 843bd13281c3cea242516eb25d48ed8284acf199 Mon Sep 17 00:00:00 2001 From: LautaroCesso <59095036+LautaroCesso@users.noreply.github.com> Date: Fri, 20 Dec 2019 16:49:43 -0300 Subject: [PATCH 41/50] Allows space in custom fields name (issue #598) (#681) * replace spaces for underscores in edit custom fields * Create function getCustomFieldParamName --- .../dashboard-edit-profile-page.js | 31 ++++++++++++------- client/src/data/languages/en.js | 4 +-- client/src/lib-core/APIUtils.js | 4 +++ server/libs/Controller.php | 4 +-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/client/src/app/main/dashboard/dashboard-edit-profile/dashboard-edit-profile-page.js b/client/src/app/main/dashboard/dashboard-edit-profile/dashboard-edit-profile-page.js index 6bf28bfb..75fbd39b 100644 --- a/client/src/app/main/dashboard/dashboard-edit-profile/dashboard-edit-profile-page.js +++ b/client/src/app/main/dashboard/dashboard-edit-profile/dashboard-edit-profile-page.js @@ -4,6 +4,7 @@ import _ from 'lodash'; import API from 'lib-app/api-call'; import i18n from 'lib-app/i18n'; +import { getCustomFieldParamName } from 'lib-core/APIUtils'; import SessionActions from 'actions/session-actions'; import AreYouSure from 'app-components/are-you-sure'; @@ -42,15 +43,6 @@ class DashboardEditProfilePage extends React.Component { return (
-
{i18n('ADDITIONAL_FIELDS')}
-
this.setState({customFieldsFrom: form})} onSubmit={this.onCustomFieldsSubmit.bind(this)}> -
- {this.state.customFields.map(this.renderCustomField.bind(this))} -
-
- {i18n('SAVE')} -
-
{i18n('EDIT_EMAIL')}
@@ -65,6 +57,23 @@ class DashboardEditProfilePage extends React.Component { {i18n('CHANGE_PASSWORD')} {this.renderMessagePass()} + {this.state.customFields.length ? this.renderCustomFields() : null} +
+ ); + } + + renderCustomFields() { + return ( +
+
{i18n('ADDITIONAL_FIELDS')}
+
this.setState({customFieldsFrom: form})} onSubmit={this.onCustomFieldsSubmit.bind(this)}> +
+ {this.state.customFields.map(this.renderCustomField.bind(this))} +
+
+ {i18n('SAVE')} +
+
); } @@ -116,9 +125,9 @@ class DashboardEditProfilePage extends React.Component { customFields.forEach(customField => { if(customField.type === 'select') { - parsedFrom[`customfield_${customField.name}`] = customField.options[form[customField.name]].name; + parsedFrom[getCustomFieldParamName(customField.name)] = customField.options[form[customField.name]].name; } else { - parsedFrom[`customfield_${customField.name}`] = form[customField.name]; + parsedFrom[getCustomFieldParamName(customField.name)] = form[customField.name]; } }); diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index f331f06d..361dcb00 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -204,7 +204,7 @@ export default { 'IMAGE_HEADER_URL': 'Image header URL', 'IMAGE_HEADER_DESCRIPTION': 'Image that will be used as header of the email', 'EMAIL_SETTINGS': 'Email Settings', - 'ADDITIONAL_FIELDS': 'Additonal Fields', + 'ADDITIONAL_FIELDS': 'Edit additonal fields', 'NEW_CUSTOM_FIELD': 'New Custom field', 'TYPE': 'Type', 'SELECT_INPUT': 'Select input', @@ -326,7 +326,7 @@ export default { 'REGISTRATION_ENABLED': 'Registration has been enabled', 'ADD_API_KEY_DESCRIPTION': 'Insert the name and a registration api key will be generated.', 'SIGN_UP_VIEW_DESCRIPTION': 'Here you can create an account for our support center. It is required to send tickets and see documentation.', - 'EDIT_PROFILE_VIEW_DESCRIPTION': 'Here you can edit your user by changing your email or your password.', + 'EDIT_PROFILE_VIEW_DESCRIPTION': 'Here you can edit your user preferences.', 'ENABLE_USER_SYSTEM_DESCRIPTION': 'Enable/disable the use of an user system. If you disable it, all users will be deleted but the tickets will be kept. If you enable it, the users of existent tickets will be created.', 'CSV_DESCRIPTION': 'The CSV file must have 3 columns: email, password, name. There is no limit in row count. It will be created one user per row in the file.', 'SMTP_SERVER_DESCRIPTION': 'The configuration of the SMTP server allows the application to send mails. If you do not configure it, no emails will be sent by OpenSupports.', diff --git a/client/src/lib-core/APIUtils.js b/client/src/lib-core/APIUtils.js index c9c544f5..d8ca183e 100644 --- a/client/src/lib-core/APIUtils.js +++ b/client/src/lib-core/APIUtils.js @@ -44,4 +44,8 @@ const APIUtils = { } }; +export const getCustomFieldParamName = function (customFieldName) { + return `customfield_${customFieldName}`.replace(/ /g,'_'); +} + export default APIUtils; diff --git a/server/libs/Controller.php b/server/libs/Controller.php index e8c61ff2..04711b5f 100755 --- a/server/libs/Controller.php +++ b/server/libs/Controller.php @@ -152,9 +152,8 @@ abstract class Controller { public static function getCustomFieldValues() { $customFields = Customfield::getAll(); $customFieldValues = new DataStoreList(); - foreach($customFields as $customField) { - $value = Controller::request('customfield_' . $customField->name); + $value = Controller::request('customfield_' . str_replace(' ', '_', $customField->name)); if($value !== null) { $customFieldValue = new Customfieldvalue(); $customFieldValue->setProperties([ @@ -183,7 +182,6 @@ abstract class Controller { $customFieldValues->add($customFieldValue); } } - return $customFieldValues; } } From f9e8a0abec43119dd8e28a9973142af4848a1079 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 26 Dec 2019 17:25:00 -0300 Subject: [PATCH 42/50] add department test and escape query --- server/controllers/ticket/search.php | 24 ++++----- server/libs/validations/validPrioritys.php | 19 ------- .../tests/controllers/ticket/searchTest.php | 50 ++++++++++++++++--- 3 files changed, 54 insertions(+), 39 deletions(-) delete mode 100644 server/libs/validations/validPrioritys.php diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index a3ea08fe..3eb05304 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -111,21 +111,22 @@ class SearchController extends Controller { 'query' => Controller::request('query'), 'orderBy' => json_decode(Controller::request('orderBy'),true), 'page' => Controller::request('page'), - 'user' => Controller::getLoggedUser(), + 'allowedDepartments' => Controller::getLoggedUser()->sharedDepartmentList->toArray(), ]; + $query = $this->getSQLQuery($inputs); $queryWithOrder = $this->getSQLQueryWithOrder($inputs); - $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; - $ticketIdList = RedBean::getAll($queryWithOrder); + $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, @@ -171,7 +172,7 @@ class SearchController extends Controller { 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['user'], $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; @@ -243,9 +244,8 @@ class SearchController extends Controller { } } - private function setDepartmentFilter($departments,$user, &$filters){ - - $validDepartments = $this->generateValidDepartmentList($departments, $user); + private function setDepartmentFilter($departments,$allowedDepartments, &$filters){ + $validDepartments = $this->generateValidDepartmentList($departments, $allowedDepartments); if ($filters != "") $filters .= " and "; $first = TRUE; @@ -302,16 +302,16 @@ class SearchController extends Controller { if($search != null){ if ($filters != "") $filters .= " and "; - $ticketevent = ( $ticketEventTableExists ? " or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $search ."%')" : ""); - $filters .= " (ticket.title LIKE '%" . $search . "%' or ticket.content LIKE '%" . $search . "%' or ticket.ticket_number LIKE '%" . $search . "%'". $ticketevent ." )"; + $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, $user){ + private function generateValidDepartmentList($departments, $allowedDepartments){ $result = []; $managedDepartments = []; if($departments == null) $departments = []; - foreach ($user->sharedDepartmentList->toArray() as $department) { + foreach ($allowedDepartments as $department) { array_push($managedDepartments,$department['id']); } $result = array_intersect($departments,$managedDepartments); diff --git a/server/libs/validations/validPrioritys.php b/server/libs/validations/validPrioritys.php deleted file mode 100644 index 53bf9505..00000000 --- a/server/libs/validations/validPrioritys.php +++ /dev/null @@ -1,19 +0,0 @@ -= 1 and ticket.date <= 2) GROUP BY ticket.id' ); } - /* + public function testDepartmentsFilter() { $this->assertEquals( $this->searchController->getSQLQuery([ - 'departments' => null + '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) GROUP BY 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.department_id = 2 or ticket.department_id = 1 or ticket.department_id = 3) GROUP BY ticket.id' ); $this->assertEquals( $this->searchController->getSQLQuery([ - 'departments' => [1] + '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] + '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([ @@ -228,7 +261,8 @@ class SearchControllerTest extends TestCase { $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 '%hello world%' or ticket.content LIKE '%hello world%' or ticket.ticket_number LIKE '%hello world%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%hello world%') ) GROUP BY 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" + ); } public function testQueryWithOrder() { @@ -244,7 +278,7 @@ class SearchControllerTest extends TestCase { '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 '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') 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" + "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 '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') 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( From 38c6295a7b8a95093d8d9e78dd33c11c36dea69e Mon Sep 17 00:00:00 2001 From: LautaroCesso <59095036+LautaroCesso@users.noreply.github.com> Date: Fri, 27 Dec 2019 14:46:33 -0300 Subject: [PATCH 43/50] Improved the render in MainSignUpWidget (Issue #576). (#683) * replace spaces for underscores in edit custom fields * Create function getCustomFieldParamName * Improved the render in MainSignUpWidget --- client/src/app/main/main-signup/main-signup-widget.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/app/main/main-signup/main-signup-widget.js b/client/src/app/main/main-signup/main-signup-widget.js index b0a77964..bfe390fe 100644 --- a/client/src/app/main/main-signup/main-signup-widget.js +++ b/client/src/app/main/main-signup/main-signup-widget.js @@ -26,7 +26,7 @@ class MainSignUpWidget extends React.Component { this.state = { loading: false, email: null, - customFields: [] + customFields: null }; } @@ -39,6 +39,7 @@ class MainSignUpWidget extends React.Component { } render() { + if(!this.state.customFields) return null; return (
From d25da2c5ff055d0575bd88c99347a16d68ace8af Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 19:50:50 -0300 Subject: [PATCH 44/50] add validation test --- client/src/data/languages/en.js | 2 +- server/controllers/ticket/search.php | 6 +++--- server/tests/controllers/ticket/searchTest.php | 2 +- tests/init.rb | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index f244d610..7816e9c3 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -298,7 +298,7 @@ export default { 'MY_TICKETS_DESCRIPTION': 'Here you can view the tickets you are responsible for.', 'NEW_TICKETS_DESCRIPTION': 'Here you can view all the new tickets that are not assigned by anyone.', 'ALL_TICKETS_DESCRIPTION': 'Here you can view the tickets of the departments you are assigned.', - 'SEARCH_TICKETS_DESCRIPTION': 'Here you can search tickets by specifics filters.', + '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.', diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index 3eb05304..e1fe5696 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -91,7 +91,7 @@ class SearchController extends Controller { 'error' => ERRORS::INVALID_ASSIGNED_FILTER ], 'orderBy' => [ - 'validation' => DataValidator::oneOf(DataValidator::ValidOrderBy(),DataValidator::nullType()), + 'validation' => DataValidator::oneOf(DataValidator::validOrderBy(),DataValidator::nullType()), 'error' => ERRORS::INVALID_ORDER_BY ], ] @@ -340,8 +340,8 @@ class SearchController extends Controller { $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 '%".$querysearch."%') THEN ticketevent.content END desc," : ""); - $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $querysearch ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $querysearch ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $querysearch ."%') THEN ticket.content END desc," . $ticketeventOrder ; + $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 ; } } diff --git a/server/tests/controllers/ticket/searchTest.php b/server/tests/controllers/ticket/searchTest.php index b402a81d..6f74449a 100644 --- a/server/tests/controllers/ticket/searchTest.php +++ b/server/tests/controllers/ticket/searchTest.php @@ -278,7 +278,7 @@ class SearchControllerTest extends TestCase { '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 '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') 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" + "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( diff --git a/tests/init.rb b/tests/init.rb index ffffdcb7..e86faea2 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -71,4 +71,5 @@ require './ticket/add-tag.rb' require './ticket/delete-tag.rb' require './ticket/edit-comment.rb' require './system/disable-user-system.rb' +require './ticket/search.rb' # require './system/get-stats.rb' From 9f84239c3e5b162cbd64cf24bc46ecab06193f07 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 19:59:23 -0300 Subject: [PATCH 45/50] search ruby tests --- tests/ticket/search.rb | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/ticket/search.rb diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb new file mode 100644 index 00000000..4b3e4c0a --- /dev/null +++ b/tests/ticket/search.rb @@ -0,0 +1,114 @@ +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 dateRange values are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + dateRange: [11,69,11] + }) + (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') + 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 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 From e64e8e65f165b335d77514b4b7eb844feaffec61 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 20:51:22 -0300 Subject: [PATCH 46/50] update rudy test --- tests/ticket/search.rb | 43 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb index 4b3e4c0a..ad22f081 100644 --- a/tests/ticket/search.rb +++ b/tests/ticket/search.rb @@ -18,7 +18,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - tags: [1,11,111,1111,11111,111111,1111111,11111111] + tags: "[1,11,111,1111,11111,111111,1111111,11111111]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_TAG_FILTER') @@ -51,7 +51,18 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - priority: [0,1,5,6] + 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') @@ -62,7 +73,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - dateRange: [11,69,11] + dateRange: "[11,69,()) ]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_DATE_RANGE_FILTER') @@ -73,7 +84,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - departments: [-1,-2,99] + departments: "[-1,-2,99]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_DEPARTMENT_FILTER') @@ -84,7 +95,16 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - authors: [{id:30001, staff: 1},{id:30,staff: 3}] + 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') @@ -101,12 +121,23 @@ describe '/ticket/search' do (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, + query: 'dasjfawj!!!)())' + }) + (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}] + orderBy: "{value: 'closeddd', asc: 11}" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_ORDER_BY') From 702b7862028acfc2ce394c388a0e011ad58bed62 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 22:46:25 -0300 Subject: [PATCH 47/50] search ruby test --- tests/ticket/search.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb index ad22f081..2ae320d6 100644 --- a/tests/ticket/search.rb +++ b/tests/ticket/search.rb @@ -126,7 +126,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - query: 'dasjfawj!!!)())' + assigned: 11113 }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_ASSIGNED_FILTER') From 8be43bb7ead604d33f77cd6ffb95f989ddc48202 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Sat, 28 Dec 2019 17:50:11 -0300 Subject: [PATCH 48/50] resolve mirrors errors --- server/controllers/ticket/search.php | 24 ++++++++++----------- server/libs/validations/validDateRange.php | 22 +++++++++---------- server/libs/validations/validOrderBy.php | 9 +++----- server/libs/validations/validPriorities.php | 4 ++-- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index e1fe5696..00431c34 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -195,19 +195,19 @@ class SearchController extends Controller { } } public function setClosedFilter($closed, &$filters){ - if ($closed != null) { + if ($closed !== null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.closed = " . $closed ; } } private function setSeenFilter($unreadStaff, &$filters){ - if ($unreadStaff != null) { + if ($unreadStaff !== null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.unread_staff = " . $unreadStaff; } } private function setPriorityFilter($priorities, &$filters){ - if($priorities != null){ + if($priorities !== null){ $first = TRUE; if ($filters != "") $filters .= " and "; foreach(array_unique($priorities) as $priority) { @@ -220,11 +220,11 @@ class SearchController extends Controller { } if($priority == 0){ - $filters .= "ticket.priority = " . "'low'"; + $filters .= "ticket.priority = 'low'"; }elseif($priority == 1){ - $filters .= "ticket.priority = " . "'medium'"; + $filters .= "ticket.priority = 'medium'"; }elseif($priority == 2){ - $filters .= "ticket.priority = " . "'high'"; + $filters .= "ticket.priority = 'high'"; } @@ -235,7 +235,7 @@ class SearchController extends Controller { } private function setDateFilter($dateRange, &$filters){ - if ($dateRange != null) { + if ($dateRange !== null) { if ($filters != "") $filters .= " and "; foreach($dateRange as $key => $date) { @@ -263,7 +263,7 @@ class SearchController extends Controller { } private function setAuthorFilter($authors, &$filters){ - if($authors != null){ + if($authors !== null){ $first = TRUE; if ($filters != "") $filters .= " and "; @@ -289,7 +289,7 @@ class SearchController extends Controller { } private function setAssignedFilter($assigned, &$filters){ - if($assigned != null){ + if($assigned !== null){ if ($filters != "") $filters .= " and "; $key = ""; $assigned == 0 ? $key = "IS NULL" : $key = "IS NOT NULL"; @@ -300,7 +300,7 @@ class SearchController extends Controller { private function setStringFilter($search, &$filters){ $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); - if($search != null){ + 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 ." )"; @@ -331,7 +331,7 @@ class SearchController extends Controller { $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){ + if($orderBy !== null){ $orientation = ($orderBy['asc'] ? " asc" : " desc" ); $order .= "ticket." . $orderBy['value'] . $orientation . ","; }; @@ -339,7 +339,7 @@ class SearchController extends Controller { private function setStringOrder($querysearch, &$order){ $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); - if($querysearch != null){ + 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 ; } diff --git a/server/libs/validations/validDateRange.php b/server/libs/validations/validDateRange.php index 05f6a3c2..b332909e 100644 --- a/server/libs/validations/validDateRange.php +++ b/server/libs/validations/validDateRange.php @@ -7,15 +7,15 @@ use Respect\Validation\Rules\AbstractRule; class ValidDateRange extends AbstractRule { public function validate($dateRange) { - $dateArray = json_decode($dateRange); - $counter = 0; - if(is_array($dateArray)){ - foreach ($dateArray as $date) { - if (is_numeric($date)) $counter++; - } + $dateArray = json_decode($dateRange); + $length = count($dateArray); - return ((sizeof($dateArray) == 2 && $counter == 2) || sizeof($dateArray) == 0 ); - } - return false; - } -} \ No newline at end of file + if(is_array($dateArray) && $length == 2 ){ + foreach ($dateArray as $date) { + if (!is_numeric($date)) return false; + } + return $dateArray[0] <= $dateArray[1]; + } + return false; + } +} diff --git a/server/libs/validations/validOrderBy.php b/server/libs/validations/validOrderBy.php index 44466805..40babc27 100644 --- a/server/libs/validations/validOrderBy.php +++ b/server/libs/validations/validOrderBy.php @@ -8,15 +8,12 @@ class ValidOrderBy extends AbstractRule { public function validate($orderBy) { if(is_object(json_decode($orderBy))){ $values =["closed","owner_id","unread_staff","priority","date"]; - $isTrue = false; + $object = json_decode($orderBy); - if($object->asc !== 1 && $object->asc !== 0) return false; + if(($object->asc !== 1 && $object->asc !== 0) || !in_array($object->value, $values)) return false; - foreach ($values as $value) { - if($object->value == $value) $isTrue = true; - } - return $isTrue; + return true; } } } diff --git a/server/libs/validations/validPriorities.php b/server/libs/validations/validPriorities.php index 7313deec..aa3ba658 100644 --- a/server/libs/validations/validPriorities.php +++ b/server/libs/validations/validPriorities.php @@ -5,8 +5,8 @@ namespace CustomValidations; use Respect\Validation\Rules\AbstractRule; class ValidPriorities extends AbstractRule { - public function validate($prioritys) { - $PriorityList = json_decode($prioritys); + public function validate($priorities) { + $PriorityList = json_decode($priorities); if(is_array($PriorityList)){ foreach (array_unique($PriorityList) as $priorityId) { From cc9c51b85627de371c93b3590d17dda2d3dbf15c Mon Sep 17 00:00:00 2001 From: Guillermo Date: Sun, 29 Dec 2019 15:48:57 -0300 Subject: [PATCH 49/50] fix date range validation on php 7.2 --- server/libs/validations/validDateRange.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/libs/validations/validDateRange.php b/server/libs/validations/validDateRange.php index b332909e..c2bb3f05 100644 --- a/server/libs/validations/validDateRange.php +++ b/server/libs/validations/validDateRange.php @@ -8,9 +8,8 @@ class ValidDateRange extends AbstractRule { public function validate($dateRange) { $dateArray = json_decode($dateRange); - $length = count($dateArray); - - if(is_array($dateArray) && $length == 2 ){ + + if(is_array($dateArray) && count($dateArray) == 2 ){ foreach ($dateArray as $date) { if (!is_numeric($date)) return false; } From 97d6bd6a36a47a3a5203c4ac30b0d3f615034b68 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 2 Jan 2020 10:09:44 -0300 Subject: [PATCH 50/50] fix ticket search path showing --- .../src/app/admin/panel/tickets/admin-panel-search-tickets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js index 2dd4f530..f5726f2c 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js @@ -20,7 +20,7 @@ class AdminPanelSearchTickets extends React.Component { } getFilters() { - let customList = window.customTicketList[this.props.location.query.custom*1] ? window.customTicketList[this.props.location.query.custom*1] : null + let customList = (window.customTicketList && window.customTicketList[this.props.location.query.custom*1]) ? window.customTicketList[this.props.location.query.custom*1] : null return { ...customList };
{header.value} @@ -97,7 +97,7 @@ class Table extends React.Component { }; return ( - + {row[key]}