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

Merge branch 'master' into feat/maintenance-mode-base

cao 4 лет назад
Родитель
Сommit
b158a5393b

+ 6 - 6
packages/app/resource/locales/en_US/admin/admin.json

@@ -22,13 +22,13 @@
   "v5_page_migration": {
     "page_tree_not_avaliable" : "Page tree feature is not available yet.",
     "go_to_settings": "Go to settings to enable the feature",
-    "migration_desc": "Some of the public pages have the old schema. To take advantage of new features such as page trees and easy renaming, please upgrade the schema of all your pages.",
+    "migration_desc": "There are some pages with old v4 compatibility. To take advantage of new features such as page trees and easy renaming, please convert all your pages to v5 compatibility.",
     "migration_note": "Note: You will lose unique constraints from the page paths.",
-    "upgrade_to_v5": "Upgrade to V5",
-    "modal_migration_warning": "This process may take long. It is highly recommended that administrators tell users not to create, modify, or delete pages during migration.",
-    "start_upgrading": "Start upgrading",
-    "successfully_started": "Succeeded to start migration",
-    "already_upgraded": "You have already completed upgrading"
+    "upgrade_to_v5": "Convert to v5 compatibility",
+    "modal_migration_warning": "This process may take long. It is highly recommended that administrators tell users not to create, modify, or delete pages during the conversion.",
+    "start_upgrading": "Start converting to v5 compatibility",
+    "successfully_started": "Succeeded to start the conversion",
+    "already_upgraded": "You have already completed the conversion to v5 compatibility"
   },
   "maintenance_mode": {
     "maintenance_mode": "Maintenance Mode",

+ 1 - 1
packages/app/resource/locales/en_US/translation.json

@@ -111,7 +111,7 @@
   "Create under": "Create page under below:",
   "Wiki Management Home Page": "Wiki Management Home Page",
   "App Settings": "App Settings",
-  "V5 Page Migration": "V5 Page Migration",
+  "V5 Page Migration": "Convert To V5 Compatibility",
   "GROWI.5.0_new_schema": "GROWI.5.0 new schema",
   "See_more_detail_on_new_schema": "See more detail on <a href='#'>{{url}}</a> <i class='icon-share-alt'></i> ",
   "Site URL settings": "Site URL settings",

+ 6 - 6
packages/app/resource/locales/ja_JP/admin/admin.json

@@ -22,13 +22,13 @@
   "v5_page_migration": {
     "page_tree_not_avaliable" : "Page Tree 機能は現在使用できません。",
     "go_to_settings": "設定する",
-    "migration_desc": "公開されているページに古いスキーマのものが存在します。ページツリーや簡単なリネームなどの新機能を利用するには、全てのページのスキーマをアップグレードしてください。",
+    "migration_desc": "公開されているページに 古い v4 互換形式のものが存在します。ページツリーや簡単なリネームなどの新機能を利用するには、全てのページを v5 互換形式に変換してください。",
     "migration_note": "注意: ページパスからユニーク制約が失われます。",
-    "upgrade_to_v5": "V5 にアップグレード",
-    "modal_migration_warning": "管理者はユーザーに、マイグレーション中はページを作成・変更・削除しないように伝えることを強くお勧めします。",
-    "start_upgrading": "アップグレードを開始",
-    "successfully_started": "正常にマイグレーションが開始されました",
-    "already_upgraded": "アップグレードは既に完了しています"
+    "upgrade_to_v5": "v5 互換形式 へ変換",
+    "modal_migration_warning": "管理者はユーザーに、v5 互換形式への変換中はページを作成・変更・削除しないように伝えることを強くお勧めします。",
+    "start_upgrading": "v5 互換形式への変換を開始",
+    "successfully_started": "正常に v5 互換形式への変換が開始されました",
+    "already_upgraded": "v5 互換形式への変換は既に完了しています"
   },
   "maintenance_mode": {
     "maintenance_mode": "メンテナンスモード",

+ 1 - 1
packages/app/resource/locales/ja_JP/translation.json

@@ -111,7 +111,7 @@
   "Create under": "ページを以下に作成",
   "Wiki Management Home Page": "Wiki管理トップ",
   "App Settings": "アプリ設定",
-  "V5 Page Migration": "V5 ページマイグレーション",
+  "V5 Page Migration": "V5 互換形式 への変換",
   "GROWI.5.0_new_schema": "GROWI.5.0における新スキーマについて",
   "See_more_detail_on_new_schema": "詳しくは<a href='#'>{{url}}</a><i class='icon-share-alt'></i>を参照ください。",
   "Site URL settings": "サイトURL設定",

+ 7 - 7
packages/app/resource/locales/zh_CN/admin/admin.json

@@ -22,13 +22,13 @@
   "v5_page_migration": {
     "page_tree_not_avaliable": "Page Tree 功能不可用",
     "go_to_settings": "进入设置,启用该功能",
-    "migration_desc": "Some of the public pages have the old schema. To take advantage of new features such as page trees and easy renaming, please upgrade the schema of all your pages. ",
-    "migration_note": "Note: You will lose unique constraints from the page paths.",
-    "upgrade_to_v5": "Upgrade to V5",
-    "modal_migration_warning": "This process may take long. It is highly recommended that administrators tell users not to create, modify, or delete pages during migration.",
-    "start_upgrading": "Start upgrading",
-    "successfully_started": "Succeeded to start migration",
-    "already_upgraded": "You have already completed upgrading"
+    "migration_desc": "有一些页面具有旧的v4兼容性。为了利用新的功能,如页面树和容易重命名,请将您的所有页面转换为v5兼容性。",
+    "migration_note": "注意:你将失去页面路径的唯一约束。",
+    "upgrade_to_v5": "转换为v5兼容性",
+    "modal_migration_warning": "这个过程可能需要很长时间。强烈建议管理员告诉用户在转换期间不要创建、修改或删除页面。",
+    "start_upgrading": "开始转换为v5兼容性",
+    "successfully_started": "成功开始转换",
+    "already_upgraded": "你已经完成了向v5兼容性的转换"
   },
   "maintenance_mode": {
     "maintenance_mode": "维护模式",

+ 13 - 13
packages/app/resource/locales/zh_CN/translation.json

@@ -119,7 +119,7 @@
 	"Create under": "Create page under below:",
 	"Wiki Management Home Page": "Wiki管理首页",
 	"App Settings": "系统设置",
-  "V5 Page Migration": "V5 Page Migration",
+  "V5 Page Migration": "转换为V5的兼容性",
   "GROWI.5.0_new_schema": "GROWI.5.0 new schema",
   "See_more_detail_on_new_schema": "更多详情请见<a href='#'>{{url}}</a> <i class='icon-share-alt'></i> ",
 	"Site URL settings": "主页URL设置",
@@ -919,19 +919,19 @@
     }
 	},
   "private_legacy_pages": {
-    "bulk_operation": "Bulk operation",
-    "convert_all_selected_pages": "Convert all to new v5 compatible format",
-    "alert_title": "You are viewing old v4 compatible private pages.",
-    "alert_desc1": "On this page, you can select pages with the checkbox and batch convert to the new v5 compatible format from the \"Bulk operation\" button at the top of the screen.",
-    "nopages_title": "Congratulations. Ready to use GROWI v5!",
-    "nopages_desc1": "Now all the pages you can manage seem to be in v5 compatible format.",
-    "detail_info": "See the detail information from <a href='https://docs.growi.org/en/admin-guide/upgrading/50x.html' target='_blank' class='alert-link'>Upgrading GROWI to v5.0.x <i class='icon-share-alt'></i></a>.",
+    "bulk_operation": "批量操作",
+    "convert_all_selected_pages": "全部转换为新的v5兼容格式",
+    "alert_title": "你正在查看旧的v4兼容的私人网页。",
+    "alert_desc1": "在这一页,你可以用复选框选择页面,并通过屏幕上方的批量操作按钮批量转换为新的v5兼容格式。",
+    "nopages_title": "恭喜你。准备使用GROWI v5!",
+    "nopages_desc1": "现在你能管理的所有页面似乎都是v5兼容的格式。",
+    "detail_info": "请参见 <a href='https://docs.growi.org/en/admin-guide/upgrading/50x.html' target='_blank' class='alert-link'>升级GROWI到v5.0.x <i class='icon-share-alt'></i></a>.的详细内容。",
     "modal": {
-      "title": "Convert to new v5 compatible format",
-      "converting_pages": "Converting pages",
-      "convert_recursively_label": "Convert child pages recursively.",
-      "convert_recursively_desc": "Convert pages under this path recursively.",
-      "button_label": "Convert"
+      "title": "转换为新的v5兼容格式",
+      "converting_pages": "转换页面",
+      "convert_recursively_label": "递归地转换子页面。",
+      "convert_recursively_desc": "递归地转换该路径下的页面。",
+      "button_label": "转换"
     }
   },
 	"to_cloud_settings": "進入 GROWI.cloud 的管理界面",

+ 16 - 1
packages/app/src/client/services/user-ui-settings.ts

@@ -5,6 +5,7 @@ import { debounce } from 'throttle-debounce';
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
+import { useIsGuestUser } from '~/stores/context';
 
 let settingsForBulk: Partial<IUserUISettings> = {};
 const _putUserUISettingsInBulk = (): Promise<AxiosResponse<IUserUISettings>> => {
@@ -18,7 +19,8 @@ const _putUserUISettingsInBulk = (): Promise<AxiosResponse<IUserUISettings>> =>
 
 const _putUserUISettingsInBulkDebounced = debounce(1500, false, _putUserUISettingsInBulk);
 
-export const scheduleToPutUserUISettings = (settings: Partial<IUserUISettings>): Promise<AxiosResponse<IUserUISettings>> => {
+type ScheduleToPutFunction = (settings: Partial<IUserUISettings>) => Promise<AxiosResponse<IUserUISettings>>;
+const scheduleToPut: ScheduleToPutFunction = (settings: Partial<IUserUISettings>): Promise<AxiosResponse<IUserUISettings>> => {
   settingsForBulk = {
     ...settingsForBulk,
     ...settings,
@@ -26,3 +28,16 @@ export const scheduleToPutUserUISettings = (settings: Partial<IUserUISettings>):
 
   return _putUserUISettingsInBulkDebounced();
 };
+
+type UserUISettingsUtil = {
+  scheduleToPut: ScheduleToPutFunction | (() => void),
+}
+export const useUserUISettings = (): UserUISettingsUtil => {
+  const { data: isGuestUser } = useIsGuestUser();
+
+  return {
+    scheduleToPut: isGuestUser
+      ? () => {}
+      : scheduleToPut,
+  };
+};

+ 3 - 1
packages/app/src/components/Admin/App/V5PageMigration.tsx

@@ -50,7 +50,9 @@ const V5PageMigration: FC<Props> = (props: Props) => {
       </p>
       <div className="row my-3">
         <div className="mx-auto">
-          <button type="button" className="btn btn-warning" onClick={() => setIsV5PageMigrationModalShown(true)}>Upgrade to v5</button>
+          <button type="button" className="btn btn-warning" onClick={() => setIsV5PageMigrationModalShown(true)}>
+            {t('admin:v5_page_migration.upgrade_to_v5')}
+          </button>
         </div>
       </div>
     </>

+ 6 - 5
packages/app/src/components/Navbar/PersonalDropdown.jsx

@@ -7,7 +7,7 @@ import { UncontrolledTooltip } from 'reactstrap';
 
 import { UserPicture } from '@growi/ui';
 
-import { scheduleToPutUserUISettings } from '~/client/services/user-ui-settings';
+import { useUserUISettings } from '~/client/services/user-ui-settings';
 import AppContainer from '~/client/services/AppContainer';
 
 import { withUnstatedContainers } from '../UnstatedUtils';
@@ -39,6 +39,7 @@ const PersonalDropdown = (props) => {
 
   const { data: isPreferDrawerMode, mutate: mutatePreferDrawerMode } = usePreferDrawerModeByUser();
   const { data: isPreferDrawerModeOnEdit, mutate: mutatePreferDrawerModeOnEdit } = usePreferDrawerModeOnEditByUser();
+  const { scheduleToPut } = useUserUISettings();
 
   const logoutHandler = () => {
     const { interceptorManager } = appContainer;
@@ -54,13 +55,13 @@ const PersonalDropdown = (props) => {
 
   const preferDrawerModeSwitchModifiedHandler = useCallback((bool) => {
     mutatePreferDrawerMode(bool);
-    scheduleToPutUserUISettings({ preferDrawerModeByUser: bool });
-  }, [mutatePreferDrawerMode]);
+    scheduleToPut({ preferDrawerModeByUser: bool });
+  }, [mutatePreferDrawerMode, scheduleToPut]);
 
   const preferDrawerModeOnEditSwitchModifiedHandler = useCallback((bool) => {
     mutatePreferDrawerModeOnEdit(bool);
-    scheduleToPutUserUISettings({ preferDrawerModeOnEditByUser: bool });
-  }, [mutatePreferDrawerModeOnEdit]);
+    scheduleToPut({ preferDrawerModeOnEditByUser: bool });
+  }, [mutatePreferDrawerModeOnEdit, scheduleToPut]);
 
   const followOsCheckboxModifiedHandler = (bool) => {
     if (bool) {

+ 12 - 8
packages/app/src/components/Sidebar.tsx

@@ -2,7 +2,7 @@ import React, {
   FC, useCallback, useEffect, useRef, useState,
 } from 'react';
 
-import { scheduleToPutUserUISettings } from '~/client/services/user-ui-settings';
+import { useUserUISettings } from '~/client/services/user-ui-settings';
 import {
   useDrawerMode, useDrawerOpened,
   useSidebarCollapsed,
@@ -29,6 +29,8 @@ const GlobalNavigation = () => {
   const { data: currentContents } = useCurrentSidebarContents();
   const { data: isCollapsed, mutate: mutateSidebarCollapsed } = useSidebarCollapsed();
 
+  const { scheduleToPut } = useUserUISettings();
+
   const itemSelectedHandler = useCallback((selectedContents) => {
     if (isDrawerMode) {
       return;
@@ -43,9 +45,9 @@ const GlobalNavigation = () => {
     }
 
     mutateSidebarCollapsed(newValue, false);
-    scheduleToPutUserUISettings({ isSidebarCollapsed: newValue });
+    scheduleToPut({ isSidebarCollapsed: newValue });
 
-  }, [currentContents, isCollapsed, isDrawerMode, mutateSidebarCollapsed]);
+  }, [currentContents, isCollapsed, isDrawerMode, mutateSidebarCollapsed, scheduleToPut]);
 
   return <SidebarNav onItemSelected={itemSelectedHandler} />;
 };
@@ -88,6 +90,8 @@ const Sidebar: FC<Props> = (props: Props) => {
   const { data: isCollapsed, mutate: mutateSidebarCollapsed } = useSidebarCollapsed();
   const { data: isResizeDisabled, mutate: mutateSidebarResizeDisabled } = useSidebarResizeDisabled();
 
+  const { scheduleToPut } = useUserUISettings();
+
   const [isTransitionEnabled, setTransitionEnabled] = useState(false);
 
   const [isHover, setHover] = useState(false);
@@ -165,8 +169,8 @@ const Sidebar: FC<Props> = (props: Props) => {
   const toggleNavigationBtnClickHandler = useCallback(() => {
     const newValue = !isCollapsed;
     mutateSidebarCollapsed(newValue, false);
-    scheduleToPutUserUISettings({ isSidebarCollapsed: newValue });
-  }, [isCollapsed, mutateSidebarCollapsed]);
+    scheduleToPut({ isSidebarCollapsed: newValue });
+  }, [isCollapsed, mutateSidebarCollapsed, scheduleToPut]);
 
   useEffect(() => {
     if (isCollapsed) {
@@ -199,18 +203,18 @@ const Sidebar: FC<Props> = (props: Props) => {
       // force collapsed
       mutateSidebarCollapsed(true);
       mutateProductNavWidth(sidebarMinWidth, false);
-      scheduleToPutUserUISettings({ isSidebarCollapsed: true, currentProductNavWidth: sidebarMinWidth });
+      scheduleToPut({ isSidebarCollapsed: true, currentProductNavWidth: sidebarMinWidth });
     }
     else {
       const newWidth = resizableContainer.current.clientWidth;
       mutateSidebarCollapsed(false);
       mutateProductNavWidth(newWidth, false);
-      scheduleToPutUserUISettings({ isSidebarCollapsed: false, currentProductNavWidth: newWidth });
+      scheduleToPut({ isSidebarCollapsed: false, currentProductNavWidth: newWidth });
     }
 
     resizableContainer.current.classList.remove('dragging');
 
-  }, [mutateProductNavWidth, mutateSidebarCollapsed]);
+  }, [mutateProductNavWidth, mutateSidebarCollapsed, scheduleToPut]);
 
   const dragableAreaMouseDownHandler = useCallback((event: React.MouseEvent) => {
     if (!isResizableByDrag) {

+ 13 - 11
packages/app/src/components/Sidebar/SidebarNav.tsx

@@ -1,8 +1,8 @@
 import React, { FC, memo, useCallback } from 'react';
 
-import { scheduleToPutUserUISettings } from '~/client/services/user-ui-settings';
+import { useUserUISettings } from '~/client/services/user-ui-settings';
 import { SidebarContentsType } from '~/interfaces/ui';
-import { useCurrentUser, useIsSharedUser } from '~/stores/context';
+import { useCurrentUser, useIsGuestUser } from '~/stores/context';
 import { useCurrentSidebarContents } from '~/stores/ui';
 
 
@@ -19,6 +19,7 @@ const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
   } = props;
 
   const { data: currentContents, mutate } = useCurrentSidebarContents();
+  const { scheduleToPut } = useUserUISettings();
 
   const isSelected = contents === currentContents;
 
@@ -28,8 +29,9 @@ const PrimaryItem: FC<PrimaryItemProps> = (props: PrimaryItemProps) => {
     }
 
     mutate(contents, false);
-    scheduleToPutUserUISettings({ currentSidebarContents: contents });
-  }, [contents, mutate, onItemSelected]);
+
+    scheduleToPut({ currentSidebarContents: contents });
+  }, [contents, mutate, onItemSelected, scheduleToPut]);
 
   const labelForTestId = label.toLowerCase().replace(' ', '-');
 
@@ -69,28 +71,28 @@ type Props = {
 
 const SidebarNav: FC<Props> = (props: Props) => {
 
-  const { data: isSharedUser } = useIsSharedUser();
   const { data: currentUser } = useCurrentUser();
 
   const isAdmin = currentUser?.admin;
-  const isLoggedIn = currentUser != null;
 
   const { onItemSelected } = props;
 
   return (
     <div className="grw-sidebar-nav">
       <div className="grw-sidebar-nav-primary-container">
-        {!isSharedUser && <PrimaryItem contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" onItemSelected={onItemSelected} />}
-        {!isSharedUser && <PrimaryItem contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onItemSelected={onItemSelected} />}
-        {!isSharedUser && <PrimaryItem contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onItemSelected={onItemSelected} />}
+        {/* eslint-disable max-len */}
+        <PrimaryItem contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" onItemSelected={onItemSelected} />
+        <PrimaryItem contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onItemSelected={onItemSelected} />
+        <PrimaryItem contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onItemSelected={onItemSelected} />
         {/* <PrimaryItem id="tag" label="Tags" iconName="icon-tag" /> */}
         {/* <PrimaryItem id="favorite" label="Favorite" iconName="fa fa-bookmark-o" /> */}
-        {!isSharedUser && <PrimaryItem contents={SidebarContentsType.TAG} label="Tags" iconName="tag" onItemSelected={onItemSelected} /> }
+        <PrimaryItem contents={SidebarContentsType.TAG} label="Tags" iconName="tag" onItemSelected={onItemSelected} />
         {/* <PrimaryItem id="favorite" label="Favorite" iconName="icon-star" /> */}
+        {/* eslint-enable max-len */}
       </div>
       <div className="grw-sidebar-nav-secondary-container">
         {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
-        {isLoggedIn && <SecondaryItem label="Draft" iconName="file_copy" href="/me/drafts" />}
+        <SecondaryItem label="Draft" iconName="file_copy" href="/me/drafts" />
         <SecondaryItem label="Help" iconName="help" href="https://docs.growi.org" isBlank />
         <SecondaryItem label="Trash" iconName="delete" href="/trash" />
       </div>

+ 2 - 2
packages/app/src/server/middlewares/login-required.js

@@ -44,8 +44,8 @@ module.exports = (crowi, isGuestAllowed = false, fallback = null) => {
     }
 
     // is api path
-    const path = req.path || '';
-    if (path.match(/^\/_api\/.+$/)) {
+    const baseUrl = req.baseUrl || '';
+    if (baseUrl.match(/^\/_api\/.+$/)) {
       if (fallback != null) {
         return fallback(req, res, next);
       }

+ 4 - 4
packages/app/test/integration/middlewares/login-required.test.js

@@ -98,8 +98,8 @@ describe('loginRequired', () => {
       isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead');
     });
 
-    test('send status 403 when \'req.path\' starts with \'_api\'', () => {
-      req.path = '/_api/someapi';
+    test('send status 403 when \'req.baseUrl\' starts with \'_api\'', () => {
+      req.baseUrl = '/_api/someapi';
 
       const result = loginRequiredStrictly(req, res, next);
 
@@ -113,7 +113,7 @@ describe('loginRequired', () => {
     });
 
     test('redirect to \'/login\' when the user does not loggedin', () => {
-      req.path = '/path/that/requires/loggedin';
+      req.baseUrl = '/path/that/requires/loggedin';
 
       const result = loginRequiredStrictly(req, res, next);
 
@@ -174,7 +174,7 @@ describe('loginRequired', () => {
     test('redirect to \'/login\' when user.status is \'STATUS_DELETED\'', () => {
       const User = crowi.model('User');
 
-      req.path = '/path/that/requires/loggedin';
+      req.baseUrl = '/path/that/requires/loggedin';
       req.user = {
         _id: 'user id',
         status: User.STATUS_DELETED,