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

Merge branch 'support/gw7734-VRT-create-page' of https://github.com/weseek/growi into support/gw7734-VRT-create-page

mudana 3 лет назад
Родитель
Сommit
cd0b327e63

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


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


+ 31 - 0
packages/app/public/images/customize-settings/drawer-dark.svg

@@ -0,0 +1,31 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="249" height="160" viewBox="0 0 249 160">
+  <g transform="translate(17766 9529)">
+    <rect width="249" height="160" rx="2" transform="translate(-17766 -9529)" fill="#2a2d33"/>
+    <g transform="translate(-17700 -9500)">
+      <rect width="170" height="5" transform="translate(0 10)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="42.646" height="5" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="170" height="5" transform="translate(0 20)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="170" height="5" transform="translate(0 30)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+    </g>
+    <g transform="translate(-17700 -9435)">
+      <rect width="170" height="5" transform="translate(0 10)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="42.646" height="5" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="170" height="5" transform="translate(0 20)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="170" height="5" transform="translate(0 30)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+    </g>
+    <rect width="249" height="160" transform="translate(-17766 -9529)" fill="#16171d" opacity="0.586"/>
+    <g transform="translate(-217 -20)">
+      <path d="M2,160H-2V0H2Z" transform="translate(-17461 -9509)" fill="#209fd8"/>
+      <rect width="86" height="160" transform="translate(-17549 -9509)" fill="#343a40"/>
+    </g>
+    <g transform="translate(-217 -9)">
+      <rect width="47" height="5" transform="translate(-17530 -9431)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="47" height="5" transform="translate(-17530 -9441)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="47" height="5" transform="translate(-17530 -9451)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="47" height="5" transform="translate(-17530 -9461)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="47" height="5" transform="translate(-17530 -9471)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="47" height="5" transform="translate(-17530 -9481)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+      <rect width="11.787" height="5" transform="translate(-17530 -9491)" fill="#8e9aa7" stroke="rgba(0,0,0,0)" stroke-width="1"/>
+    </g>
+  </g>
+</svg>

+ 31 - 0
packages/app/public/images/customize-settings/drawer-light.svg

@@ -0,0 +1,31 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="249" height="160" viewBox="0 0 249 160">
+  <g transform="translate(17766 9529)">
+    <rect width="249" height="160" rx="2" transform="translate(-17766 -9529)" fill="#fff"/>
+    <g transform="translate(-17700 -9500)">
+      <rect width="170" height="5" transform="translate(0 10)" fill="#abb4bd"/>
+      <rect width="42.646" height="5" fill="#abb4bd"/>
+      <rect width="170" height="5" transform="translate(0 20)" fill="#abb4bd"/>
+      <rect width="170" height="5" transform="translate(0 30)" fill="#abb4bd"/>
+    </g>
+    <g transform="translate(-17700 -9435)">
+      <rect width="170" height="5" transform="translate(0 10)" fill="#abb4bd"/>
+      <rect width="42.646" height="5" fill="#abb4bd"/>
+      <rect width="170" height="5" transform="translate(0 20)" fill="#abb4bd"/>
+      <rect width="170" height="5" transform="translate(0 30)" fill="#abb4bd"/>
+    </g>
+    <rect width="249" height="160" transform="translate(-17766 -9529)" fill="#25272f" opacity="0.271"/>
+    <g transform="translate(-217 -20)">
+      <path d="M2,160H-2V0H2Z" transform="translate(-17461 -9509)" fill="#209fd8"/>
+      <rect width="86" height="160" transform="translate(-17549 -9509)" fill="#f3f7fc"/>
+    </g>
+    <g transform="translate(-217 -9)">
+      <rect width="47" height="5" transform="translate(-17530 -9431)" fill="#abb4bd"/>
+      <rect width="47" height="5" transform="translate(-17530 -9441)" fill="#abb4bd"/>
+      <rect width="47" height="5" transform="translate(-17530 -9451)" fill="#abb4bd"/>
+      <rect width="47" height="5" transform="translate(-17530 -9461)" fill="#abb4bd"/>
+      <rect width="47" height="5" transform="translate(-17530 -9471)" fill="#abb4bd"/>
+      <rect width="47" height="5" transform="translate(-17530 -9481)" fill="#abb4bd"/>
+      <rect width="11.787" height="5" transform="translate(-17530 -9491)" fill="#abb4bd"/>
+    </g>
+  </g>
+</svg>

