فهرست منبع

Merge pull request #1348 from weseek/create-custom-css-frontside

Create custom css frontside
itizawa 6 سال پیش
والد
کامیت
33ee15461f

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

@@ -681,6 +681,7 @@
     "update_layout_success": "Succeeded to update layout",
     "update_layout_success": "Succeeded to update layout",
     "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_customCss_success": "Succeeded to update customize css",
     "layout_description":{
     "layout_description":{
       "growi_title":"Simple and Clear",
       "growi_title":"Simple and Clear",
       "growi_text1":"Full screen layout and thin margins/paddings",
       "growi_text1":"Full screen layout and thin margins/paddings",

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

@@ -665,6 +665,7 @@
     "update_layout_success": "レイアウトを更新しました",
     "update_layout_success": "レイアウトを更新しました",
     "update_behavior_success": "動作を更新しました",
     "update_behavior_success": "動作を更新しました",
     "update_function_success": "機能を更新しました",
     "update_function_success": "機能を更新しました",
+    "update_customCss_success": "カスタムCSSを更新しました",
     "layout_description":{
     "layout_description":{
       "growi_title":"シンプル・明瞭",
       "growi_title":"シンプル・明瞭",
       "growi_text1":"全画面レイアウトで、余白は少なくなります。",
       "growi_text1":"全画面レイアウトで、余白は少なくなります。",

+ 0 - 11
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 CustomCssEditor from './components/Admin/CustomCssEditor';
 import CustomScriptEditor from './components/Admin/CustomScriptEditor';
 import CustomScriptEditor from './components/Admin/CustomScriptEditor';
 import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
@@ -210,16 +209,6 @@ if (adminMarkDownSettingElem != null) {
   );
   );
 }
 }
 
 
-const customCssEditorElem = document.getElementById('custom-css-editor');
-if (customCssEditorElem != null) {
-  // get input[type=hidden] element
-  const customCssInputElem = document.getElementById('inputCustomCss');
-
-  ReactDOM.render(
-    <CustomCssEditor inputElem={customCssInputElem} />,
-    customCssEditorElem,
-  );
-}
 const customScriptEditorElem = document.getElementById('custom-script-editor');
 const customScriptEditorElem = document.getElementById('custom-script-editor');
 if (customScriptEditorElem != null) {
 if (customScriptEditorElem != null) {
   // get input[type=hidden] element
   // get input[type=hidden] element

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

@@ -16,12 +16,10 @@ require('jquery-ui/ui/widgets/resizable');
 export default class CustomCssEditor extends React.Component {
 export default class CustomCssEditor 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: 'css',
           mode: 'css',
@@ -43,7 +41,7 @@ export default class CustomCssEditor extends React.Component {
           });
           });
         }}
         }}
         onChange={(editor, data, value) => {
         onChange={(editor, data, value) => {
-          this.props.inputElem.value = value;
+          this.props.onChange(value);
         }}
         }}
       />
       />
     );
     );
@@ -52,5 +50,6 @@ export default class CustomCssEditor extends React.Component {
 }
 }
 
 
 CustomCssEditor.propTypes = {
 CustomCssEditor.propTypes = {
-  inputElem: PropTypes.object.isRequired,
+  value: PropTypes.string.isRequired,
+  onChange: PropTypes.func.isRequired,
 };
 };

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

@@ -9,6 +9,7 @@ import { createSubscribedElement } from '../../UnstatedUtils';
 import CustomizeLayoutSetting from './CustomizeLayoutSetting';
 import CustomizeLayoutSetting from './CustomizeLayoutSetting';
 import CustomizeBehaviorSetting from './CustomizeBehaviorSetting';
 import CustomizeBehaviorSetting from './CustomizeBehaviorSetting';
 import CustomizeFunctionSetting from './CustomizeFunctionSetting';
 import CustomizeFunctionSetting from './CustomizeFunctionSetting';
