Bläddra i källkod

Merge commit 'cf166aa32f00c648b7629f2a99acc45eb3a3ddb0' into reactify-admin/app-settings-stocks

yusuketk 6 år sedan
förälder
incheckning
5498d6563f

+ 1 - 1
resource/locales/en-US/translation.json

@@ -411,7 +411,7 @@
     "siteurl_help": "Site full URL beginning from <code>http://</code> or <code>https://</code>.",
     "Confidential name": "Confidential name",
     "Default Language for new users": "Default Language for new users",
-    "ex): internal use only":"ex): internal use only",
+    "ex) internal use only":"ex): internal use only",
     "File Uploading": "File Uploading",
     "enable_files_except_image": "Enable file upload other than image files.",
     "attach_enable": "You can attach files other than image files if you enable this option.",

+ 1 - 1
resource/locales/ja/translation.json

@@ -410,7 +410,7 @@
     "siteurl_help": "<code>http://</code> または <code>https://</code> から始まるサイトのURL",
     "Confidential name": "コンフィデンシャル表示",
     "Default Language for new users": "新規ユーザーのデフォルト設定言語",
-    "ex): internal use only": "例: 社外秘",
+    "ex) internal use only": "例: 社外秘",
     "File Uploading": "ファイルアップロード",
     "enable_files_except_image": "画像以外のファイルアップロードを許可",
     "attach_enable": "許可をしている場合、画像以外のファイルをページに添付可能になります。",

+ 106 - 14
src/client/js/components/Admin/App/AppSetting.jsx

@@ -1,20 +1,87 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
+import loggerFactory from '@alias/logger';
 
 import { createSubscribedElement } from '../../UnstatedUtils';
+import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
 
