Ivan - Add SMTP improvements WIP

This commit is contained in:
ivan 2017-06-10 04:17:54 -03:00
parent 4d5ff5449d
commit 4d7ac557c7
19 changed files with 195 additions and 55 deletions

View File

@ -46,5 +46,12 @@ export default {
data: {}
})
};
},
updateUserSystemSettings(payload) {
return {
type: 'UPDATE_USER_SYSTEM_SETTINGS',
payload: payload
};
}
};

View File

@ -57,8 +57,9 @@ import InstallStep1Language from 'app/install/install-step-1-language';
import InstallStep2Requirements from 'app/install/install-step-2-requirements';
import InstallStep3Database from 'app/install/install-step-3-database';
import InstallStep4UserSystem from 'app/install/install-step-4-user-system';
import InstallStep5Admin from 'app/install/install-step-5-admin';
import InstallStep6Completed from 'app/install/install-step-6-completed';
import InstallStep5Settings from 'app/install/install-step-5-user-system';
import InstallStep6Admin from 'app/install/install-step-6-admin';
import InstallStep7Completed from 'app/install/install-step-7-completed';
export default (
<Router history={history}>
@ -93,8 +94,9 @@ export default (
<Route path="step-2" component={InstallStep2Requirements} />
<Route path="step-3" component={InstallStep3Database} />
<Route path="step-4" component={InstallStep4UserSystem} />
<Route path="step-5" component={InstallStep5Admin} />
<Route path="step-6" component={InstallStep6Completed} />
<Route path="step-5" component={InstallStep5Settings} />
<Route path="step-6" component={InstallStep6Admin} />
<Route path="step-7" component={InstallStep7Completed} />
</Route>
<Route path="admin">
<IndexRoute component={AdminLoginPage} />

View File

@ -14,6 +14,7 @@ const steps = [
'SERVER_REQUIREMENTS',
'DATABASE_CONFIGURATION',
'USER_SYSTEM',
'SYSTEM_SETTINGS',
'ADMIN_SETUP',
'COMPLETED'
];

View File

@ -14,7 +14,7 @@ class InstallStep1Language extends React.Component {
render() {
return (
<div className="install-step-1">
<Header title={i18n('STEP_TITLE', {title: i18n('SELECT_LANGUAGE'), current: 1, total: 6})} description={i18n('STEP_1_DESCRIPTION')}/>
<Header title={i18n('STEP_TITLE', {title: i18n('SELECT_LANGUAGE'), current: 1, total: 7})} description={i18n('STEP_1_DESCRIPTION')}/>
<LanguageSelector {...this.getLanguageSelectorProps()} />
<div className="install-step-1__button">
<Button size="medium" type="secondary" onClick={() => history.push('/install/step-2')}>

View File

@ -30,7 +30,7 @@ class InstallStep2Requirements extends React.Component {
render() {
return (
<div className="install-step-2">
<Header title={i18n('STEP_TITLE', {title: i18n('SERVER_REQUIREMENTS'), current: 2, total: 6})} description={i18n('STEP_2_DESCRIPTION')}/>
<Header title={i18n('STEP_TITLE', {title: i18n('SERVER_REQUIREMENTS'), current: 2, total: 7})} description={i18n('STEP_2_DESCRIPTION')}/>
<div className="install-step-2__refresh">
<Button className="install-step-2__refresh-button" type="secondary" size="medium" onClick={this.retrieveRequirements.bind(this)}>
<Icon className="install-step-2__refresh-icon" name="refresh" /> {i18n('REFRESH')}

View File

@ -22,7 +22,7 @@ class InstallStep3Database extends React.Component {
render() {
return (
<div className="install-step-3">
<Header title={i18n('STEP_TITLE', {title: i18n('DATABASE_CONFIGURATION'), current: 3, total: 6})} description={i18n('STEP_3_DESCRIPTION')}/>
<Header title={i18n('STEP_TITLE', {title: i18n('DATABASE_CONFIGURATION'), current: 3, total: 7})} description={i18n('STEP_3_DESCRIPTION')}/>
{this.renderMessage()}
<Form loading={this.state.loading} onSubmit={this.onSubmit.bind(this)}>
<FormField name="dbHost" label={i18n('DATABASE_HOST')} fieldProps={{size: 'large'}} required/>

View File

@ -3,7 +3,7 @@ import { connect } from 'react-redux'
import history from 'lib-app/history';
import i18n from 'lib-app/i18n';
import API from 'lib-app/api-call';
import ConfigActions from 'actions/config-actions';
import ToggleButton from 'app-components/toggle-button';
import Button from 'core-components/button';
@ -15,7 +15,6 @@ import SubmitButton from 'core-components/submit-button';
class InstallStep4UserSystem extends React.Component {
state = {
loading: false,
form: {
'user-system-enabled': true,
'registration': true
@ -25,8 +24,8 @@ class InstallStep4UserSystem extends React.Component {
render() {
return (
<div className="install-step-4">
<Header title={i18n('STEP_TITLE', {title: i18n('USER_SYSTEM'), current: 4, total: 6})} description={i18n('STEP_4_DESCRIPTION')}/>
<Form onSubmit={this.onSubmit.bind(this)} values={this.state.form} onChange={this.onChange.bind(this)} loading={this.state.loading}>
<Header title={i18n('STEP_TITLE', {title: i18n('USER_SYSTEM'), current: 4, total: 7})} description={i18n('STEP_4_DESCRIPTION')}/>
<Form onSubmit={this.onSubmit.bind(this)} values={this.state.form} onChange={this.onChange.bind(this)}>
<FormField name="user-system-enabled" label={i18n('ENABLE_USER_SYSTEM')} decorator={ToggleButton}/>
<FormField name="registration" label={i18n('ENABLE_USER_REGISTRATION')} decorator={ToggleButton} fieldProps={{disabled: this.isDisabled()}}/>
<div className="install-step-4__buttons">
@ -53,20 +52,12 @@ class InstallStep4UserSystem extends React.Component {
}
onSubmit(form) {
this.setState({
loading: true
}, () => API.call({
path: '/system/init-settings',
data: {
'language': this.props.language,
'user-system-enabled': form['user-system-enabled'] * 1,
'registration': form['registration'] * 1
}
}).then(() => this.setState({
loading: false
}, () => history.push('/install/step-5'))).catch(() => this.setState({
loading: false
})));
this.props.dispatch(ConfigActions.updateUserSystemSettings({
'user-system-enabled': form['user-system-enabled'] * 1,
'registration': form['registration'] * 1
}));
history.push('/install/step-5');
}
isDisabled() {
@ -74,7 +65,6 @@ class InstallStep4UserSystem extends React.Component {
}
}
export default connect((store) => {
return {
language: store.config.language

View File

@ -1,4 +1,6 @@
import React from 'react';
import _ from 'lodash';
import { connect } from 'react-redux'
import history from 'lib-app/history';
import i18n from 'lib-app/i18n';
@ -15,7 +17,7 @@ class InstallStep5Settings extends React.Component {
state = {
loading: false,
smtpConnection: null, // ad a message
smtpConnection: null,
form: {},
onFormChange: (form) => this.setState({form}),
error: false,
@ -25,19 +27,20 @@ class InstallStep5Settings extends React.Component {
render() {
return (
<div className="install-step-5">
<Header title={i18n('STEP_TITLE', {title: i18n('INITIAL_SETTINGS'), current: 5, total: 7})} description={i18n('STEP_4_DESCRIPTION')}/>
<Header title={i18n('STEP_TITLE', {title: i18n('INITIAL_SETTINGS'), current: 5, total: 7})} description={i18n('STEP_5_DESCRIPTION')}/>
{this.renderMessage()}
<Form loading={this.state.loading} onSubmit={this.onSubmit.bind(this)} value={this.state.form} onChange={this.onFormChange.bind(this)}>
<FormField name="title" label={i18n('TITLE')} fieldProps={{size: 'large'}} required/>
<FormField name="allow-attachments" label={i18n('ALLOW_FILE_ATTACHMENTS')} fieldProps={{size: 'large'}} infoMessage={i18n('LEFT_EMPTY_DATABASE')}/>
<FormField name="no-reply-email" label={i18n('NO_REPLY_EMAIL')} fieldProps={{size: 'large'}} required/>
<FormField name="smtp-host" label={i18n('SMTP_SERVER')} fieldProps={{size: 'large'}} required/>
<FormField name="smtp-port" label={i18n('SMTP_PORT')} fieldProps={{size: 'small'}} required/>
<FormField name="smtp-user" label={i18n('SMTP_USER')} fieldProps={{size: 'large'}} required/>
<FormField name="allow-attachments" label={i18n('ALLOW_FILE_ATTACHMENTS')} fieldProps={{size: 'large'}}/>
<FormField name="no-reply-email" label={i18n('NO_REPLY_EMAIL')} fieldProps={{size: 'large'}}/>
<FormField name="smtp-host" label={i18n('SMTP_SERVER')} fieldProps={{size: 'large'}}/>
<FormField name="smtp-port" label={i18n('SMTP_PORT')} fieldProps={{size: 'small'}}/>
<FormField name="smtp-user" label={i18n('SMTP_USER')} fieldProps={{size: 'large'}}/>
<FormField name="smtp-password" label={i18n('SMTP_PASSWORD')} fieldProps={{size: 'large', password: true}}/>
<Button className="install-step-5__test-connection" size="medium" onClick={this.onTestSMTPClick.bind(this)}>
{i18n('TEST_SMTP_CONNECTION')}
</Button>
{this.renderMessageSMTP()}
<div className="install-step-5__buttons">
<SubmitButton className="install-step-5__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
<Button className="install-step-5__previous" size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
@ -61,6 +64,28 @@ class InstallStep5Settings extends React.Component {
return message;
}
renderMessageSMTP() {
let message = null;
if(this.state.smtpConnection !== null) {
if(this.state.smtpConnection) {
message = (
<Message className="install-step-5__message" type="success">
{i18n('SMTP_CONNECTION_SUCCESS')}
</Message>
);
} else {
message = (
<Message className="install-step-5__message" type="error">
{i18n('ERROR_SMTP_CONNECTION')}
</Message>
);
}
}
return message;
}
onTestSMTPClick(event) {
event.preventDefault();
@ -82,7 +107,11 @@ class InstallStep5Settings extends React.Component {
}, () => {
API.call({
path: '/system/init-settings',
data: form // add step 4 to form
data: _.extend({}, form, {
'language': this.props.language,
'user-system-enabled': this.props['user-system-enabled'],
'registration': this.props['registration']
})
})
.then(() => history.push('/install/step-6'))
.catch(({message}) => this.setState({
@ -94,4 +123,10 @@ class InstallStep5Settings extends React.Component {
}
}
export default InstallStep5Settings;
export default connect((store) => {
return {
'user-system-enabled': store.config['user-system-enabled'],
'registration': store.config['registration'],
language: store.config.language
};
})(InstallStep5Settings);

View File

@ -0,0 +1,15 @@
@import "../../scss/vars";
.install-step-5 {
&__previous {
margin-right: 20px;
}
&__next {
float: left;
position: absolute;
margin-left: 230px;
min-width: 70px;
}
}

View File

@ -10,7 +10,7 @@ import FormField from 'core-components/form-field';
import SubmitButton from 'core-components/submit-button';
import Message from 'core-components/message';
class InstallStep5Admin extends React.Component {
class InstallStep6Admin extends React.Component {
state = {
loading: false,
@ -20,14 +20,14 @@ class InstallStep5Admin extends React.Component {
render() {
return (
<div className="install-step-5">
<Header title={i18n('STEP_TITLE', {title: i18n('ADMIN_SETUP'), current: 5, total: 6})} description={i18n('STEP_5_DESCRIPTION')}/>
<div className="install-step-6">
<Header title={i18n('STEP_TITLE', {title: i18n('ADMIN_SETUP'), current: 6, total: 7})} description={i18n('STEP_6_DESCRIPTION')}/>
{this.renderMessage()}
<Form onSubmit={this.onSubmit.bind(this)}>
<FormField name="name" validation="NAME" label={i18n('ADMIN_NAME')} fieldProps={{size: 'large'}} required/>
<FormField name="email" validation="EMAIL" label={i18n('ADMIN_EMAIL')} fieldProps={{size: 'large'}} required/>
<FormField name="password" validation="PASSWORD" label={i18n('ADMIN_PASSWORD')} infoMessage={i18n('ADMIN_PASSWORD_DESCRIPTION')} fieldProps={{size: 'large', autoComplete: 'off'}} required/>
<div className="install-step-5__buttons">
<div className="install-step-6__buttons">
<SubmitButton className="install-step-5__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
</div>
</Form>
@ -40,7 +40,7 @@ class InstallStep5Admin extends React.Component {
if(this.state.error) {
message = (
<Message className="install-step-5_message" type="error">
<Message className="install-step-6_message" type="error">
{i18n('ERROR_UPDATING_SETTINGS')}: {this.state.errorMessage}
</Message>
);
@ -57,7 +57,7 @@ class InstallStep5Admin extends React.Component {
path: '/system/init-admin',
data: form
})
.then(() => history.push('/install/step-6'))
.then(() => history.push('/install/step-7'))
.catch(({message}) => this.setState({
loading: false,
error: true,
@ -67,4 +67,4 @@ class InstallStep5Admin extends React.Component {
}
}
export default InstallStep5Admin;
export default InstallStep6Admin;

View File

@ -1,6 +1,6 @@
@import "../../scss/vars";
.install-step-5 {
.install-step-6 {
&__message {
margin-bottom: 20px;

View File

@ -7,7 +7,7 @@ import i18n from 'lib-app/i18n';
import Header from 'core-components/header';
import Message from 'core-components/message';
class InstallStep6Completed extends React.Component {
class InstallStep7Completed extends React.Component {
componentDidMount() {
store.dispatch(ConfigActions.init());
@ -19,8 +19,8 @@ class InstallStep6Completed extends React.Component {
render() {
return (
<div className="install-step-6">
<Header title={i18n('STEP_TITLE', {title: i18n('COMPLETED'), current: 6, total: 6})} description={i18n('STEP_6_DESCRIPTION')}/>
<div className="install-step-7">
<Header title={i18n('STEP_TITLE', {title: i18n('COMPLETED'), current: 7, total: 7})} description={i18n('STEP_7_DESCRIPTION')}/>
<Message title={i18n('INSTALLATION_COMPLETED')} type="success">
{i18n('INSTALLATION_COMPLETED_DESCRIPTION')}
</Message>
@ -29,4 +29,4 @@ class InstallStep6Completed extends React.Component {
}
}
export default InstallStep6Completed;
export default InstallStep7Completed;

View File

@ -224,6 +224,16 @@ module.exports = [
};
}
},
{
path: '/system/test-smtp',
time: 100,
response: function () {
return {
status: 'success',
data: {}
};
}
},
{
path: '/system/get-mail-templates',
time: 100,

View File

@ -239,8 +239,9 @@ export default {
'STEP_2_DESCRIPTION': 'Here are listed the requirements for running OpenSupports. Please make sure that all requirements are satisfied.',
'STEP_3_DESCRIPTION': 'Please fill the MySQL database configuration.',
'STEP_4_DESCRIPTION': 'Please select your user system preferences.',
'STEP_5_DESCRIPTION': 'Please configure the administrator account.',
'STEP_6_DESCRIPTION': 'Installation is completed.',
'STEP_5_DESCRIPTION': 'Please select your system preferences.',
'STEP_6_DESCRIPTION': 'Please configure the administrator account.',
'STEP_7_DESCRIPTION': 'Installation is completed.',
//VIEW DESCRIPTIONS
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',

View File

@ -19,7 +19,8 @@ class ConfigReducer extends Reducer {
'CHANGE_LANGUAGE': this.onLanguageChange,
'INIT_CONFIGS_FULFILLED': this.onInitConfigs,
'CHECK_INSTALLATION_FULFILLED': this.onInstallationChecked,
'UPDATE_DATA_FULFILLED': this.onInitConfigs
'UPDATE_DATA_FULFILLED': this.onInitConfigs,
'UPDATE_USER_SYSTEM_SETTINGS': this.onUserSystemSettingsChange
};
}
@ -48,6 +49,13 @@ class ConfigReducer extends Reducer {
});
}
onUserSystemSettingsChange(state, payload) {
return _.extend({}, state, {
'user-system-enabled': !!(payload['user-system-enabled'] * 1),
'allow-attachments': !!(payload['allow-attachments'] * 1)
});
}
onInstallationChecked(state, payload) {
return _.extend({}, state, {
installedDone: true,

View File

@ -25,6 +25,7 @@ require_once 'system/delete-all-users.php';
require_once 'system/csv-import.php';
require_once 'system/backup-database.php';
require_once 'system/download.php';
require_once 'system/test-smtp.php';
$systemControllerGroup = new ControllerGroup();
$systemControllerGroup->setGroupPath('/system');
@ -55,5 +56,6 @@ $systemControllerGroup->addController(new DownloadController);
$systemControllerGroup->addController(new CSVImportController);
$systemControllerGroup->addController(new DisableUserSystemController);
$systemControllerGroup->addController(new EnableUserSystemController);
$systemControllerGroup->addController(new TestSMTPController);
$systemControllerGroup->finalize();

View File

@ -0,0 +1,54 @@
<?php
use Respect\Validation\Validator as DataValidator;
/**
* @api {post} /system/test-smtp Test SMTP Connection
* @apiVersion 4.1.0
*
* @apiName Test SMTP Connection
*
* @apiGroup System
*
* @apiDescription Test if the given values connect correctly to a SMTP server.
*
* @apiPermission any
*
* @apiParam {String} smtp-host Host of the SMTP server.
* @apiParam {String} smtp-port Port of the SMTP server.
* @apiParam {String} smtp-user User for the SMTP server.
* @apiParam {String} smtp-pass Password for the SMTP server.
*
* @apiUse SMTP_CONNECTION
*
* @apiSuccess {Object} data Empty object
*
*/
class TestSMTPController extends Controller {
const PATH = '/test-smtp';
const METHOD = 'POST';
public function validations() {
return [
'permission' => 'any',
'requestData' => []
];
}
public function handler() {
$mailSender = MailSender::getInstance();
$mailSender->setConnectionSettings(
Controller::request('smtp-host'),
Controller::request('smtp-port'),
Controller::request('smtp-user'),
Controller::request('smtp-pass'),
Controller::request('no-reply-email')
);
if($mailSender->isConnected()) {
Response::respondSuccess();
} else {
throw new Exception(ERRORS::SMTP_CONNECTION);
}
}
}

View File

@ -171,6 +171,10 @@
* @apiDefine DATABASE_CREATION
* @apiError {String} DATABASE_CREATION It's a database creation error.
*/
/**
* @apiDefine SMTP_CONNECTION
* @apiError {String} SMTP_CONNECTION Could not connect with SMTP server.
*/
class ERRORS {
const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS';
@ -216,4 +220,5 @@ class ERRORS {
const INVALID_FILE = 'INVALID_FILE';
const DATABASE_CONNECTION = 'DATABASE_CONNECTION';
const DATABASE_CREATION = 'DATABASE_CREATION';
const SMTP_CONNECTION = 'SMTP_CONNECTION';
}

View File

@ -14,13 +14,23 @@ class MailSender {
}
private function __construct() {
$this->mailOptions['from'] = Setting::getSetting('no-reply-email')->value;
$this->setConnectionSettings(
Setting::getSetting('smtp-host')->getValue(),
Setting::getSetting('smtp-port')->getValue(),
Setting::getSetting('smtp-user')->getValue(),
Setting::getSetting('smtp-pass')->getValue(),
Setting::getSetting('no-reply-email')->getValue()
);
}
public function setConnectionSettings($host, $port, $user, $pass, $noReplyEmail) {
$this->mailOptions['from'] = $noReplyEmail;
$this->mailOptions['fromName'] = 'OpenSupports';
$this->mailOptions['smtp-host'] = Setting::getSetting('smtp-host')->value;
$this->mailOptions['smtp-port'] = Setting::getSetting('smtp-port')->value;
$this->mailOptions['smtp-user'] = Setting::getSetting('smtp-user')->value;
$this->mailOptions['smtp-pass'] = Setting::getSetting('smtp-pass')->value;
$this->mailOptions['smtp-host'] = $host;
$this->mailOptions['smtp-port'] = $port;
$this->mailOptions['smtp-user'] = $user;
$this->mailOptions['smtp-pass'] = $pass;
}
public function setTemplate($type, $config) {