Browse Source

Merge branch 'imprv/can-update-SES-setting-in-the-mail-setting-form' into imprv/show-mailer-status

itizawa 5 years ago
parent
commit
28321d9f3a

+ 4 - 0
resource/locales/en_US/admin/admin.json

@@ -29,6 +29,9 @@
     "mail_settings": "E-mail Settings",
     "mailer_is_not_set_up": "E-mail setting is not set up.",
     "from_e-mail_address": "From e-mail address",
+    "transmission_method":"Transmission Method",
+    "smtp_label":"SMTP",
+    "ses_label":"SES(AWS)",
     "send_test_email": "Send a test-email",
     "success_to_send_test_email": "Success to send a test-email",
     "smtp_settings": "SMTP settings",
@@ -36,6 +39,7 @@
     "port": "Port",
     "user": "User",
     "ses_settings":"SES settings",
+    "test_connection": "Test connection to mail",
     "aws_settings": "AWS settings",
     "aws_access": "This is for AWS settings. If you complete AWS settings, file upload function, profile picture function etc will be enabled.",
     "change_setting": "Caution:if you change this setting not completed, you will not be able to access files you have uploaded so far.",

+ 4 - 0
resource/locales/ja_JP/admin/admin.json

@@ -29,6 +29,9 @@
     "mail_settings": "メールの設定",
     "mailer_is_not_set_up": "メール設定がセットアップされていません。",
     "from_e-mail_address": "Fromアドレス",
+    "transmission_method":"送信方法",
+    "smtp_label":"SMTP",
+    "ses_label":"SES(AWS)",
     "send_test_email": "テストメールを送信",
     "success_to_send_test_email": "テストメールを送信しました。",
     "smtp_settings": "SMTP設定",
@@ -36,6 +39,7 @@
     "port": "ポート",
     "user": "ユーザー",
     "ses_settings":"SES設定",
+    "test_connection": "接続テスト",
     "aws_settings": "AWS設定",
     "aws_access": "AWS にアクセスするための設定を行います。AWS の設定を完了させると、ファイルアップロード機能、プロフィール写真機能などが有効になります。",
     "change_setting": "この設定を途中で変更すると、これまでにアップロードしたファイル等へのアクセスができなくなりますのでご注意下さい。",

+ 5 - 1
resource/locales/zh_CN/admin/admin.json

@@ -28,7 +28,10 @@
 		"update": "更新",
 		"mail_settings": "邮件设置",
     "mailer_is_not_set_up": "E-mail setting is not set up.",
-    "from_e-mail_address": "From e-mail address",
+    "transmission_method":"Transmission Method",
+    "smtp_label":"SMTP",
+    "ses_label":"SES(AWS)",
+		"from_e-mail_address": "From e-mail address",
     "send_test_email": "Send a test-email",
     "success_to_send_test_email": "Success to send a test-email",
     "smtp_settings": "SMTP 设置",
@@ -36,6 +39,7 @@
 		"port": "端口号",
 		"user": "用户名",
     "ses_settings":"SES设置",
+    "test_connection": "Test connection to mail",
 		"aws_settings": "AWS设置",
 		"aws_access": "这是用于AWS设置的。如果您完成了AWS设置,文件上传功能,个人资料图片功能等将被启用。",
 		"": "如果您没有SMTP设置,电子邮件将通过SES发送。您需要从电子邮件地址和生产设置进行验证。",

+ 76 - 90
src/client/js/components/Admin/App/MailSetting.jsx

@@ -1,119 +1,105 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
-import {
-  TabContent, TabPane, Nav, NavItem, NavLink,
-} from 'reactstrap';
-import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import AppContainer from '../../../services/AppContainer';
 import AdminAppContainer from '../../../services/AdminAppContainer';
 import SmtpSetting from './SmtpSetting';
 import SesSetting from './SesSetting';
 