+const logger = loggerFactory('growi:appSettings');
+
 class AppSetting extends React.Component {
 
   constructor(props) {
     super(props);
 
     this.state = {
+      title: '',
+      confidential: '',
+      globalLang: 'en-US',
+      fileUpload: false,
     };
+
+    this.submitHandler = this.submitHandler.bind(this);
+    this.inputTitleChangeHandler = this.inputTitleChangeHandler.bind(this);
+    this.inputConfidentialChangeHandler = this.inputConfidentialChangeHandler.bind(this);
+    this.inputGlobalLangChangeHandler = this.inputGlobalLangChangeHandler.bind(this);
+    this.inputFileUploadChangeHandler = this.inputFileUploadChangeHandler.bind(this);
+  }
+
+  async componentDidMount() {
+    try {
+      const response = await this.props.appContainer.apiv3.get('/app-settings/app-setting');
+      const appSettingParams = response.data.appSettingParams;
+
+      this.setState({
+        title: appSettingParams.title || '',
+        confidential: appSettingParams.confidential || '',
+        globalLang: appSettingParams.globalLang || 'en-US',
+        fileUpload: appSettingParams.fileUpload || false,
+      });
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  }
+
+  async submitHandler() {
+    const { t } = this.props;
+
+    const params = {
+      title: this.state.title,
+      confidential: this.state.confidential,
+      globalLang: this.state.globalLang,
+      fileUpload: this.state.fileUpload,
+    };
+
+    try {
+      await this.props.appContainer.apiv3.put('/app-settings/app-setting', params);
+      toastSuccess(t('Updated app setting'));
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  }
+
+  inputTitleChangeHandler(event) {
+    this.setState({ title: event.target.value });
+  }
+
+  inputConfidentialChangeHandler(event) {
+    this.setState({ confidential: event.target.value });
   }
 
+  inputGlobalLangChangeHandler(event) {
+    this.setState({ globalLang: event.target.value });
+  }
+
+  inputFileUploadChangeHandler(event) {
+    this.setState({ fileUpload: event.target.checked });
+  }
 
   render() {
     const { t } = this.props;
@@ -24,16 +91,15 @@ class AppSetting extends React.Component {
         <div className="row">
           <div className="col-md-12">
             <div className="form-group">
-              <label htmlFor="settingForm[app:title]" className="col-xs-3 control-label">
-                {t('app_setting.Site Name')}
-              </label>
+              <label className="col-xs-3 control-label">{t('app_setting.Site Name')}</label>
               <div className="col-xs-6">
                 <input
                   className="form-control"
                   id="settingForm[app:title]"
                   type="text"
-                  name="settingForm[app:title]"
-                  value="{{ getConfig('crowi', 'app:title') | default('') }}"
+                  name="title"
+                  value={this.state.title}
+                  onChange={this.inputTitleChangeHandler}
                   placeholder="GROWI"
                 />
                 <p className="help-block">{t('app_setting.sitename_change')}</p>
@@ -45,17 +111,16 @@ class AppSetting extends React.Component {
         <div className="row">
           <div className="col-md-12">
             <div className="form-group">
-              <label htmlFor="settingForm[app:confidential]" className="col-xs-3 control-label">
-                {t('app_setting.Confidential name')}
-              </label>
+              <label className="col-xs-3 control-label">{t('app_setting.Confidential name')}</label>
               <div className="col-xs-6">
                 <input
                   className="form-control"
                   id="settingForm[app:confidential]"
                   type="text"
-                  name="settingForm[app:confidential]"
-                  value="{{ getConfig('crowi', 'app:confidential') | default('') }}"
-                  placeholder="{{ t('app_setting. ex&rpar;: internal use only') }}"
+                  name="confidential"
+                  value={this.state.confidential}
+                  onChange={this.inputConfidentialChangeHandler}
+                  placeholder={t('app_setting.ex) internal use only')}
                 />
                 <p className="help-block">{t('app_setting.header_content')}</p>
               </div>
@@ -63,13 +128,41 @@ class AppSetting extends React.Component {
           </div>
         </div>
 
+        <div className="form-group">
+          <label className="col-xs-3 control-label">{t('app_setting.Default Language for new users')}</label>
+          <div className="col-xs-6">
+            <div className="radio radio-primary radio-inline">
+              <input
+                type="radio"
+                id="radioLangEn"
+                name="globalLang"
+                value="en-US"
+                checked={this.state.globalLang === 'en-US'}
+                onClick={this.inputGlobalLangChangeHandler}
+              />
+              <label htmlFor="radioLangEn">{t('English')}</label>
+            </div>
+            <div className="radio radio-primary radio-inline">
+              <input
+                type="radio"
+                id="radioLangJa"
+                name="globalLang"
+                value="ja"
+                checked={this.state.globalLang === 'ja'}
+                onClick={this.inputGlobalLangChangeHandler}
+              />
+              <label htmlFor="radioLangJa">{t('Japanese')}</label>
+            </div>
+          </div>
+        </div>
+
         <div className="row">
           <div className="col-md-12">
             <div className="form-group">
               <label className="col-xs-3 control-label">{t('app_setting.File Uploading')}</label>
               <div className="col-xs-6">
                 <div className="checkbox checkbox-info">
-                  <input type="checkbox" id="cbFileUpload" name="settingForm[app:fileUpload]" value="1" />
+                  <input type="checkbox" id="cbFileUpload" name="fileUpload" checked={this.state.fileUpload} onChange={this.inputFileUploadChangeHandler} />
                   <label htmlFor="cbFileUpload">{t('app_setting.enable_files_except_image')}</label>
                 </div>
 
@@ -87,8 +180,7 @@ class AppSetting extends React.Component {
           <div className="col-md-12">
             <div className="form-group">
               <div className="col-xs-offset-3 col-xs-6">
-                <input type="hidden" name="_csrf" value="{{ csrf() }}" />
-                <button type="submit" className="btn btn-primary">
+                <button type="submit" className="btn btn-primary" onClick={this.submitHandler}>
                   {t('app_setting.Update')}
                 </button>
               </div>

+ 164 - 0
src/server/routes/apiv3/app-settings.js

@@ -0,0 +1,164 @@
+const loggerFactory = require('@alias/logger');
+
+const logger = loggerFactory('growi:routes:apiv3:app-settings');
+
+const express = require('express');
+
+const router = express.Router();
+
+const { body } = require('express-validator/check');
+
+const ErrorV3 = require('../../models/vo/error-apiv3');
+
+const validator = {
+  appSetting: [
+    body('title').trim(),
+    body('confidential'),
+    body('globalLang').isIn(['en-US', 'ja']),
+    body('fileUpload').isBoolean(),
+  ],
+};
+
+
+/**
+ * @swagger
+ *  tags:
+ *    name: AppSettings
+ */
+
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      AppSettingParams:
+ *        type: object
+ *        properties:
+ *          title:
+ *            type: String
+ *            description: site name show on page header and tilte of HTML
+ *          confidential:
+ *            type: String
+ *            description: confidential show on page header
+ *          globalLang:
+ *            type: String
+ *            description: language set when create user
+ *          fileUpload:
+ *            type: boolean
+ *            description: enable upload file except image file
+ */
+
+module.exports = (crowi) => {
+  const accessTokenParser = require('../../middleware/access-token-parser')(crowi);
+  const loginRequired = require('../../middleware/login-required')(crowi);
+  const loginRequiredStrictly = require('../../middleware/login-required')(crowi);
+  const adminRequired = require('../../middleware/admin-required')(crowi);
+  const csrf = require('../../middleware/csrf')(crowi);
+
+  const { ApiV3FormValidator } = crowi.middlewares;
+
+  /**
+   * @swagger
+   *
+   *    /app-settings/app-setting:
+   *      get:
+   *        tags: [AppSettings]
+   *        description: get app setting params
+   *        responses:
+   *          200:
+   *            description: Resources are available
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  properties:
+   *                    title:
+   *                      type: String
+   *                      description: site name show on page header and tilte of HTML
+   *                    confidential:
+   *                      type: String
+   *                      description: confidential show on page header
+   *                    globalLang:
+   *                      type: String
+   *                      description: language set when create user
+   *                    fileUpload:
+   *                      type: boolean
+   *                      description: enable upload file except image file
+   */
+  router.get('/app-setting', accessTokenParser, loginRequired, adminRequired, async(req, res) => {
+    const appSettingParams = {
+      title: crowi.configManager.getConfig('crowi', 'app:title'),
+      confidential: crowi.configManager.getConfig('crowi', 'app:confidential'),
+      globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
+      fileUpload: crowi.configManager.getConfig('crowi', 'app:fileUpload'),
+    };
+    return res.apiv3({ appSettingParams });
+
+  });
+
+
+  /**
+   * @swagger
+   *
+   *    /app-settings/app-setting:
+   *      put:
+   *        tags: [AppSettings]
+   *        description: Update app setting
+   *        requestBody:
+   *          required: true
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  title:
+   *                    type: String
+   *                    description: site name show on page header and tilte of HTML
+   *                  confidential:
+   *                    type: String
+   *                    description: confidential show on page header
+   *                  globalLang:
+   *                    type: String
+   *                    description: language set when create user
+   *                  fileUpload:
+   *                    type: boolean
+   *                    description: enable upload file except image file
+   *        responses:
+   *          200:
+   *            description: Succeeded to update app setting
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  properties:
+   *                    status:
+   *                      $ref: '#/components/schemas/appSettingParams'
+   */
+  router.put('/app-setting', loginRequiredStrictly, adminRequired, csrf, validator.appSetting, ApiV3FormValidator, async(req, res) => {
+
+    const requestAppSettingParams = {
+      'app:title': req.body.title,
+      'app:confidential': req.body.confidential,
+      'app:globalLang': req.body.globalLang,
+      'app:fileUpload': req.body.fileUpload,
+    };
+
+    try {
+      await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestAppSettingParams);
+      const appSettingParams = {
+        title: crowi.configManager.getConfig('crowi', 'app:title'),
+        confidential: crowi.configManager.getConfig('crowi', 'app:confidential'),
+        globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
+        fileUpload: crowi.configManager.getConfig('crowi', 'app:fileUpload'),
+      };
+      return res.apiv3({ appSettingParams });
+    }
+    catch (err) {
+      const msg = 'Error occurred in updating app setting';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'update-appSetting-failed'));
+    }
+
+  });
+
+
+  return router;
+};

+ 2 - 0
src/server/routes/apiv3/index.js

@@ -15,6 +15,8 @@ module.exports = (crowi) => {
 
   router.use('/markdown-setting', require('./markdown-setting')(crowi));
 
+  router.use('/app-settings', require('./app-settings')(crowi));
+
   router.use('/users', require('./users')(crowi));
 
   router.use('/user-groups', require('./user-group')(crowi));