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

Merge branch 'master' into feat/growi-bot

Yuki Takei 4 лет назад
Родитель
Сommit
3e0b40534f

+ 4 - 1
CHANGES.md

@@ -1,6 +1,9 @@
 # CHANGES
 
-## v4.2.17-RC
+## v4.2.18-RC
+* Feature: Cobvertible page contents width.
+
+## v4.2.17
 
 * Improvement: Invoke garbage collection when reindex all pages by elasticsearch
 * Improvement: Hide Sidebar at shared pages

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "4.2.17-RC",
+  "version": "4.2.18-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/default-dark.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/default-light.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/fluid-dark.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/images/customize-settings/fluid-light.svg


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

@@ -115,6 +115,11 @@
     }
   },
   "customize_setting": {
+    "layout": "Layout",
+    "layout_options": {
+      "default": "Default content width",
+      "expanded": "Content width 100%"
+    },
     "theme": "Theme",
     "theme_desc": {
       "light_and_dark": "Light and dark modes",

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

@@ -115,6 +115,11 @@
     }
   },
   "customize_setting": {
+    "layout": "レイアウト",
+    "layout_options": {
+      "default": "デフォルトのコンテンツ幅",
+      "expanded": "コンテンツ幅 100%"
+    },
     "theme": "テーマ",
     "theme_desc": {
       "light_and_dark": "Light/Dark モード選択あり",

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

@@ -114,6 +114,11 @@
 		}
 	},
 	"customize_setting": {
+    "layout": "布局",
+    "layout_options": {
+      "default": "默认内容宽度 ",
+      "expanded": "内容宽度100% "
+    },
 		"theme": "主体",
 		"behavior": "行为",
 		"behavior_desc": {

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

@@ -4,13 +4,15 @@ import PropTypes from 'prop-types';
 
 import loggerFactory from '@alias/logger';
 import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
+import AppContainer from '../../../services/AppContainer';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '../../../util/apiNotification';
 import toArrayIfNot from '../../../../../lib/util/toArrayIfNot';
 import { withLoadingSppiner } from '../../SuspenseUtils';
 
-import CustomizeLayoutSetting from './CustomizeThemeSetting';
+import CustomizeLayoutSetting from './CustomizeLayoutSetting';
+import CustomizeThemeSetting from './CustomizeThemeSetting';
 import CustomizeFunctionSetting from './CustomizeFunctionSetting';
 import CustomizeHighlightSetting from './CustomizeHighlightSetting';
 import CustomizeCssSetting from './CustomizeCssSetting';
@@ -22,7 +24,7 @@ const logger = loggerFactory('growi:services:AdminCustomizePage');
 
 let retrieveErrors = null;
 function Customize(props) {
-  const { adminCustomizeContainer } = props;
+  const { appContainer, adminCustomizeContainer } = props;
 
   if (adminCustomizeContainer.state.currentTheme === adminCustomizeContainer.dummyCurrentTheme) {
     throw (async() => {
@@ -46,7 +48,10 @@ function Customize(props) {
   return (
     <Fragment>
       <div className="mb-5">
-        <CustomizeLayoutSetting />
+        <CustomizeLayoutSetting appContainer={appContainer} />
+      </div>
+      <div className="mb-5">
+        <CustomizeThemeSetting />
       </div>
       <div className="mb-5">
         <CustomizeFunctionSetting />
@@ -70,9 +75,10 @@ function Customize(props) {
   );
 }
 
-const CustomizePageWithUnstatedContainer = withUnstatedContainers(withLoadingSppiner(Customize), [AdminCustomizeContainer]);
+const CustomizePageWithUnstatedContainer = withUnstatedContainers(withLoadingSppiner(Customize), [AppContainer, AdminCustomizeContainer]);
 
 Customize.propTypes = {
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminCustomizeContainer: PropTypes.instanceOf(AdminCustomizeContainer).isRequired,
 };
 

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

@@ -0,0 +1,93 @@
+import React, { useCallback, useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import AppContainer from '../../../services/AppContainer';
+
+import { toastSuccess, toastError } from '../../../util/apiNotification';
+import { isDarkMode as isDarkModeByUtil } from '../../../util/color-scheme';
+
+const isDarkMode = isDarkModeByUtil();
+const colorText = isDarkMode ? 'dark' : 'light';
+
+const CustomizeLayoutSetting = (props) => {
+  const { t, appContainer } = props;
+
+  const [isContainerFluid, setIsContainerFluid] = useState(false);
+  const [retrieveError, setRetrieveError] = useState();
+
+  const retrieveData = useCallback(async() => {
+    try {
+      const res = await appContainer.apiv3Get('/customize-setting/layout');
+      setIsContainerFluid(res.data.isContainerFluid);
+    }
+    catch (err) {
+      setRetrieveError(err);
+      toastError(err);
+    }
+  }, [appContainer]);
+
+  useEffect(() => {
+    retrieveData();
+  }, [retrieveData]);
+
+  const onClickSubmit = async() => {
+    try {
+      await appContainer.apiv3Put('/customize-setting/layout', { isContainerFluid });
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.layout') }));
+      retrieveData();
+    }
+    catch (err) {
+      toastError(err);
+    }
+  };
+
+  return (
+    <React.Fragment>
+      <div className="row">
+        <div className="col-12">
+          <h2 className="admin-setting-header">{t('admin:customize_setting.layout')}</h2>
+
+          <div className="d-flex justify-content-around mt-5">
+            <div id="layoutOptions" className="card-deck">
+              <div
+                className={`card customize-layout-card ${!isContainerFluid ? 'border-active' : ''}`}
+                onClick={() => setIsContainerFluid(false)}
+                role="button"
+              >
+                <img src={`/images/customize-settings/default-${colorText}.svg`} />
+                <div className="card-body text-center">
+                  {t('admin:customize_setting.layout_options.default')}
+                </div>
+              </div>
+              <div
+                className={`card customize-layout-card ${isContainerFluid ? 'border-active' : ''}`}
+                onClick={() => setIsContainerFluid(true)}
+                role="button"
+              >
+                <img src={`/images/customize-settings/fluid-${colorText}.svg`} />
+                <div className="card-body  text-center">
+                  {t('admin:customize_setting.layout_options.expanded')}
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div className="row my-3">
+            <div className="mx-auto">
+              <button type="button" className="btn btn-primary" onClick={onClickSubmit} disabled={retrieveError != null}>{ t('Update') }</button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </React.Fragment>
+  );
+};
+
+CustomizeLayoutSetting.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+};
+
+export default withTranslation()(CustomizeLayoutSetting);

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

@@ -35,7 +35,7 @@ class CustomizeThemeSetting extends React.Component {
     if (process.env.NODE_ENV === 'development') {
       return (
         <div className="alert alert-warning">
-          <strong>DEBUG MESSAGE:</strong> development build では、リアルタイムプレビューが無効になります
+          <strong>DEBUG MESSAGE:</strong> Live preview for theme is disabled in development mode.
         </div>
       );
     }

+ 1 - 1
src/client/js/components/PageContentFooter.jsx

@@ -15,7 +15,7 @@ const PageContentFooter = (props) => {
 
   return (
     <div className="page-content-footer py-4 d-edit-none d-print-none">
-      <div className="container-lg">
+      <div className="grw-container-convertible">
         <div className="page-meta">
           <AuthorInfo user={creator} date={createdAt} mode="create" locate="footer" />
           <AuthorInfo user={revisionAuthor} date={updatedAt} mode="update" locate="footer" />

+ 6 - 0
src/client/styles/scss/_admin.scss

@@ -188,6 +188,12 @@ $slack-work-space-name-card-border: #efc1f6;
   //   }
   // }
 
+  #layoutOptions {
+    .customize-layout-card {
+      border: 4px solid $border-color;
+    }
+  }
+
   // theme selector
   #themeOptions {
     // layout

+ 12 - 0
src/client/styles/scss/_layout.scss

@@ -2,6 +2,18 @@ body {
   overflow-y: scroll !important;
 }
 
+body:not(.growi-layout-fluid) .grw-container-convertible {
+  @extend .container-lg;
+}
+
+body.not-found-page .grw-container-convertible {
+  @extend .container-lg;
+}
+
+body.growi-layout-fluid .grw-container-convertible {
+  @extend .container-fluid;
+}
+
 .grw-logo {
   svg {
     width: $grw-logo-width;

+ 1 - 1
src/client/styles/scss/_mixins.scss

@@ -27,7 +27,7 @@
     height: calc(100vh - #{$editor-margin-top});
     margin-top: 0px !important;
 
-    .container-lg {
+    .grw-container-convertible {
       max-width: unset;
       padding: 0;
       margin: 0;

+ 14 - 5
src/client/styles/scss/_on-edit.scss

@@ -243,11 +243,6 @@ body.on-edit {
       overflow-y: scroll;
     }
 
-    .wiki {
-      max-width: 980px;
-      margin: 0 auto;
-    }
-
     .grw-editor-configuration-dropdown {
       .icon-container {
         width: 20px;
@@ -304,6 +299,20 @@ body.on-edit {
   }
 }
 
+body.on-edit {
+  &:not(.growi-layout-fluid) .page-editor-preview-body {
+    .wiki {
+      max-width: 980px;
+      margin: 0 auto;
+    }
+  }
+  &.growi-layout-fluid .page-editor-preview-body {
+    .wiki {
+      margin: 0 auto;
+    }
+  }
+}
+
 // overwrite .CodeMirror pre
 .CodeMirror pre.CodeMirror-line {
   font-family: $font-family-monospace;

+ 9 - 1
src/client/styles/scss/theme/_apply-colors.scss

@@ -559,9 +559,17 @@ mark.rbt-highlight-text {
 }
 
 /*
- * GROWI admin page #themeOptions
+ * GROWI admin page #layoutOptions #themeOptions
  */
 .admin-page {
+  #layoutOptions {
+    .customize-layout-card {
+      &.border-active {
+        border-color: $color-theme-color-box;
+      }
+    }
+  }
+
   #themeOptions {
     .theme-option-container.active {
       .theme-option-name {

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

@@ -22,7 +22,6 @@ module.exports = function(crowi) {
     // overwrite
     config['app:installed'] = true;
     config['app:fileUpload'] = true;
-    config['customize:layout'] = 'growi';
     config['customize:isSavedStatesOfTabChanges'] = false;
 
     return config;
@@ -107,7 +106,7 @@ module.exports = function(crowi) {
       'customize:highlightJsStyle' : 'github',
       'customize:highlightJsStyleBorder' : false,
       'customize:theme' : 'default',
-      'customize:layout' : 'growi',
+      'customize:isContainerFluid' : false,
       'customize:isEnabledTimeline' : true,
       'customize:isSavedStatesOfTabChanges' : true,
       'customize:isEnabledAttachTitleHeader' : false,
@@ -194,7 +193,6 @@ module.exports = function(crowi) {
         file: crowi.fileUploadService.getFileUploadEnabled(),
       },
       registrationWhiteList: crowi.configManager.getConfig('crowi', 'security:registrationWhiteList'),
-      layoutType: crowi.configManager.getConfig('crowi', 'customize:layout'),
       themeType: crowi.configManager.getConfig('crowi', 'customize:theme'),
       isEnabledLinebreaks: crowi.configManager.getConfig('markdown', 'markdown:isEnabledLinebreaks'),
       isEnabledLinebreaksInComments: crowi.configManager.getConfig('markdown', 'markdown:isEnabledLinebreaksInComments'),

+ 81 - 1
src/server/routes/apiv3/customize-setting.js

@@ -21,6 +21,12 @@ const ErrorV3 = require('../../models/vo/error-apiv3');
  *
  *  components:
  *    schemas:
+ *      CustomizeLayout:
+ *        description: CustomizeLayout
+ *        type: object
+ *        properties:
+ *          isContainerFluid:
+ *            type: boolean
  *      CustomizeTheme:
  *        description: CustomizeTheme
  *        type: object
@@ -87,6 +93,9 @@ module.exports = (crowi) => {
   const { customizeService } = crowi;
 
   const validator = {
+    layout: [
+      body('isContainerFluid').isBoolean(),
+    ],
     themeAssetPath: [
       query('themeName').isString(),
     ],
@@ -147,7 +156,6 @@ module.exports = (crowi) => {
   router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
 
     const customizeParams = {
-      layoutType: await crowi.configManager.getConfig('crowi', 'customize:layout'),
       themeType: await crowi.configManager.getConfig('crowi', 'customize:theme'),
       isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
       isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
@@ -169,6 +177,78 @@ module.exports = (crowi) => {
     return res.apiv3({ customizeParams });
   });
 
+  /**
+   * @swagger
+   *
+   *    /customize-setting/layout:
+   *      get:
+   *        tags: [CustomizeSetting]
+   *        operationId: getLayoutCustomizeSetting
+   *        summary: /customize-setting/layout
+   *        description: Get layout
+   *        responses:
+   *          200:
+   *            description: Succeeded to get layout
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  $ref: '#/components/schemas/CustomizeLayout'
+   */
+  router.get('/layout', loginRequiredStrictly, adminRequired, async(req, res) => {
+
+    try {
+      const isContainerFluid = await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid');
+      return res.apiv3({ isContainerFluid });
+    }
+    catch (err) {
+      const msg = 'Error occurred in getting layout';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'get-layout-failed'));
+    }
+  });
+
+  /**
+   * @swagger
+   *
+   *    /customize-setting/layout:
+   *      put:
+   *        tags: [CustomizeSetting]
+   *        operationId: updateLayoutCustomizeSetting
+   *        summary: /customize-setting/layout
+   *        description: Update layout
+   *        requestBody:
+   *          required: true
+   *          content:
+   *            application/json:
+   *              schema:
+   *                $ref: '#/components/schemas/CustomizeLayout'
+   *        responses:
+   *          200:
+   *            description: Succeeded to update layout
+   *            content:
+   *              application/json:
+   *                schema:
+   *                  $ref: '#/components/schemas/CustomizeLayout'
+   */
+  router.put('/layout', loginRequiredStrictly, adminRequired, csrf, validator.layout, apiV3FormValidator, async(req, res) => {
+    const requestParams = {
+      'customize:isContainerFluid': req.body.isContainerFluid,
+    };
+
+    try {
+      await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
+      const customizedParams = {
+        isContainerFluid: await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid'),
+      };
+      return res.apiv3({ customizedParams });
+    }
+    catch (err) {
+      const msg = 'Error occurred in updating layout';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'update-layout-failed'));
+    }
+  });
+
   /**
    * @swagger
    *

+ 3 - 2
src/server/views/layout-growi/not_found.html

@@ -1,15 +1,16 @@
 {% extends 'base/layout.html' %}
 
+{% block html_base_css %}not-found-page{% endblock %}
 
 {% block content_main_before %}
-  <div class="container-lg">
+  <div class="grw-container-convertible">
     {% include '../widget/page_alerts.html' %}
   </div>
 {% endblock %}
 
 
 {% block content_main %}
-  <div class="container-lg">
+  <div class="grw-container-convertible">
     {% include '../widget/not_found_content.html' %}
   </div>
 {% endblock %}

+ 1 - 1
src/server/views/layout-growi/page.html

@@ -6,7 +6,7 @@
 
 
 {% block content_main %}
-  <div class="container-lg">
+  <div class="grw-container-convertible">
 
     {% include '../widget/page_content.html' %}
 

+ 2 - 2
src/server/views/layout-growi/page_list.html

@@ -6,7 +6,7 @@
 
 
 {% block content_main %}
-  <div class="container-lg">
+  <div class="grw-container-convertible">
     {% include '../widget/page_content.html' %}
   </div>
 {% endblock %}
@@ -14,7 +14,7 @@
 
 {% block content_main_after %}
   {% if isTrashPage() %}
-    <div class="container-lg">
+    <div class="grw-container-convertible">
       <div id="trash-page-list"></div>
     </div>
   {% endif %}

+ 1 - 1
src/server/views/layout-growi/shared_page.html

@@ -21,7 +21,7 @@
 {% endblock %}
 
 {% block content_main %}
-  <div class="container-lg">
+  <div class="grw-container-convertible">
 
     <div
       id="is-shared-page"

+ 1 - 1
src/server/views/layout-growi/user_page.html

@@ -1,7 +1,7 @@
 {% extends 'page.html' %}
 
 {% block content_main %}
-  <div class="container-lg user-page">
+  <div class="grw-container-convertible user-page">
 
     {% include '../widget/page_content.html' %}
 

+ 4 - 1
src/server/views/layout/layout.html

@@ -60,8 +60,11 @@
 {% endblock %}
 
 {% block html_body %}
+{% if getConfig('crowi', 'customize:isContainerFluid') %}
+  {% set additionalBodyClass = 'growi-layout-fluid' %}
+{% endif %}
 <body
-  class="{% block html_base_css %}{% endblock %} growi"
+  class="{% block html_base_css %}{% endblock %} growi {{ additionalBodyClass }}"
   data-is-admin="{{ user.admin }}"
   data-plugin-enabled="{{ getConfig('crowi', 'plugin:isEnabledPlugins') }}"
   {% block html_base_attr %}{% endblock %}

+ 1 - 1
src/server/views/me/drafts.html

@@ -14,7 +14,7 @@
 {% endblock %}
 
 <div id="main" class="main">
-  <div id="content-main" class="content-main container-lg">
+  <div id="content-main" class="content-main grw-container-convertible">
     <div id="my-drafts"></div>
   </div>
 </div>

Некоторые файлы не были показаны из-за большого количества измененных файлов