-const logger = loggerFactory('growi:mailSettings');
-
-class MailSetting extends React.Component {
-
-  constructor(props) {
-    super(props);
 
-    this.state = {
-      activeTab: 'smtp-setting',
-      // Prevent unnecessary rendering
-      activeComponents: new Set(['smtp-setting']),
-    };
+function MailSetting(props) {
+  const { t, adminAppContainer } = props;
 
-    this.emailInput = React.createRef();
-    this.hostInput = React.createRef();
-    this.portInput = React.createRef();
-    this.userInput = React.createRef();
-    this.passwordInput = React.createRef();
+  const transmissionMethods = ['smtp', 'ses'];
 
-    this.submitFromAdressHandler = this.submitFromAdressHandler.bind(this);
-  }
+  async function submitHandler() {
+    const { t } = props;
 
-  toggleActiveTab(activeTab) {
-    this.setState({
-      activeTab, activeComponents: this.state.activeComponents.add(activeTab),
-    });
+    try {
+      await adminAppContainer.updateMailSettingHandler();
+      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.ses_settings') }));
+    }
+    catch (err) {
+      toastError(err);
+    }
   }
 
-  async submitFromAdressHandler() {
-    const { t, adminAppContainer } = this.props;
-
+  async function sendTestEmailHandler() {
+    const { adminAppContainer } = props;
     try {
-      await adminAppContainer.updateFromAdressHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.mail_settings') }));
+      await adminAppContainer.sendTestEmail();
+      toastSuccess(t('admin:app_setting.success_to_send_test_email'));
     }
     catch (err) {
       toastError(err);
-      logger.error(err);
     }
   }
 
-  render() {
-    const { t, adminAppContainer } = this.props;
-    const { activeTab, activeComponents } = this.state;
-
-    return (
-      <React.Fragment>
-        {!adminAppContainer.state.isMailerSetup && (
-          <p className="alert alert-danger"><i className="icon-exclamation"></i> {t('admin:app_setting.mailer_is_not_set_up')}</p>
-        )}
-        <div className="row form-group mb-5">
-          <label className="col-md-3 col-form-label text-left">{t('admin:app_setting.from_e-mail_address')}</label>
-          <div className="col-md-6">
-            <input
-              className="form-control"
-              type="text"
-              ref={this.emailInput}
-              placeholder={`${t('eg')} mail@growi.org`}
-              defaultValue={adminAppContainer.state.fromAddress || ''}
-              onChange={(e) => { adminAppContainer.changeFromAddress(e.target.value) }}
-            />
-          </div>
+
+  return (
+    <React.Fragment>
+      {!adminAppContainer.state.isMailerSetup && (
+        <div className="alert alert-danger"><i className="icon-exclamation"></i> {t('admin:app_setting.mailer_is_not_set_up')}</div>
+      )}
+      <div className="row form-group mb-5">
+        <label className="col-md-3 col-form-label text-right">{t('admin:app_setting.from_e-mail_address')}</label>
+        <div className="col-md-6">
+          <input
+            className="form-control"
+            type="text"
+            placeholder={`${t('eg')} mail@growi.org`}
+            defaultValue={adminAppContainer.state.fromAddress || ''}
+            onChange={(e) => { adminAppContainer.changeFromAddress(e.target.value) }}
+          />
         </div>
-        <div className="row my-3">
-          <div className="mx-auto">
-            <button type="button" className="btn btn-primary" onClick={this.submitFromAdressHandler}>{ t('Update') }</button>
-          </div>
+      </div>
+
+      <div className="row form-group mb-5">
+        <label className="text-left text-md-right col-md-3 col-form-label">
+          {t('admin:app_setting.transmission_method')}
+        </label>
+        <div className="col-md-6">
+          {transmissionMethods.map((method) => {
+              return (
+                <div key={method} className="custom-control custom-radio custom-control-inline">
+                  <input
+                    type="radio"
+                    className="custom-control-input"
+                    name="transmission-method"
+                    id={`transmission-nethod-radio-${method}`}
+                    checked={adminAppContainer.state.transmissionMethod === method}
+                    onChange={(e) => {
+                    adminAppContainer.changeTransmissionMethod(method);
+                  }}
+                  />
+                  <label className="custom-control-label" htmlFor={`transmission-nethod-radio-${method}`}>{t(`admin:app_setting.${method}_label`)}</label>
+                </div>
+              );
+            })}
         </div>
-
-        <Nav tabs>
-          <NavItem>
-            <NavLink
-              className={`${activeTab === 'smtp-setting' && 'active'} `}
-              onClick={() => { this.toggleActiveTab('smtp-setting') }}
-              href="#smtp-setting"
-            >
-              SMTP
-            </NavLink>
-          </NavItem>
-          <NavItem>
-            <NavLink
-              className={`${activeTab === 'ses-setting' && 'active'} `}
-              onClick={() => { this.toggleActiveTab('ses-setting') }}
-              href="#ses-setting"
-            >
-              SES(AWS)
-            </NavLink>
-          </NavItem>
-        </Nav>
-        <TabContent activeTab={activeTab}>
-          <TabPane tabId="smtp-setting">
-            {activeComponents.has('smtp-setting') && <SmtpSetting />}
-          </TabPane>
-          <TabPane tabId="ses-setting">
-            {activeComponents.has('ses-setting') && <SesSetting />}
-          </TabPane>
-        </TabContent>
-      </React.Fragment>
-    );
-  }
+      </div>
+
+      {adminAppContainer.state.transmissionMethod === 'smtp' && <SmtpSetting />}
+      {adminAppContainer.state.transmissionMethod === 'ses' && <SesSetting />}
+
+      <div className="row my-3">
+        <div className="mx-auto">
+          <button type="button" className="btn btn-primary" onClick={submitHandler} disabled={adminAppContainer.state.retrieveError != null}>
+            { t('Update') }
+          </button>
+          {adminAppContainer.state.transmissionMethod === 'smtp' && (
+          <button type="button" className="btn btn-secondary ml-4" onClick={sendTestEmailHandler}>
+            {t('admin:app_setting.send_test_email')}
+          </button>
+          )}
+        </div>
+      </div>
+    </React.Fragment>
+  );
 
 }
 

+ 1 - 28
src/client/js/components/Admin/App/SesSetting.jsx

@@ -2,39 +2,20 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
-import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { withLoadingSppiner } from '../../SuspenseUtils';
 
 
 import AppContainer from '../../../services/AppContainer';
 import AdminAppContainer from '../../../services/AdminAppContainer';
 
-const logger = loggerFactory('growi:smtpSettings');
-
-
 function SmtpSetting(props) {
-  const { adminAppContainer, t } = props;
-
-  async function submitHandler() {
-    const { t } = props;
-
-    try {
-      await adminAppContainer.updateSesSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.ses_settings') }));
-    }
-    catch (err) {
-      toastError(err);
-      logger.error(err);
-    }
-  }
+  const { adminAppContainer } = props;
 
   return (
     <React.Fragment>
       <div id="mail-smtp" className="tab-pane active mt-5">
-        <label className="col-md-3 col-form-label text-left mb-3">{t('admin:app_setting.ses_settings')}</label>
 
         <div className="row form-group">
           <label className="text-left text-md-right col-md-3 col-form-label">
@@ -67,14 +48,6 @@ function SmtpSetting(props) {
             />
           </div>
         </div>
-
-        <div className="row my-3">
-          <div className="mx-auto">
-            <button type="button" className="btn btn-primary" onClick={submitHandler} disabled={adminAppContainer.state.retrieveError != null}>
-              { t('Update') }
-            </button>
-          </div>
-        </div>
       </div>
 
     </React.Fragment>

+ 24 - 54
src/client/js/components/Admin/App/SmtpSetting.jsx

@@ -2,54 +2,25 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
-import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { withLoadingSppiner } from '../../SuspenseUtils';
 
 
 import AppContainer from '../../../services/AppContainer';
 import AdminAppContainer from '../../../services/AdminAppContainer';
 
-const logger = loggerFactory('growi:smtpSettings');
-
-
 function SmtpSetting(props) {
   const { adminAppContainer, t } = props;
 
-  async function submitHandler() {
-    const { t, adminAppContainer } = props;
-
-    try {
-      await adminAppContainer.updateSmtpSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.smtp_settings') }));
-    }
-    catch (err) {
-      toastError(err);
-      logger.error(err);
-    }
-  }
-
-  async function sendTestEmailHandler() {
-    const { adminAppContainer } = props;
-    try {
-      await adminAppContainer.sendTestEmail();
-      toastSuccess(t('admin:app_setting.success_to_send_test_email'));
-    }
-    catch (err) {
-      toastError(err);
-      logger.error(err);
-    }
-  }
-
   return (
     <React.Fragment>
       <div id="mail-smtp" className="tab-pane active mt-5">
-        <div className="row form-group mb-5">
-          <label className="col-md-3 col-form-label text-left">{t('admin:app_setting.smtp_settings')}</label>
-          <div className="col-md-4">
-            <label>{t('admin:app_setting.host')}</label>
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
+            {t('admin:app_setting.host')}
+          </label>
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -57,8 +28,13 @@ function SmtpSetting(props) {
               onChange={(e) => { adminAppContainer.changeSmtpHost(e.target.value) }}
             />
           </div>
-          <div className="col-md-2">
-            <label>{t('admin:app_setting.port')}</label>
+        </div>
+
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
+            {t('admin:app_setting.port')}
+          </label>
+          <div className="col-md-6">
             <input
               className="form-control"
               defaultValue={adminAppContainer.state.smtpPort || ''}
@@ -67,9 +43,11 @@ function SmtpSetting(props) {
           </div>
         </div>
 
-        <div className="row form-group mb-5">
-          <div className="col-md-3 offset-md-3">
-            <label>{t('admin:app_setting.user')}</label>
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
+            {t('admin:app_setting.user')}
+          </label>
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -77,8 +55,13 @@ function SmtpSetting(props) {
               onChange={(e) => { adminAppContainer.changeSmtpUser(e.target.value) }}
             />
           </div>
-          <div className="col-md-3">
-            <label>{t('Password')}</label>
+        </div>
+
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
+            {t('Password')}
+          </label>
+          <div className="col-md-6">
             <input
               className="form-control"
               type="password"
@@ -87,20 +70,7 @@ function SmtpSetting(props) {
             />
           </div>
         </div>
-
-        <div className="row my-3">
-          <div className="mx-auto">
-            <button type="button" className="btn btn-primary" onClick={submitHandler} disabled={adminAppContainer.state.retrieveError != null}>
-              { t('Update') }
-            </button>
-            <button type="button" className="btn btn-secondary ml-4" onClick={sendTestEmailHandler}>
-              {t('admin:app_setting.send_test_email')}
-            </button>
-          </div>
-        </div>
-
       </div>
-
     </React.Fragment>
   );
 }

+ 21 - 9
src/client/js/services/AdminAppContainer.js

@@ -25,6 +25,7 @@ export default class AdminAppContainer extends Container {
       isSetSiteUrl: true,
       isMailerSetup: false,
       fromAddress: '',
+      transmissionMethod: '',
       smtpHost: '',
       smtpPort: '',
       smtpUser: '',
@@ -67,6 +68,7 @@ export default class AdminAppContainer extends Container {
       isMailerSetup: true,
       // isMailerSetup: appSettingsParams.isMailerSetup,
       fromAddress: appSettingsParams.fromAddress,
+      transmissionMethod: appSettingsParams.transmissionMethod,
       smtpHost: appSettingsParams.smtpHost,
       smtpPort: appSettingsParams.smtpPort,
       smtpUser: appSettingsParams.smtpUser,
@@ -125,6 +127,13 @@ export default class AdminAppContainer extends Container {
     this.setState({ fromAddress });
   }
 
+  /**
+   * Change from transmission method
+   */
+  changeTransmissionMethod(transmissionMethod) {
+    this.setState({ transmissionMethod });
+  }
+
   /**
    * Change smtp host
    */
@@ -240,16 +249,15 @@ export default class AdminAppContainer extends Container {
   }
 
   /**
-   * Update from adress
+   * Update mail setting
    * @memberOf AdminAppContainer
    * @return {Array} Appearance
    */
-  async updateFromAdressHandler() {
-    const response = await this.appContainer.apiv3.put('/app-settings/from-address', {
-      fromAddress: this.state.fromAddress,
-    });
-    const { mailSettingParams } = response.data;
-    return mailSettingParams;
+  updateMailSettingHandler() {
+    if (this.state.transmissionMethod === 'smtp') {
+      return this.updateSmtpSetting();
+    }
+    return this.updateSesSetting();
   }
 
   /**
@@ -257,8 +265,10 @@ export default class AdminAppContainer extends Container {
    * @memberOf AdminAppContainer
    * @return {Array} Appearance
    */
-  async updateSmtpSettingHandler() {
+  async updateSmtpSetting() {
     const response = await this.appContainer.apiv3.put('/app-settings/smtp-setting', {
+      fromAddress: this.state.fromAddress,
+      transmissionMethod: this.state.transmissionMethod,
       smtpHost: this.state.smtpHost,
       smtpPort: this.state.smtpPort,
       smtpUser: this.state.smtpUser,
@@ -276,8 +286,10 @@ export default class AdminAppContainer extends Container {
    * @memberOf AdminAppContainer
    * @return {Array} Appearance
    */
-  async updateSesSettingHandler() {
+  async updateSesSetting() {
     const response = await this.appContainer.apiv3.put('/app-settings/ses-setting', {
+      fromAddress: this.state.fromAddress,
+      transmissionMethod: this.state.transmissionMethod,
       sesAccessKeyId: this.state.sesAccessKeyId,
       sesSecretAccessKey: this.state.sesSecretAccessKey,
     });

+ 20 - 45
src/server/routes/apiv3/app-settings.js

@@ -50,13 +50,16 @@ const ErrorV3 = require('../../models/vo/error-apiv3');
  *          envSiteUrl:
  *            type: string
  *            description: environment variable 'APP_SITE_URL'
- *      FromAddress:
+ *      MailSetting:
  *        description: MailSettingParams
  *        type: object
  *        properties:
  *          fromAddress:
  *            type: string
  *            description: e-mail address used as from address of mail which sent from GROWI app
+ *          transmissionMethod:
+ *            type: string
+ *            description: transmission method
  *      SmtpSettingParams:
  *        description: SmtpSettingParams
  *        type: object
@@ -129,8 +132,9 @@ module.exports = (crowi) => {
     siteUrlSetting: [
       body('siteUrl').trim().matches(/^(https?:\/\/[^/]+|)$/).isURL({ require_tld: false }),
     ],
-    fromAddress: [
+    mailSetting: [
       body('fromAddress').trim().if(value => value !== '').isEmail(),
+      body('transmissionMethod').isIn(['smtp', 'ses']),
     ],
     smtpSetting: [
       body('smtpHost').trim(),
@@ -183,6 +187,7 @@ module.exports = (crowi) => {
       siteUrl: crowi.configManager.getConfig('crowi', 'app:siteUrl'),
       envSiteUrl: crowi.configManager.getConfigFromEnvVars('crowi', 'app:siteUrl'),
       fromAddress: crowi.configManager.getConfig('crowi', 'mail:from'),
+      transmissionMethod: crowi.configManager.getConfig('crowi', 'mail:transmissionMethod'),
       smtpHost: crowi.configManager.getConfig('crowi', 'mail:smtpHost'),
       smtpPort: crowi.configManager.getConfig('crowi', 'mail:smtpPort'),
       smtpUser: crowi.configManager.getConfig('crowi', 'mail:smtpUser'),
@@ -382,43 +387,6 @@ module.exports = (crowi) => {
     };
   };
 
-  /**
-   * @swagger
-   *
-   *    /app-settings/from-address:
-   *      put:
-   *        tags: [AppSettings]
-   *        operationId: updateAppSettingFromAddress
-   *        summary: /app-settings/from-address
-   *        description: Update from address
-   *        requestBody:
-   *          required: true
-   *          content:
-   *            application/json:
-   *              schema:
-   *                $ref: '#/components/schemas/FromAddress'
-   *        responses:
-   *          200:
-   *            description: Succeeded to update from adress
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  $ref: '#/components/schemas/FromAddress'
-   */
-  router.put('/from-address', loginRequiredStrictly, adminRequired, csrf, validator.fromAddress, apiV3FormValidator, async(req, res) => {
-
-    try {
-      const mailSettingParams = await updateMailSettinConfig({ 'mail:from': req.body.fromAddress });
-      return res.apiv3({ mailSettingParams });
-    }
-    catch (err) {
-      const msg = 'Error occurred in updating from adress';
-      logger.error('Error', err);
-      return res.apiv3Err(new ErrorV3(msg, 'update-from-adress-failed'));
-    }
-
-  });
-
   /**
    * @swagger
    *
@@ -444,6 +412,8 @@ module.exports = (crowi) => {
    */
   router.put('/smtp-setting', loginRequiredStrictly, adminRequired, csrf, validator.smtpSetting, apiV3FormValidator, async(req, res) => {
     const requestMailSettingParams = {
+      'mail:from': req.body.fromAddress,
+      'mail:transmissionMethod': req.body.transmissionMethod,
       'mail:smtpHost': req.body.smtpHost,
       'mail:smtpPort': req.body.smtpPort,
       'mail:smtpUser': req.body.smtpUser,
@@ -511,21 +481,29 @@ module.exports = (crowi) => {
    *                  $ref: '#/components/schemas/SesSettingParams'
    */
   router.put('/ses-setting', loginRequiredStrictly, adminRequired, csrf, validator.sesSetting, apiV3FormValidator, async(req, res) => {
+    const { mailService } = crowi;
 
     const requestSesSettingParams = {
+      'mail:from': req.body.fromAddress,
+      'mail:transmissionMethod': req.body.transmissionMethod,
       'mail:sesAccessKeyId': req.body.sesAccessKeyId,
       'mail:sesSecretAccessKey': req.body.sesSecretAccessKey,
     };
 
+    let mailSettingParams;
     try {
-      const mailSettingParams = await updateMailSettinConfig(requestSesSettingParams);
-      return res.apiv3({ mailSettingParams });
+      mailSettingParams = await updateMailSettinConfig(requestSesSettingParams);
     }
     catch (err) {
       const msg = 'Error occurred in updating ses setting';
       logger.error('Error', err);
       return res.apiv3Err(new ErrorV3(msg, 'update-ses-setting-failed'));
     }
+
+    await mailService.initialize();
+    mailService.publishUpdatedMessage();
+
+    return res.apiv3({ mailSettingParams });
   });
 
   /**
@@ -561,14 +539,11 @@ module.exports = (crowi) => {
     };
 
     try {
-      const { configManager, mailService } = crowi;
+      const { configManager } = crowi;
 
       // update config without publishing S2sMessage
       await configManager.updateConfigsInTheSameNamespace('crowi', requestAwsSettingParams, true);
 
-      await mailService.initialize();
-      mailService.publishUpdatedMessage();
-
       const awsSettingParams = {
         region: crowi.configManager.getConfig('crowi', 'aws:region'),
         customEndpoint: crowi.configManager.getConfig('crowi', 'aws:customEndpoint'),