From c475d0e35c34b0c48b0ccbff2671375efaa00c47 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Mon, 7 Oct 2019 14:47:28 -0300 Subject: [PATCH 01/29] Use single build file --- client/src/index.html | 1 + client/src/index.php | 1 - client/webpack.config.js | 23 +---------------------- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/client/src/index.html b/client/src/index.html index 969722e9..90c8c2e2 100755 --- a/client/src/index.html +++ b/client/src/index.html @@ -13,6 +13,7 @@
+ diff --git a/client/src/index.php b/client/src/index.php index 750d5402..abd6c654 100755 --- a/client/src/index.php +++ b/client/src/index.php @@ -28,7 +28,6 @@ - diff --git a/client/webpack.config.js b/client/webpack.config.js index 39f959a1..3489ce2a 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -10,15 +10,11 @@ const {dependencies} = require('./package.json'); const BUILD_DIR = path.join(__dirname, 'build'); const APP_DIR = path.join(__dirname, 'src'); -const VENDOR_LIST = Object.keys(dependencies); - const config = env => { return { devtool: 'source-map', entry: { - config: APP_DIR + '/config.js', bundle: APP_DIR + '/index.js', - vendor: VENDOR_LIST, }, output: { path: BUILD_DIR, @@ -93,30 +89,13 @@ const config = env => { // }), new CopyPlugin([ './src/.htaccess', + './src/config.js', {from: './src/assets/images', to: 'images'}, ]), new BundleAnalyzerPlugin({ analyzerMode: process.env.NODE_ENV !== 'production' ? 'server' : 'disabled' }), ], - optimization: { - splitChunks: { - cacheGroups: { - config: { - chunks: 'initial', - name: 'config', - test: 'config', - enforce: true, - }, - vendor: { - chunks: 'initial', - name: 'vendor', - test: 'vendor', - enforce: true, - }, - } - }, - }, resolve: { modules: ['./src', './node_modules'] }, 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 02/29] 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 03/29] 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 04/29] 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 05/29] 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 06/29] 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 07/29] 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 08/29] 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 09/29] 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 10/29] 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 11/29] 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 12/29] 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 13/29] 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 14/29] 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 15/29] 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 16/29] 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 17/29] 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 18/29] 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 19/29] 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 20/29] 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 21/29] 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 22/29] 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 545c910d5e4c45beb7e9c6da822a6eafa042e128 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 11 Nov 2019 12:55:14 -0300 Subject: [PATCH 23/29] 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/29] 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 8ea9c6ee9bbf8198961e6ee2ae3e9d007dfc49f3 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Fri, 15 Nov 2019 18:50:35 -0300 Subject: [PATCH 25/29] 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 26/29] 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')} :
} +