Просмотр исходного кода

Merge pull request #1370 from weseek/create-custom-header-setting-frontside

Create custom header setting frontside
Yuki Takei 6 лет назад
Родитель
Сommit
bf4be1e5f3

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

@@ -682,6 +682,7 @@
     "update_behavior_success": "Succeeded to update behavior",
     "update_behavior_success": "Succeeded to update behavior",
     "update_function_success": "Succeeded to update function",
     "update_function_success": "Succeeded to update function",
     "update_highlight_success": "Succeeded to update code highlight",
     "update_highlight_success": "Succeeded to update code highlight",
+    "update_customHeader_success": "Succeeded to update customize html header",
     "update_customCss_success": "Succeeded to update customize css",
     "update_customCss_success": "Succeeded to update customize css",
     "update_script_success": "Succeeded to update custom script",
     "update_script_success": "Succeeded to update custom script",
     "layout_description":{
     "layout_description":{

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

@@ -666,6 +666,7 @@
     "update_behavior_success": "動作を更新しました",
     "update_behavior_success": "動作を更新しました",
     "update_function_success": "機能を更新しました",
     "update_function_success": "機能を更新しました",
     "update_highlight_success": "コードハイライトを更新しました",
     "update_highlight_success": "コードハイライトを更新しました",
+    "update_customHeader_success": "カスタムHTMLヘッダーを更新しました",
     "update_customCss_success": "カスタムCSSを更新しました",
     "update_customCss_success": "カスタムCSSを更新しました",
     "update_script_success": "カスタムスクリプトを更新しました",
     "update_script_success": "カスタムスクリプトを更新しました",
     "layout_description":{
     "layout_description":{

+ 0 - 12
src/client/js/app.jsx

@@ -35,7 +35,6 @@ import UserPictureList from './components/User/UserPictureList';
 import TableOfContents from './components/TableOfContents';
 import TableOfContents from './components/TableOfContents';
 
 
 import UserGroupDetailPage from './components/Admin/UserGroupDetail/UserGroupDetailPage';
 import UserGroupDetailPage from './components/Admin/UserGroupDetail/UserGroupDetailPage';
-import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import UserManagement from './components/Admin/UserManagement';
 import UserManagement from './components/Admin/UserManagement';
 import ManageExternalAccount from './components/Admin/ManageExternalAccount';
 import ManageExternalAccount from './components/Admin/ManageExternalAccount';
@@ -209,17 +208,6 @@ if (adminUserGroupDetailElem != null) {
   );
   );
 }
 }
 
 
-const customHeaderEditorElem = document.getElementById('custom-header-editor');
-if (customHeaderEditorElem != null) {
-  // get input[type=hidden] element
-  const customHeaderInputElem = document.getElementById('inputCustomHeader');
-
-  ReactDOM.render(
-    <CustomHeaderEditor inputElem={customHeaderInputElem} />,
-    customHeaderEditorElem,
-  );
-}
-
 const adminUserGroupPageElem = document.getElementById('admin-user-group-page');
 const adminUserGroupPageElem = document.getElementById('admin-user-group-page');
 if (adminUserGroupPageElem != null) {
 if (adminUserGroupPageElem != null) {
   const isAclEnabled = adminUserGroupPageElem.getAttribute('data-isAclEnabled') === 'true';
   const isAclEnabled = adminUserGroupPageElem.getAttribute('data-isAclEnabled') === 'true';

+ 4 - 5
src/client/js/components/Admin/CustomHeaderEditor.jsx

@@ -14,12 +14,10 @@ require('jquery-ui/ui/widgets/resizable');
 export default class CustomHeaderEditor extends React.Component {
 export default class CustomHeaderEditor extends React.Component {
 
 
   render() {
   render() {
-    // get initial value from inputElem
-    const value = this.props.inputElem.value;
 
 
     return (
     return (
       <CodeMirror
       <CodeMirror
-        value={value}
+        value={this.props.value}
         autoFocus
         autoFocus
         options={{
         options={{
           mode: 'htmlmixed',
           mode: 'htmlmixed',
@@ -41,7 +39,7 @@ export default class CustomHeaderEditor extends React.Component {
           });
           });
         }}
         }}
         onChange={(editor, data, value) => {
         onChange={(editor, data, value) => {
-          this.props.inputElem.value = value;
+          this.props.onChange(value);
         }}
         }}
       />
       />
     );
     );
@@ -50,5 +48,6 @@ export default class CustomHeaderEditor extends React.Component {
 }
 }
 
 
 CustomHeaderEditor.propTypes = {
 CustomHeaderEditor.propTypes = {
-  inputElem: PropTypes.object.isRequired,
+  value: PropTypes.string.isRequired,
+  onChange: PropTypes.func.isRequired,
 };
 };

+ 4 - 0
src/client/js/components/Admin/Customize/Customize.jsx

@@ -12,6 +12,7 @@ import CustomizeFunctionSetting from './CustomizeFunctionSetting';
 import CustomizeHighlightSetting from './CustomizeHighlightSetting';
 import CustomizeHighlightSetting from './CustomizeHighlightSetting';
 import CustomizeCssSetting from './CustomizeCssSetting';
 import CustomizeCssSetting from './CustomizeCssSetting';
 import CustomizeScriptSetting from './CustomizeScriptSetting';
 import CustomizeScriptSetting from './CustomizeScriptSetting';
+import CustomizeHeaderSetting from './CustomizeHeaderSetting';
 
 
 class Customize extends React.Component {
 class Customize extends React.Component {
 
 
@@ -34,6 +35,9 @@ class Customize extends React.Component {
         </div>
         </div>
         <legend>{t('customize_page.custom_title')}</legend>
         <legend>{t('customize_page.custom_title')}</legend>
         {/* カスタムタイトルフォームの react componentをここで呼ぶ(GW-278) */}
         {/* カスタムタイトルフォームの react componentをここで呼ぶ(GW-278) */}
+        <div className="my-3">
+          <CustomizeHeaderSetting />
+        </div>
         <div className="my-3">
         <div className="my-3">
           <CustomizeCssSetting />
           <CustomizeCssSetting />
         </div>
         </div>

+ 1 - 1
src/client/js/components/Admin/Customize/CustomizeCssSetting.jsx

@@ -51,7 +51,7 @@ class CustomizeCssSetting extends React.Component {
             <CustomCssEditor
             <CustomCssEditor
               // The value passed must be immutable
               // The value passed must be immutable
               value={appContainer.config.customizeCss}
               value={appContainer.config.customizeCss}
-              onChange={(inputValue) => { adminCustomizeContainer.changeCustomCss(inputValue) }}
+              onChange={(inputValue) => { adminCustomizeContainer.changeCustomizeCss(inputValue) }}
             />
             />
           </div>
           </div>
           <div className="col-xs-12">
           <div className="col-xs-12">

+ 91 - 0
src/client/js/components/Admin/Customize/CustomizeHeaderSetting.jsx

@@ -0,0 +1,91 @@
+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';
+
+import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
+import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
+import CustomHeaderEditor from '../CustomHeaderEditor';
+
+const logger = loggerFactory('growi:Customize');
+
+class CustomizeHeaderSetting extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.onClickSubmit = this.onClickSubmit.bind(this);
+  }
+
+  async onClickSubmit() {
+    const { t, adminCustomizeContainer } = this.props;
+
+    try {
+      await adminCustomizeContainer.updateCustomizeHeader();
+      toastSuccess(t('customize_page.update_customHeader_success'));
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  }
+
+  render() {
+    const { t, appContainer, adminCustomizeContainer } = this.props;
+
+    return (
+      <React.Fragment>
+        <h2>{t('customize_page.custom_header')}</h2>
+
+        <p
+          className="well"
+          // eslint-disable-next-line react/no-danger
+          dangerouslySetInnerHTML={{ __html: t('customize_page.custom_header_detail', '&lt;header&gt;', '&lt;script&gt;') }}
+        />
+
+        <div className="help-block">
+          { t('Example') }:
+          <pre className="hljs">
+            {/* eslint-disable-next-line react/no-unescaped-entities */}
+            <code>&lt;script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.13.0/build/languages/yaml.min.js" defer&gt;&lt;/script&gt;</code>
+          </pre>
+        </div>
+
+        <div className="col-xs-12">
+          <CustomHeaderEditor
+              // The value passed must be immutable
+            value={appContainer.config.customizeHeader}
+            onChange={(inputValue) => { adminCustomizeContainer.changeCustomizeHeader(inputValue) }}
+          />
+        </div>
+        <div className="col-xs-12">
+          <p className="help-block text-right">
+            <i className="fa fa-fw fa-keyboard-o" aria-hidden="true"></i>
+            { t('customize_page.ctrl_space') }
+          </p>
+        </div>
+
+        <AdminUpdateButtonRow onClick={this.onClickSubmit} />
+      </React.Fragment>
+    );
+  }
+
+}
+
+const CustomizeHeaderSettingWrapper = (props) => {
+  return createSubscribedElement(CustomizeHeaderSetting, props, [AppContainer, AdminCustomizeContainer]);
+};
+
+CustomizeHeaderSetting.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminCustomizeContainer: PropTypes.instanceOf(AdminCustomizeContainer).isRequired,
+};
+
+export default withTranslation()(CustomizeHeaderSettingWrapper);

+ 18 - 3
src/client/js/services/AdminCustomizeContainer.js

@@ -29,6 +29,7 @@ export default class AdminCustomizeContainer extends Container {
       currentRecentCreatedLimit: appContainer.config.recentCreatedLimit,
       currentRecentCreatedLimit: appContainer.config.recentCreatedLimit,
       currentHighlightJsStyleId: appContainer.config.highlightJsStyle,
       currentHighlightJsStyleId: appContainer.config.highlightJsStyle,
       isHighlightJsStyleBorderEnabled: appContainer.config.highlightJsStyleBorder,
       isHighlightJsStyleBorderEnabled: appContainer.config.highlightJsStyleBorder,
+      currentCustomizeHeader: appContainer.config.customizeHeader,
       currentCustomizeCss: appContainer.config.customizeCss,
       currentCustomizeCss: appContainer.config.customizeCss,
       currentCustomizeScript: appContainer.config.customizeScript,
       currentCustomizeScript: appContainer.config.customizeScript,
       /* eslint-disable quote-props, no-multi-spaces */
       /* eslint-disable quote-props, no-multi-spaces */
@@ -154,9 +155,16 @@ export default class AdminCustomizeContainer extends Container {
   }
   }
 
 
   /**
   /**
-   * Change custom css
+   * Change customize Html header
    */
    */
-  changeCustomCss(inputValue) {
+  changeCustomizeHeader(inputValue) {
+    this.setState({ currentCustomizeHeader: inputValue });
+  }
+
+  /**
+   * Change customize css
+   */
+  changeCustomizeCss(inputValue) {
     this.setState({ currentCustomizeCss: inputValue });
     this.setState({ currentCustomizeCss: inputValue });
   }
   }
 
 
@@ -220,6 +228,14 @@ export default class AdminCustomizeContainer extends Container {
     // TODO GW-515 create apiV3
     // TODO GW-515 create apiV3
   }
   }
 
 
+  /**
+   * Update customHeader
+   * @memberOf AdminCustomizeContainer
+   * @return {string} Customize html header
+   */
+  async updateCustomizeHeader() {
+    // TODO GW-601 create apiV3
+  }
 
 
   /**
   /**
    * Update customCss
    * Update customCss
@@ -247,5 +263,4 @@ export default class AdminCustomizeContainer extends Container {
     return customizedParams;
     return customizedParams;
   }
   }
 
 
-
 }
 }

+ 1 - 0
src/server/models/config.js

@@ -184,6 +184,7 @@ module.exports = function(crowi) {
       attrWhiteList: crowi.xssService.getAttrWhiteList(),
       attrWhiteList: crowi.xssService.getAttrWhiteList(),
       highlightJsStyle: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
       highlightJsStyle: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
       highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
       highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
+      customizeHeader: crowi.configManager.getConfig('crowi', 'customize:header'),
       customizeCss: crowi.configManager.getConfig('crowi', 'customize:css'),
       customizeCss: crowi.configManager.getConfig('crowi', 'customize:css'),
       isSavedStatesOfTabChanges: crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
       isSavedStatesOfTabChanges: crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
       isEnabledAttachTitleHeader: crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
       isEnabledAttachTitleHeader: crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),