+import CustomizeCssSetting from './CustomizeCssSetting';
 
 
 class Customize extends React.Component {
 class Customize extends React.Component {
 
 
@@ -30,8 +31,9 @@ class Customize extends React.Component {
         {/* コードハイライトフォームの react componentをここで呼ぶ(GW-277) */}
         {/* コードハイライトフォームの react componentをここで呼ぶ(GW-277) */}
         <legend>{t('customize_page.custom_title')}</legend>
         <legend>{t('customize_page.custom_title')}</legend>
         {/* カスタムタイトルフォームの react componentをここで呼ぶ(GW-278) */}
         {/* カスタムタイトルフォームの react componentをここで呼ぶ(GW-278) */}
-        <legend>{t('customize_page.Custom CSS')}</legend>
-        {/* カスタムCSSフォームの react componentをここで呼ぶ(GW-279) */}
+        <div className="my-3">
+          <CustomizeCssSetting />
+        </div>
         <legend>{t('customize_page.Custom script')}</legend>
         <legend>{t('customize_page.Custom script')}</legend>
         {/* カスタムスクリプトフォームの react componentをここで呼ぶ(GW-280) */}
         {/* カスタムスクリプトフォームの react componentをここで呼ぶ(GW-280) */}
       </Fragment>
       </Fragment>

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

@@ -0,0 +1,82 @@
+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 CustomCssEditor from '../CustomCssEditor';
+
+const logger = loggerFactory('growi:Customize');
+
+class CustomizeCssSetting extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.onClickSubmit = this.onClickSubmit.bind(this);
+  }
+
+  async onClickSubmit() {
+    const { t, adminCustomizeContainer } = this.props;
+
+    try {
+      await adminCustomizeContainer.updateCustomizeCss();
+      toastSuccess(t('customize_page.update_customCss_success'));
+    }
+    catch (err) {
+      toastError(err);
+      logger.error(err);
+    }
+  }
+
+  render() {
+    const { t, appContainer, adminCustomizeContainer } = this.props;
+
+    return (
+      <React.Fragment>
+        <h2>{t('customize_page.Custom CSS')}</h2>
+        <p className="well">
+          { t('customize_page.write_CSS') }<br />
+          { t('customize_page.reflect_change') }
+        </p>
+        <div className="form-group">
+          <div className="col-xs-12">
+            <CustomCssEditor
+              // The value passed must be immutable
+              value={appContainer.config.customizeCss}
+              onChange={(inputValue) => { adminCustomizeContainer.changeCustomCss(inputValue) }}
+            />
+          </div>
+          <div className="col-xs-12">
+            <p className="help-block text-right">
+              <i className="fa fa-fw fa-keyboard-o" aria-hidden="true" />
+              { t('customize_page.ctrl_space') }
+            </p>
+          </div>
+        </div>
+
+        <AdminUpdateButtonRow onClick={this.onClickSubmit} />
+      </React.Fragment>
+    );
+  }
+
+}
+
+const CustomizeCssSettingWrapper = (props) => {
+  return createSubscribedElement(CustomizeCssSetting, props, [AppContainer, AdminCustomizeContainer]);
+};
+
+CustomizeCssSetting.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  adminCustomizeContainer: PropTypes.instanceOf(AdminCustomizeContainer).isRequired,
+};
+
+export default withTranslation()(CustomizeCssSettingWrapper);

+ 17 - 0
src/client/js/services/AdminCustomizeContainer.js

@@ -19,6 +19,7 @@ export default class AdminCustomizeContainer extends Container {
       isSavedStatesOfTabChanges: appContainer.config.isSavedStatesOfTabChanges,
       isSavedStatesOfTabChanges: appContainer.config.isSavedStatesOfTabChanges,
       isEnabledAttachTitleHeader: appContainer.config.isEnabledAttachTitleHeader,
       isEnabledAttachTitleHeader: appContainer.config.isEnabledAttachTitleHeader,
       currentRecentCreatedLimit: appContainer.config.recentCreatedLimit,
       currentRecentCreatedLimit: appContainer.config.recentCreatedLimit,
+      currentCustomizeCss: appContainer.config.customizeCss,
     };
     };
 
 
   }
   }
@@ -83,6 +84,13 @@ export default class AdminCustomizeContainer extends Container {
     this.setState({ currentRecentCreatedLimit: value });
     this.setState({ currentRecentCreatedLimit: value });
   }
   }
 
 
+  /**
+   * Change custom css
+   */
+  changeCustomCss(inputValue) {
+    this.setState({ currentCustomizeCss: inputValue });
+  }
+
   /**
   /**
    * Update layout
    * Update layout
    * @memberOf AdminCustomizeContainer
    * @memberOf AdminCustomizeContainer
@@ -126,4 +134,13 @@ export default class AdminCustomizeContainer extends Container {
     return customizedParams;
     return customizedParams;
   }
   }
 
 
+  /**
+   * Update customCss
+   * @memberOf AdminCustomizeContainer
+   * @return {string} css
+   */
+  async updateCustomizeCss() {
+    // TODO GW-534 create apiV3
+  }
+
 }
 }

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

@@ -183,6 +183,7 @@ module.exports = function(crowi) {
       tagWhiteList: crowi.xssService.getTagWhiteList(),
       tagWhiteList: crowi.xssService.getTagWhiteList(),
       attrWhiteList: crowi.xssService.getAttrWhiteList(),
       attrWhiteList: crowi.xssService.getAttrWhiteList(),
       highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
       highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
+      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'),
       hasSlackConfig: crowi.slackNotificationService.hasSlackConfig(),
       hasSlackConfig: crowi.slackNotificationService.hasSlackConfig(),