From 614b5a1a6709dcbf8a5c1a4aba85d06e73ad9af8 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Wed, 14 Nov 2018 19:58:32 -0300 Subject: [PATCH] Refactor mail templates frontend, update backend refactor --- .../settings/admin-panel-email-templates.js | 221 ++++++++++-------- .../settings/admin-panel-email-templates.scss | 4 +- client/src/core-components/form-field.scss | 2 +- server/controllers/system.php | 2 + .../controllers/system/edit-mail-template.php | 83 ++++++- .../system/get-mail-template-list.php | 36 +++ .../controllers/system/get-mail-template.php | 2 +- server/data/ERRORS.php | 15 ++ server/models/MailTemplate.php | 4 +- tests/system/mail-templates.rb | 33 ++- 10 files changed, 281 insertions(+), 121 deletions(-) create mode 100644 server/controllers/system/get-mail-template-list.php diff --git a/client/src/app/admin/panel/settings/admin-panel-email-templates.js b/client/src/app/admin/panel/settings/admin-panel-email-templates.js index 076be4d2..ce3e9372 100644 --- a/client/src/app/admin/panel/settings/admin-panel-email-templates.js +++ b/client/src/app/admin/panel/settings/admin-panel-email-templates.js @@ -18,28 +18,31 @@ import SubmitButton from 'core-components/submit-button'; class AdminPanelEmailTemplates extends React.Component { state = { - loaded: false, - items: [], - formLoading: false, - selectedIndex: 0, + loadingList: true, + loadingTemplate: false, + templates: [], + loadingForm: false, + selectedIndex: -1, edited: false, errors: {}, language: 'en', form: { - title: '', - content: '' + subject: '', + text1: '', + text2: '', + text3: '', } }; componentDidMount() { - this.retrieveEmailTemplates(); + this.retrieveMailTemplateList(); } render() { return (
- {(this.state.loaded) ? this.renderContent() : this.renderLoading()} + {(!this.state.loadingList) ? this.renderContent() : this.renderLoading()}
); } @@ -50,33 +53,7 @@ class AdminPanelEmailTemplates extends React.Component {
-
- this.onItemChange(this.state.selectedIndex, event.target.value)} fieldProps={{ - type: 'allowed', - size: 'medium' - }}/> -
-
-
- -
-
- -
-
- {i18n('SAVE')} -
-
- {(this.state.edited) ? this.renderDiscardButton() : null} -
- -
-
-
- -
+ {(this.state.selectedIndex != -1) ? this.renderForm() : null} ); } @@ -89,6 +66,42 @@ class AdminPanelEmailTemplates extends React.Component { ); } + renderForm() { + return ( +
+ this.onItemChange(this.state.selectedIndex, event.target.value)} fieldProps={{ + type: 'allowed', + size: 'medium' + }}/> +
+
+
+ +
+
+ + + {(this.state.form.text2) ? : null} + {(this.state.form.text3) ? : null} + +
+
+ {i18n('SAVE')} +
+
+ {(this.state.edited) ? this.renderDiscardButton() : null} +
+ +
+
+
+ +
+ ); + } + renderDiscardButton() { return (
@@ -102,7 +115,7 @@ class AdminPanelEmailTemplates extends React.Component { getListingProps() { return { title: i18n('EMAIL_TEMPLATES'), - items: this.getItems(), + items: this.getTemplateItems(), selectedIndex: this.state.selectedIndex, onChange: this.onItemChange.bind(this) }; @@ -112,49 +125,79 @@ class AdminPanelEmailTemplates extends React.Component { return { values: this.state.form, errors: this.state.errors, - loading: this.state.formLoading, + loading: this.state.loadingForm, onChange: (form) => {this.setState({form, edited: true})}, onValidateErrors: (errors) => {this.setState({errors})}, onSubmit: this.onFormSubmit.bind(this) } } - getItems() { - return this.state.items.map((item) => { + getTemplateItems() { + return this.state.templates.map((template) => { return { - content: item.type + content: template }; }); } onItemChange(index, language) { if(this.state.edited) { - AreYouSure.openModal(i18n('WILL_LOSE_CHANGES'), this.updateForm.bind(this, index, language)); + AreYouSure.openModal(i18n('WILL_LOSE_CHANGES'), this.retrieveEmailTemplate.bind(this, index, language || this.state.language)); } else { - this.updateForm(index, language); + this.retrieveEmailTemplate(index, language || this.state.language); } } onFormSubmit(form) { - this.setState({formLoading: true}); + const {selectedIndex, language, templates} = this.state; + + this.setState({loadingForm: true}); API.call({ path: '/system/edit-mail-template', data: { - templateType: this.state.items[this.state.selectedIndex].type, - subject: form.title, - body: form.content, - language: this.state.language + template: templates[selectedIndex], + language, + subject: form.subject, + text1: form.text1, + text2: form.text2, + text3: form.text3, } }).then(() => { - this.setState({formLoading: false}); - this.retrieveEmailTemplates(); + this.setState({loadingForm: false, edited: false}); + }).catch(response => { + this.setState({ + loadingForm: false, + }); + + switch(response.message) { + case 'INVALID_SUBJECT': + this.setState({ + errors: {subject: 'Invalid syntax'} + }); + break; + case 'INVALID_TEXT_1': + this.setState({ + errors: {text1: 'Invalid syntax'} + }); + break; + case 'INVALID_TEXT_2': + this.setState({ + errors: {text2: 'Invalid syntax'} + }); + break; + case 'INVALID_TEXT_3': + this.setState({ + errors: {text3: 'Invalid syntax'} + }); + break; + } }); } onDiscardChangesClick(event) { event.preventDefault(); - this.onItemChange(this.state.selectedIndex); + this.onItemChange(this.state.selectedIndex, this.state.language); } onRecoverClick(event) { @@ -163,73 +206,45 @@ class AdminPanelEmailTemplates extends React.Component { } recoverEmailTemplate() { + const {selectedIndex, language, templates} = this.state; + API.call({ path: '/system/recover-mail-template', data: { - templateType: this.state.items[this.state.selectedIndex].type, - language: this.state.language + template: templates[selectedIndex], + language } }).then(() => { - this.retrieveEmailTemplates(); + this.retrieveEmailTemplate(this.state.selectedIndex, language); }); } - updateForm(index, language) { - let form = _.clone(this.state.form); - let items = this.state.items; - - language = language || this.state.language; - - form.title = (items[index] && items[index][language].subject) || ''; - form.content = (items[index] && items[index][language].body) || ''; - + retrieveEmailTemplate(index, language) { this.setState({ - selectedIndex: index, - language: language, - edited: false, - formLoading: false, - form: form, - errors: {} + loadingForm: true, }); + + API.call({ + path: '/system/get-mail-template', + data: {template: this.state.templates[index], language} + }).then((result) => this.setState({ + language, + selectedIndex: index, + edited: false, + loadingForm: false, + form: result.data, + errors: {}, + })); } - retrieveEmailTemplates() { - return API.call({ - path: '/system/get-mail-templates', + retrieveMailTemplateList() { + API.call({ + path: '/system/get-mail-template-list', data: {} }).then((result) => this.setState({ - edited: false, - loaded: true, - items: this.getParsedItems(result.data) - }, this.updateForm.bind(this, this.state.selectedIndex))); - } - - getParsedItems(items) { - let parsedItems = {}; - - _.forEach(items, (item) => { - if(parsedItems[item.type]) { - parsedItems[item.type][item.language] = { - subject: item.subject, - body: item.body - }; - } else { - parsedItems[item.type] = { - [item.language]: { - subject: item.subject, - body: item.body - } - }; - } - }); - - parsedItems = Object.keys(parsedItems).map((type) => { - return _.extend({ - type: type - }, parsedItems[type]); - }); - - return parsedItems; + loadingList: false, + templates: result.data + })); } } diff --git a/client/src/app/admin/panel/settings/admin-panel-email-templates.scss b/client/src/app/admin/panel/settings/admin-panel-email-templates.scss index 43f49a28..2b7ade6b 100644 --- a/client/src/app/admin/panel/settings/admin-panel-email-templates.scss +++ b/client/src/app/admin/panel/settings/admin-panel-email-templates.scss @@ -2,7 +2,7 @@ &__text-area { width: 100%; - height: 157px; + height: 45px; } &__save-button { @@ -23,4 +23,4 @@ display: inline-block; margin-left: 10px; } -} \ No newline at end of file +} diff --git a/client/src/core-components/form-field.scss b/client/src/core-components/form-field.scss index 28a3d6df..4629ee9a 100644 --- a/client/src/core-components/form-field.scss +++ b/client/src/core-components/form-field.scss @@ -45,4 +45,4 @@ padding-bottom: 10px; } } -} \ No newline at end of file +} diff --git a/server/controllers/system.php b/server/controllers/system.php index 59de6d88..539da3e1 100755 --- a/server/controllers/system.php +++ b/server/controllers/system.php @@ -10,6 +10,7 @@ require_once 'system/add-department.php'; require_once 'system/edit-department.php'; require_once 'system/delete-department.php'; require_once 'system/get-logs.php'; +require_once 'system/get-mail-template-list.php'; require_once 'system/get-mail-template.php'; require_once 'system/edit-mail-template.php'; require_once 'system/recover-mail-template.php'; @@ -41,6 +42,7 @@ $systemControllerGroup->addController(new AddDepartmentController); $systemControllerGroup->addController(new EditDepartmentController); $systemControllerGroup->addController(new DeleteDepartmentController); $systemControllerGroup->addController(new GetLogsController); +$systemControllerGroup->addController(new GetMailTemplateListController); $systemControllerGroup->addController(new GetMailTemplateController); $systemControllerGroup->addController(new EditMailTemplateController); $systemControllerGroup->addController(new RecoverMailTemplateController); diff --git a/server/controllers/system/edit-mail-template.php b/server/controllers/system/edit-mail-template.php index 781e64ca..b452cccb 100755 --- a/server/controllers/system/edit-mail-template.php +++ b/server/controllers/system/edit-mail-template.php @@ -24,6 +24,9 @@ use Respect\Validation\Validator as DataValidator; * @apiUse INVALID_TEMPLATE * @apiUse INVALID_LANGUAGE * @apiUse INVALID_SUBJECT + * @apiUse INVALID_TEXT_1 + * @apiUse INVALID_TEXT_2 + * @apiUse INVALID_TEXT_3 * * @apiSuccess {Object} data Empty object * @@ -33,6 +36,11 @@ class EditMailTemplateController extends Controller { const PATH = '/edit-mail-template'; const METHOD = 'POST'; + private $langauge; + private $templateType; + private $subject; + private $texts; + public function validations() { return [ 'permission' => 'staff_3', @@ -54,25 +62,82 @@ class EditMailTemplateController extends Controller { } public function handler() { - $language = Controller::request('language'); - $templateType = Controller::request('template'); - $subject = Controller::request('subject', true); - $texts = [ + $this->language = Controller::request('language'); + $this->templateType = Controller::request('template'); + $this->subject = Controller::request('subject', true); + $this->texts = [ Controller::request('text1'), Controller::request('text2'), Controller::request('text3'), ]; - $mailTemplate = MailTemplate::findOne(' language = ? AND template = ?', [$language, $templateType]); + $mailTemplate = MailTemplate::findOne(' language = ? AND template = ?', [$this->language, $this->templateType]); + if($mailTemplate->isNull()) { throw new Exception(ERRORS::INVALID_TEMPLATE); } - $mailTemplate->subject = $subject; - $mailTemplate->text1 = $texts[0]; - $mailTemplate->text2 = $texts[1]; - $mailTemplate->text3 = $texts[2]; + + $this->validateReplacements(); + + $mailTemplate->subject = $this->subject; + $mailTemplate->text1 = $this->texts[0]; + $mailTemplate->text2 = $this->texts[1]; + $mailTemplate->text3 = $this->texts[2]; + $mailTemplate->store(); Response::respondSuccess(); } + + public function validateReplacements() { + $originalText = MailTexts::getTexts()[$this->language][$this->templateType]; + + if(!$this->includes( + $this->getReplacementStrings($originalText[1]), + $this->getReplacementStrings($this->texts[0]) + )) { + throw new Exception(ERRORS::INVALID_TEXT_1); + } + + if(!$this->includes( + $this->getReplacementStrings($originalText[2]), + $this->getReplacementStrings($this->texts[1]) + )) { + throw new Exception(ERRORS::INVALID_TEXT_2); + } + + if(!$this->includes( + $this->getReplacementStrings($originalText[3]), + $this->getReplacementStrings($this->texts[2]) + )) { + throw new Exception(ERRORS::INVALID_TEXT_3); + } + } + + public function includes($array1, $array2) { + foreach($array1 as $item) { + if(!in_array($item, $array2)) return false; + } + + return true; + } + + public function getReplacementStrings($string) { + $replacements = []; + + for($i=0; $i 'staff_3', + 'requestData' => [] + ]; + } + + public function handler() { + Response::respondSuccess(array_keys(MailTemplate::getFilePaths())); + } +} diff --git a/server/controllers/system/get-mail-template.php b/server/controllers/system/get-mail-template.php index de135229..86b63e6a 100755 --- a/server/controllers/system/get-mail-template.php +++ b/server/controllers/system/get-mail-template.php @@ -18,7 +18,7 @@ use Respect\Validation\Validator as DataValidator; * * @apiUse NO_PERMISSION * - * @apiSuccess {[MailTemplate](#api-Data_Structures-ObjectMailtemplate)[]} data Array of mail templates + * @apiSuccess {[MailTemplate](#api-Data_Structures-ObjectMailtemplate)} data Data of the mail template * */ diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index 5776d831..add61421 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -187,6 +187,18 @@ * @apiDefine USER_DISABLED * @apiError {String} USER_DISABLED User is disabled */ +/** + * @apiDefine INVALID_TEXT_1 + * @apiError {String} INVALID_TEXT_1 text1 of mail template has an invalid syntax or missing variables + */ +/** + * @apiDefine INVALID_TEXT_2 + * @apiError {String} INVALID_TEXT_2 text2 of mail template has an invalid syntax or missing variables + */ +/** + * @apiDefine INVALID_TEXT_3 + * @apiError {String} INVALID_TEXT_3 text3 of mail template has an invalid syntax or missing variables + */ class ERRORS { const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'; @@ -237,4 +249,7 @@ class ERRORS { const ALREADY_DISABLED = 'ALREADY_DISABLED'; const ALREADY_ENABLED = 'ALREADY_ENABLED'; const USER_DISABLED = 'USER_DISABLED'; + const INVALID_TEXT_1 = 'INVALID_TEXT_1'; + const INVALID_TEXT_2 = 'INVALID_TEXT_2'; + const INVALID_TEXT_3 = 'INVALID_TEXT_3'; } diff --git a/server/models/MailTemplate.php b/server/models/MailTemplate.php index 17b93d1a..a9f6c74f 100755 --- a/server/models/MailTemplate.php +++ b/server/models/MailTemplate.php @@ -8,7 +8,9 @@ use RedBeanPHP\Facade as RedBean; * @apiParam {String} type The type of the mail template. * @apiParam {String} subject The subject of the mail template. * @apiParam {string} language The language of the mail template. - * @apiParam {String} body The body of the mail template. + * @apiParam {String} text1 First paragraph of the mail template. + * @apiParam {String} text2 Second paragraph of the mail template. + * @apiParam {String} text3 Thrid paragraph of the mail template. */ class MailTemplate extends DataStore { diff --git a/tests/system/mail-templates.rb b/tests/system/mail-templates.rb index 583e0c71..5de33b1a 100644 --- a/tests/system/mail-templates.rb +++ b/tests/system/mail-templates.rb @@ -69,8 +69,8 @@ describe 'Mail templates' do template: 'USER_SIGNUP', subject: 'new subject', text1: 'new text1', - text2: 'new text2', - text3: 'new text3', + text2: 'new text2 {{name}}', + text3: 'new text3 {{url}}/verify-token/{{to}}/{{verificationToken}}', }) (result['status']).should.equal('success') @@ -80,9 +80,34 @@ describe 'Mail templates' do (row['template']).should.equal('USER_SIGNUP') (row['subject']).should.equal('new subject') (row['text1']).should.equal('new text1') - (row['text2']).should.equal('new text2') - (row['text3']).should.equal('new text3') + (row['text2']).should.equal('new text2 {{name}}') + (row['text3']).should.equal('new text3 {{url}}/verify-token/{{to}}/{{verificationToken}}') end + + it 'should fail if one of the texts has invalid syntax' do + result = request('/system/edit-mail-template', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + language: 'en', + template: 'USER_SIGNUP', + subject: 'new subject', + text1: 'new text1', + text2: 'new text2', + text3: 'new text3 {{url}}/verify-token/{{to}}/{{verificationToken}}', + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TEXT_2') + + row = $database.getRow('mailtemplate', 1, 'id') + + (row['template']).should.equal('USER_SIGNUP') + (row['subject']).should.equal('new subject') + (row['text1']).should.equal('new text1') + (row['text2']).should.equal('new text2 {{name}}') + (row['text3']).should.equal('new text3 {{url}}/verify-token/{{to}}/{{verificationToken}}') + end + end describe 'system/recover-mail-template' do