+ 7 - 0
packages/app/public/static/locales/en_US/admin/admin.json

@@ -149,6 +149,13 @@
     }
   },
   "customize_setting": {
+    "default_sidebar_mode": {
+      "title": "Default sidebar mode",
+      "desc": "You can set the sidebar mode for new users and guests visiting the page.",
+      "dock_mode_default_desc": "You can set the initial state of the sidebar when Dock Mode is selected.",
+      "dock_mode_default_open": "Open the page as it was opened from the beginning",
+      "dock_mode_default_close": "Open the page as it was closed from the beginning"
+    },
     "layout": "Layout",
     "layout_options": {
       "default": "Default content width",

+ 7 - 0
packages/app/public/static/locales/ja_JP/admin/admin.json

@@ -149,6 +149,13 @@
     }
   },
   "customize_setting": {
+    "default_sidebar_mode": {
+      "title": "デフォルトのサイドバーモード",
+      "desc": "新規ユーザー、ページを訪れたゲストのサイドバーモードを設定できます。",
+      "dock_mode_default_desc": "Dock Mode選択時のサイドバーの初期状態を設定できます。",
+      "dock_mode_default_open": "初めから開いた状態でページを開く",
+      "dock_mode_default_close": "初めから閉じた状態でページを開く"
+    },
     "layout": "レイアウト",
     "layout_options": {
       "default": "デフォルトのコンテンツ幅",

+ 7 - 0
packages/app/public/static/locales/zh_CN/admin/admin.json

@@ -148,6 +148,13 @@
     }
   },
   "customize_setting": {
+    "default_sidebar_mode": {
+      "title": "默认的侧边栏模式",
+      "desc": "你可以为新用户和访问该网页的客人设置侧边栏模式。",
+      "dock_mode_default_desc": "当选择Dock模式时,可以设置侧边栏的初始状态。",
+      "dock_mode_default_open": "从头开始翻页",
+      "dock_mode_default_close": "从头开始打开关闭的页面"
+    },
     "layout": "布局",
     "layout_options": {
       "default": "默认内容宽度 ",

+ 2 - 2
packages/app/src/client/services/ContextExtractor.tsx

@@ -109,9 +109,9 @@ const ContextExtractorOnce: FC = () => {
   useCurrentUser(currentUser);
 
   // UserUISettings
-  usePreferDrawerModeByUser(userUISettings?.preferDrawerModeByUser);
+  usePreferDrawerModeByUser(userUISettings?.preferDrawerModeByUser ?? configByContextHydrate.isSidebarDrawerMode);
   usePreferDrawerModeOnEditByUser(userUISettings?.preferDrawerModeOnEditByUser);
-  useSidebarCollapsed(userUISettings?.isSidebarCollapsed);
+  useSidebarCollapsed(userUISettings?.isSidebarCollapsed ?? configByContextHydrate.isSidebarClosedAtDockMode);
   useCurrentSidebarContents(userUISettings?.currentSidebarContents);
   useCurrentProductNavWidth(userUISettings?.currentProductNavWidth);
 

+ 12 - 7
packages/app/src/components/Admin/Customize/Customize.jsx

@@ -1,23 +1,25 @@
 
 import React, { Fragment } from 'react';
+
 import PropTypes from 'prop-types';
 
-import loggerFactory from '~/utils/logger';
 import AdminCustomizeContainer from '~/client/services/AdminCustomizeContainer';
 import AppContainer from '~/client/services/AppContainer';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '~/client/util/apiNotification';
 import { toArrayIfNot } from '~/utils/array-utils';
+import loggerFactory from '~/utils/logger';
+
 import { withLoadingSppiner } from '../../SuspenseUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
-import CustomizeLayoutSetting from './CustomizeLayoutSetting';
-import CustomizeThemeSetting from './CustomizeThemeSetting';
+import CustomizeCssSetting from './CustomizeCssSetting';
 import CustomizeFunctionSetting from './CustomizeFunctionSetting';
+import CustomizeHeaderSetting from './CustomizeHeaderSetting';
 import CustomizeHighlightSetting from './CustomizeHighlightSetting';
-import CustomizeCssSetting from './CustomizeCssSetting';
+import CustomizeLayoutSetting from './CustomizeLayoutSetting';
 import CustomizeScriptSetting from './CustomizeScriptSetting';
-import CustomizeHeaderSetting from './CustomizeHeaderSetting';
+import CustomizeSidebarSetting from './CustomizeSidebarSetting';
+import CustomizeThemeSetting from './CustomizeThemeSetting';
 import CustomizeTitle from './CustomizeTitle';
 
 const logger = loggerFactory('growi:services:AdminCustomizePage');
@@ -53,6 +55,9 @@ function Customize(props) {
       <div className="mb-5">
         <CustomizeThemeSetting />
       </div>
+      <div className="mb-5">
+        <CustomizeSidebarSetting />
+      </div>
       <div className="mb-5">
         <CustomizeFunctionSetting />
       </div>

+ 118 - 0
packages/app/src/components/Admin/Customize/CustomizeSidebarSetting.tsx

@@ -0,0 +1,118 @@
+import React, { useCallback } from 'react';
+
+import { useTranslation } from 'react-i18next';
+import { Card, CardBody } from 'reactstrap';
+
+import { toastSuccess, toastError } from '~/client/util/apiNotification';
+import { isDarkMode as isDarkModeByUtil } from '~/client/util/color-scheme';
+import { useSWRxSidebarConfig } from '~/stores/ui';
+
+const CustomizeSidebarsetting = (): JSX.Element => {
+  const { t } = useTranslation();
+  const {
+    update, isSidebarDrawerMode, isSidebarClosedAtDockMode, setIsSidebarDrawerMode, setIsSidebarClosedAtDockMode,
+  } = useSWRxSidebarConfig();
+
+  const isDarkMode = isDarkModeByUtil();
+  const colorText = isDarkMode ? 'dark' : 'light';
+  const drawerIconFileName = `/images/customize-settings/drawer-${colorText}.svg`;
+  const dockIconFileName = `/images/customize-settings/dock-${colorText}.svg`;
+
+  const onClickSubmit = useCallback(async() => {
+    try {
+      await update();
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.default_sidebar_mode.title') }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [t, update]);
+
+  return (
+    <React.Fragment>
+      <div className="row">
+        <div className="col-12">
+
+          <h2 className="admin-setting-header">{t('admin:customize_setting.default_sidebar_mode.title')}</h2>
+
+          <Card className="card well my-3">
+            <CardBody className="px-0 py-2">
+              {t('admin:customize_setting.default_sidebar_mode.desc')}
+            </CardBody>
+          </Card>
+
+          <div className="d-flex justify-content-around mt-5">
+            <div id="layoutOptions" className="card-deck">
+              <div
+                className={`card customize-layout-card ${isSidebarDrawerMode ? 'border-active' : ''}`}
+                onClick={() => setIsSidebarDrawerMode(true)}
+                role="button"
+              >
+                <img src={drawerIconFileName} />
+                <div className="card-body text-center">
+                  Drawer Mode
+                </div>
+              </div>
+              <div
+                className={`card customize-layout-card ${!isSidebarDrawerMode ? 'border-active' : ''}`}
+                onClick={() => setIsSidebarDrawerMode(false)}
+                role="button"
+              >
+                <img src={dockIconFileName} />
+                <div className="card-body  text-center">
+                  Dock Mode
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <Card className="card well my-5">
+            <CardBody className="px-0 py-2">
+              {t('admin:customize_setting.default_sidebar_mode.dock_mode_default_desc')}
+            </CardBody>
+          </Card>
+
+          <div className="px-3">
+            <div className="custom-control custom-radio my-3">
+              <input
+                type="radio"
+                id="is-open"
+                className="custom-control-input"
+                name="mailVisibility"
+                checked={!isSidebarDrawerMode && !isSidebarClosedAtDockMode}
+                disabled={isSidebarDrawerMode}
+                onChange={() => setIsSidebarClosedAtDockMode(false)}
+              />
+              <label className="custom-control-label" htmlFor="is-open">
+                {t('admin:customize_setting.default_sidebar_mode.dock_mode_default_open')}
+              </label>
+            </div>
+            <div className="custom-control custom-radio my-3">
+              <input
+                type="radio"
+                id="is-closed"
+                className="custom-control-input"
+                name="mailVisibility"
+                checked={!isSidebarDrawerMode && isSidebarClosedAtDockMode}
+                disabled={isSidebarDrawerMode}
+                onChange={() => setIsSidebarClosedAtDockMode(true)}
+              />
+              <label className="custom-control-label" htmlFor="is-closed">
+                {t('admin:customize_setting.default_sidebar_mode.dock_mode_default_close')}
+              </label>
+            </div>
+          </div>
+
+          <div className="row my-3">
+            <div className="mx-auto">
+              <button type="button" onClick={onClickSubmit} className="btn btn-primary">{ t('Update') }</button>
+            </div>
+          </div>
+
+        </div>
+      </div>
+    </React.Fragment>
+  );
+};
+
+export default CustomizeSidebarsetting;

+ 5 - 0
packages/app/src/interfaces/sidebar-config.ts

@@ -0,0 +1,5 @@
+
+export interface ISidebarConfig {
+  isSidebarDrawerMode: boolean,
+  isSidebarClosedAtDockMode: boolean
+}

+ 4 - 0
packages/app/src/server/models/config.ts

@@ -135,6 +135,8 @@ export const defaultCrowiConfigs: { [key: string]: any } = {
   'customize:isEnabledStaleNotification': false,
   'customize:isAllReplyShown': false,
   'customize:isSearchScopeChildrenAsDefault': false,
+  'customize:isSidebarDrawerMode': false,
+  'customize:isSidebarClosedAtDockMode': false,
 
   'notification:owner-page:isEnabled': false,
   'notification:group-page:isEnabled': false,
@@ -243,6 +245,8 @@ schema.statics.getLocalconfig = function(crowi) {
     globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
     pageLimitationL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
     pageLimitationXL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
+    isSidebarDrawerMode: crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
+    isSidebarClosedAtDockMode: crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
   };
 
   return localConfig;

+ 41 - 0
packages/app/src/server/routes/apiv3/customize-setting.js

@@ -10,6 +10,7 @@ const express = require('express');
 const router = express.Router();
 
 const { body, query } = require('express-validator');
+
 const ErrorV3 = require('../../models/vo/error-apiv3');
 
 /**
@@ -105,6 +106,10 @@ module.exports = (crowi) => {
     theme: [
       body('themeType').isString(),
     ],
+    sidebar: [
+      body('isSidebarDrawerMode').isBoolean(),
+      body('isSidebarClosedAtDockMode').isBoolean(),
+    ],
     function: [
       body('isEnabledTimeline').isBoolean(),
       body('isSavedStatesOfTabChanges').isBoolean(),
@@ -334,6 +339,42 @@ module.exports = (crowi) => {
     }
   });
 
+  // sidebar
+  router.get('/sidebar', loginRequiredStrictly, adminRequired, async(req, res) => {
+
+    try {
+      const isSidebarDrawerMode = await crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode');
+      const isSidebarClosedAtDockMode = await crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode');
+      return res.apiv3({ isSidebarDrawerMode, isSidebarClosedAtDockMode });
+    }
+    catch (err) {
+      const msg = 'Error occurred in getting sidebar';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'get-sidebar-failed'));
+    }
+  });
+
+  router.put('/sidebar', loginRequiredStrictly, adminRequired, csrf, validator.sidebar, apiV3FormValidator, async(req, res) => {
+    const requestParams = {
+      'customize:isSidebarDrawerMode': req.body.isSidebarDrawerMode,
+      'customize:isSidebarClosedAtDockMode': req.body.isSidebarClosedAtDockMode,
+    };
+
+    try {
+      await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
+      const customizedParams = {
+        isSidebarDrawerMode: await crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
+        isSidebarClosedAtDockMode: await crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
+      };
+      return res.apiv3({ customizedParams });
+    }
+    catch (err) {
+      const msg = 'Error occurred in updating sidebar';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'update-sidebar-failed'));
+    }
+  });
+
   /**
    * @swagger
    *

+ 1 - 0
packages/app/src/server/service/search-delegator/elasticsearch.ts

@@ -943,6 +943,7 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
 
   appendHighlight(query) {
     query.body.highlight = {
+      max_analyzed_offset: 1000000 - 1, // Set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them.
       fields: {
         '*': {
           fragment_size: 40,

+ 68 - 0
packages/app/src/stores/ui.tsx

@@ -10,7 +10,9 @@ import useSWRImmutable from 'swr/immutable';
 
 import { IFocusable } from '~/client/interfaces/focusable';
 import { useUserUISettings } from '~/client/services/user-ui-settings';
+import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import { Nullable } from '~/interfaces/common';
+import { ISidebarConfig } from '~/interfaces/sidebar-config';
 import { SidebarContentsType } from '~/interfaces/ui';
 import { UpdateDescCountData } from '~/interfaces/websocket';
 import loggerFactory from '~/utils/logger';
@@ -278,6 +280,72 @@ export const useDrawerMode = (): SWRResponse<boolean, Error> => {
   );
 };
 
+type SidebarConfigOption = {
+  update: () => Promise<void>,
+  isSidebarDrawerMode: boolean|undefined,
+  isSidebarClosedAtDockMode: boolean|undefined,
+  setIsSidebarDrawerMode: (isSidebarDrawerMode: boolean) => void,
+  setIsSidebarClosedAtDockMode: (isSidebarClosedAtDockMode: boolean) => void
+}
+
+export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> & SidebarConfigOption => {
+  const swrResponse = useSWRImmutable<ISidebarConfig>(
+    '/customize-setting/sidebar',
+    endpoint => apiv3Get(endpoint).then(result => result.data),
+  );
+  return {
+    ...swrResponse,
+    update: async() => {
+      const { data } = swrResponse;
+
+      if (data == null) {
+        return;
+      }
+
+      const { isSidebarDrawerMode, isSidebarClosedAtDockMode } = data;
+
+      const updateData = {
+        isSidebarDrawerMode,
+        isSidebarClosedAtDockMode,
+      };
+
+      // invoke API
+      await apiv3Put('/customize-setting/sidebar', updateData);
+    },
+    isSidebarDrawerMode: swrResponse.data?.isSidebarDrawerMode,
+    isSidebarClosedAtDockMode: swrResponse.data?.isSidebarClosedAtDockMode,
+    setIsSidebarDrawerMode: (isSidebarDrawerMode) => {
+      const { data, mutate } = swrResponse;
+
+      if (data == null) {
+        return;
+      }
+
+      const updateData = {
+        isSidebarDrawerMode,
+      };
+
+      // update isSidebarDrawerMode in cache, not revalidate
+      mutate({ ...data, ...updateData }, false);
+
+    },
+    setIsSidebarClosedAtDockMode: (isSidebarClosedAtDockMode) => {
+      const { data, mutate } = swrResponse;
+
+      if (data == null) {
+        return;
+      }
+
+      const updateData = {
+        isSidebarClosedAtDockMode,
+      };
+
+      // update isSidebarClosedAtDockMode in cache, not revalidate
+      mutate({ ...data, ...updateData }, false);
+    },
+  };
+};
+
 export const useDrawerOpened = (isOpened?: boolean): SWRResponse<boolean, Error> => {
   return useStaticSWR('isDrawerOpened', isOpened, { fallbackData: false });
 };

+ 7 - 0
packages/app/src/styles/_admin.scss

@@ -59,6 +59,13 @@ $slack-work-space-name-card-border: #efc1f6;
     }
   }
 
+  .admin-customize-sidebar-icon {
+    svg {
+      width: 20px;
+      height: 20px;
+    }
+  }
+
   .admin-notification {
     table .admin-notif-list {
       td {

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