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

Merge branch 'dev/7.4.x' into fix/176123-duplicate-button-missing-with-user-home-page-deletion-enabled

Shun Miyazawa 3 месяцев назад
Родитель
Сommit
0a2e04f53c
23 измененных файлов с 272 добавлено и 170 удалено
  1. 34 0
      apps/app/public/images/customize-settings/collapsed-dark.svg
  2. 4 1
      apps/app/public/images/customize-settings/collapsed-light.svg
  3. 0 31
      apps/app/public/images/customize-settings/drawer-dark.svg
  4. 1 4
      apps/app/public/static/locales/en_US/admin.json
  5. 1 4
      apps/app/public/static/locales/fr_FR/admin.json
  6. 1 4
      apps/app/public/static/locales/ja_JP/admin.json
  7. 1 4
      apps/app/public/static/locales/ko_KR/admin.json
  8. 1 4
      apps/app/public/static/locales/zh_CN/admin.json
  9. 5 40
      apps/app/src/client/components/Admin/Customize/CustomizeSidebarSetting.tsx
  10. 1 0
      apps/app/src/client/components/LoginForm/LoginForm.tsx
  11. 0 1
      apps/app/src/interfaces/sidebar-config.ts
  12. 4 1
      apps/app/src/migrations/20211227060705-revision-path-to-page-id-schema-migration--fixed-8998.js
  13. 0 3
      apps/app/src/pages/basic-layout-page/get-server-side-props/sidebar-configurations.ts
  14. 4 18
      apps/app/src/server/routes/apiv3/customize-setting.js
  15. 1 1
      apps/app/src/server/routes/apiv3/page/index.ts
  16. 1 1
      apps/app/src/server/routes/apiv3/page/update-page.ts
  17. 7 26
      apps/app/src/server/routes/apiv3/revisions.js
  18. 0 5
      apps/app/src/server/service/config-manager/config-definition.ts
  19. 117 2
      apps/app/src/server/service/revision/normalize-latest-revision-if-broken.integ.ts
  20. 87 1
      apps/app/src/server/service/revision/normalize-latest-revision-if-broken.ts
  21. 1 1
      apps/app/src/server/service/yjs/sync-ydoc.ts
  22. 1 1
      apps/app/src/server/service/yjs/yjs.ts
  23. 0 17
      apps/app/src/stores/admin/sidebar-config.tsx

+ 34 - 0
apps/app/public/images/customize-settings/collapsed-dark.svg

@@ -0,0 +1,34 @@
+<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"/>
+      <rect width="42.646" height="5" fill="#8e9aa7"/>
+      <rect width="170" height="5" transform="translate(0 20)" fill="#8e9aa7"/>
+      <rect width="170" height="5" transform="translate(0 30)" fill="#8e9aa7"/>
+    </g>
+    <g transform="translate(-17700 -9435)">
+      <rect width="170" height="5" transform="translate(0 10)" fill="#8e9aa7"/>
+      <rect width="42.646" height="5" fill="#8e9aa7"/>
+      <rect width="170" height="5" transform="translate(0 20)" fill="#8e9aa7"/>
+      <rect width="170" height="5" transform="translate(0 30)" fill="#8e9aa7"/>
+    </g>
+    <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"/>
+      <rect width="47" height="5" transform="translate(-17530 -9441)" fill="#8e9aa7"/>
+      <rect width="47" height="5" transform="translate(-17530 -9451)" fill="#8e9aa7"/>
+      <rect width="47" height="5" transform="translate(-17530 -9461)" fill="#8e9aa7"/>
+      <rect width="47" height="5" transform="translate(-17530 -9471)" fill="#8e9aa7"/>
+      <rect width="47" height="5" transform="translate(-17530 -9481)" fill="#8e9aa7"/>
+      <rect width="11.787" height="5" transform="translate(-17530 -9491)" fill="#8e9aa7"/>
+    </g>
+    <g transform="translate(-37 186)" fill="#2a2d33" stroke-linecap="round" stroke-linejoin="round">
+      <path d="M -17689.794921875 -9624.6689453125 L -17689.794921875 -9628.078125 L -17689.794921875 -9645.810546875 L -17689.794921875 -9649.60546875 L -17687.201171875 -9646.8359375 L -17675.93359375 -9634.8095703125 L -17674.029296875 -9632.7783203125 L -17676.7734375 -9632.3056640625 L -17682.544921875 -9631.3115234375 L -17687.28125 -9626.9716796875 L -17689.794921875 -9624.6689453125 Z" stroke="none"/>
+      <path d="M -17688.294921875 -9645.810546875 L -17688.294921875 -9628.078125 L -17683.234375 -9632.71484375 L -17677.02734375 -9633.7841796875 L -17688.294921875 -9645.810546875 M -17688.294921875 -9648.810546875 C -17687.482421875 -9648.810546875 -17686.68359375 -9648.4794921875 -17686.10546875 -9647.861328125 L -17674.837890625 -9635.8349609375 C -17674.083984375 -9635.0302734375 -17673.83203125 -9633.8759765625 -17674.18359375 -9632.830078125 C -17674.533203125 -9631.7841796875 -17675.431640625 -9631.0146484375 -17676.517578125 -9630.828125 L -17681.857421875 -9629.908203125 L -17686.267578125 -9625.8662109375 C -17687.14453125 -9625.0634765625 -17688.4140625 -9624.8525390625 -17689.50390625 -9625.33203125 C -17690.591796875 -9625.8115234375 -17691.294921875 -9626.888671875 -17691.294921875 -9628.078125 L -17691.294921875 -9645.810546875 C -17691.294921875 -9647.0419921875 -17690.54296875 -9648.1474609375 -17689.3984375 -9648.6005859375 C -17689.0390625 -9648.7421875 -17688.666015625 -9648.810546875 -17688.294921875 -9648.810546875 Z" stroke="none" fill="#fff"/>
+    </g>
+  </g>
+</svg>

+ 4 - 1
apps/app/public/images/customize-settings/drawer-light.svg → apps/app/public/images/customize-settings/collapsed-light.svg

@@ -13,7 +13,6 @@
       <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"/>
@@ -27,5 +26,9 @@
       <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 transform="translate(-37 186)" fill="#2a2d33" stroke-linecap="round" stroke-linejoin="round">
+      <path d="M -17689.794921875 -9624.6689453125 L -17689.794921875 -9628.078125 L -17689.794921875 -9645.810546875 L -17689.794921875 -9649.60546875 L -17687.201171875 -9646.8359375 L -17675.93359375 -9634.8095703125 L -17674.029296875 -9632.7783203125 L -17676.7734375 -9632.3056640625 L -17682.544921875 -9631.3115234375 L -17687.28125 -9626.9716796875 L -17689.794921875 -9624.6689453125 Z" stroke="none"/>
+      <path d="M -17688.294921875 -9645.810546875 L -17688.294921875 -9628.078125 L -17683.234375 -9632.71484375 L -17677.02734375 -9633.7841796875 L -17688.294921875 -9645.810546875 M -17688.294921875 -9648.810546875 C -17687.482421875 -9648.810546875 -17686.68359375 -9648.4794921875 -17686.10546875 -9647.861328125 L -17674.837890625 -9635.8349609375 C -17674.083984375 -9635.0302734375 -17673.83203125 -9633.8759765625 -17674.18359375 -9632.830078125 C -17674.533203125 -9631.7841796875 -17675.431640625 -9631.0146484375 -17676.517578125 -9630.828125 L -17681.857421875 -9629.908203125 L -17686.267578125 -9625.8662109375 C -17687.14453125 -9625.0634765625 -17688.4140625 -9624.8525390625 -17689.50390625 -9625.33203125 C -17690.591796875 -9625.8115234375 -17691.294921875 -9626.888671875 -17691.294921875 -9628.078125 L -17691.294921875 -9645.810546875 C -17691.294921875 -9647.0419921875 -17690.54296875 -9648.1474609375 -17689.3984375 -9648.6005859375 C -17689.0390625 -9648.7421875 -17688.666015625 -9648.810546875 -17688.294921875 -9648.810546875 Z" stroke="none" fill="#fff"/>
+    </g>
   </g>
 </svg>

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

@@ -1,31 +0,0 @@
-<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>

+ 1 - 4
apps/app/public/static/locales/en_US/admin.json

@@ -446,10 +446,7 @@
     "customize_settings": "Customize",
     "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"
+      "desc": "You can set the sidebar mode for new users and guests visiting the page."
     },
     "layout": "Layout",
     "layout_options": {

+ 1 - 4
apps/app/public/static/locales/fr_FR/admin.json

@@ -446,10 +446,7 @@
     "customize_settings": "Interface",
     "default_sidebar_mode": {
       "title": "Barre latérale",
-      "desc": "Mode d'affichage et comportement par défaut de la barre latérale.",
-      "dock_mode_default_desc": "État initial de la barre latérale lorsque le mode Dock est sélectionné.",
-      "dock_mode_default_open": "Afficher la page comme si elle était ouverte",
-      "dock_mode_default_close": "Afficher la page comme si elle était fermée"
+      "desc": "Mode d'affichage et comportement par défaut de la barre latérale."
     },
     "layout": "Largeur du contenu",
     "layout_options": {

+ 1 - 4
apps/app/public/static/locales/ja_JP/admin.json

@@ -455,10 +455,7 @@
     "customize_settings": "カスタマイズ",
     "default_sidebar_mode": {
       "title": "デフォルトのサイドバーモード",
-      "desc": "新規ユーザー、ページを訪れたゲストのサイドバーモードを設定できます。",
-      "dock_mode_default_desc": "Dock Mode選択時のサイドバーの初期状態を設定できます。",
-      "dock_mode_default_open": "初めから開いた状態でページを開く",
-      "dock_mode_default_close": "初めから閉じた状態でページを開く"
+      "desc": "新規ユーザー、ページを訪れたゲストのサイドバーモードを設定できます。"
     },
     "layout": "レイアウト",
     "layout_options": {

+ 1 - 4
apps/app/public/static/locales/ko_KR/admin.json

@@ -446,10 +446,7 @@
     "customize_settings": "사용자 지정",
     "default_sidebar_mode": {
       "title": "기본 사이드바 모드",
-      "desc": "새 사용자 및 페이지를 방문하는 게스트를 위한 사이드바 모드를 설정할 수 있습니다.",
-      "dock_mode_default_desc": "독 모드가 선택되었을 때 사이드바의 초기 상태를 설정할 수 있습니다.",
-      "dock_mode_default_open": "처음부터 열린 상태로 페이지 열기",
-      "dock_mode_default_close": "처음부터 닫힌 상태로 페이지 열기"
+      "desc": "새 사용자 및 페이지를 방문하는 게스트를 위한 사이드바 모드를 설정할 수 있습니다."
     },
     "layout": "레이아웃",
     "layout_options": {

+ 1 - 4
apps/app/public/static/locales/zh_CN/admin.json

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

+ 5 - 40
apps/app/src/client/components/Admin/Customize/CustomizeSidebarSetting.tsx

@@ -12,11 +12,11 @@ const CustomizeSidebarsetting = (): JSX.Element => {
   const { t } = useTranslation(['admin', 'commons']);
 
   const {
-    data, update, setIsSidebarCollapsedMode, setIsSidebarClosedAtDockMode,
+    data, update, setIsSidebarCollapsedMode,
   } = useSWRxSidebarConfig();
 
   const { resolvedTheme } = useNextThemes();
-  const drawerIconFileName = `/images/customize-settings/drawer-${resolvedTheme}.svg`;
+  const collapsedIconFileName = `/images/customize-settings/collapsed-${resolvedTheme}.svg`;
   const dockIconFileName = `/images/customize-settings/dock-${resolvedTheme}.svg`;
 
   const onClickSubmit = useCallback(async() => {
@@ -33,7 +33,7 @@ const CustomizeSidebarsetting = (): JSX.Element => {
     return <LoadingSpinner />;
   }
 
-  const { isSidebarCollapsedMode, isSidebarClosedAtDockMode } = data;
+  const { isSidebarCollapsedMode } = data;
 
   return (
     <React.Fragment>
@@ -57,9 +57,9 @@ const CustomizeSidebarsetting = (): JSX.Element => {
                   role="button"
                 >
                   {/* eslint-disable-next-line @next/next/no-img-element */}
-                  <img src={drawerIconFileName} alt="Drawer Mode" />
+                  <img src={collapsedIconFileName} alt="Collapsed Mode" />
                   <div className="card-body text-center">
-                    Drawer Mode
+                    Collapsed Mode
                   </div>
                 </div>
               </div>
@@ -79,41 +79,6 @@ const CustomizeSidebarsetting = (): JSX.Element => {
             </div>
           </div>
 
-          <Card className="card custom-card bg-body-tertiary my-5">
-            <CardBody className="px-0 py-2">
-              {t('customize_settings.default_sidebar_mode.dock_mode_default_desc')}
-            </CardBody>
-          </Card>
-
-          <div className="px-3">
-            <div className="form-check my-3">
-              <input
-                type="radio"
-                id="is-open"
-                className="form-check-input"
-                checked={isSidebarCollapsedMode === false && isSidebarClosedAtDockMode === false}
-                disabled={isSidebarCollapsedMode}
-                onChange={() => setIsSidebarClosedAtDockMode(false)}
-              />
-              <label className="form-label form-check-label" htmlFor="is-open">
-                {t('customize_settings.default_sidebar_mode.dock_mode_default_open')}
-              </label>
-            </div>
-            <div className="form-check my-3">
-              <input
-                type="radio"
-                id="is-closed"
-                className="form-check-input"
-                checked={isSidebarCollapsedMode === false && isSidebarClosedAtDockMode === true}
-                disabled={isSidebarCollapsedMode}
-                onChange={() => setIsSidebarClosedAtDockMode(true)}
-              />
-              <label className="form-label form-check-label" htmlFor="is-closed">
-                {t('customize_settings.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>

+ 1 - 0
apps/app/src/client/components/LoginForm/LoginForm.tsx

@@ -282,6 +282,7 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
 
       setIsSuccessToRagistration(true);
       resetRegisterErrors();
+      setIsLoading(false);
 
       const { redirectTo } = res.data;
 

+ 0 - 1
apps/app/src/interfaces/sidebar-config.ts

@@ -1,4 +1,3 @@
 export interface ISidebarConfig {
   isSidebarCollapsedMode: boolean;
-  isSidebarClosedAtDockMode?: boolean;
 }

+ 4 - 1
apps/app/src/migrations/20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js → apps/app/src/migrations/20211227060705-revision-path-to-page-id-schema-migration--fixed-8998.js

@@ -13,11 +13,14 @@ import {
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory(
-  'growi:migrate:revision-path-to-page-id-schema-migration--fixed-7549',
+  'growi:migrate:revision-path-to-page-id-schema-migration--fixed-8998',
 );
 
 const LIMIT = 300;
 
+/**
+ * @see https://dev.growi.org/69301054963f68dfcf2b7111
+ */
 module.exports = {
   // path => pageId
   async up(db, client) {

+ 0 - 3
apps/app/src/pages/basic-layout-page/get-server-side-props/sidebar-configurations.ts

@@ -17,9 +17,6 @@ export const getServerSideSidebarConfigProps: GetServerSideProps<
         isSidebarCollapsedMode: configManager.getConfig(
           'customize:isSidebarCollapsedMode',
         ),
-        isSidebarClosedAtDockMode: configManager.getConfig(
-          'customize:isSidebarClosedAtDockMode',
-        ),
       },
     },
   };

+ 4 - 18
apps/app/src/server/routes/apiv3/customize-setting.js

@@ -173,9 +173,6 @@ const router = express.Router();
  *          isSidebarCollapsedMode:
  *            type: boolean
  *            description: The flag whether sidebar is collapsed mode or not.
- *          isSidebarClosedAtDockMode:
- *            type: boolean
- *            description: The flag whether sidebar is closed at dock mode or not.
  *      CustomizePresentation:
  *        description: Customize Presentation
  *        type: object
@@ -206,10 +203,7 @@ module.exports = (crowi) => {
   const validator = {
     layout: [body('isContainerFluid').isBoolean()],
     theme: [body('theme').isString()],
-    sidebar: [
-      body('isSidebarCollapsedMode').isBoolean(),
-      body('isSidebarClosedAtDockMode').optional().isBoolean(),
-    ],
+    sidebar: [body('isSidebarCollapsedMode').isBoolean()],
     function: [
       body('isEnabledTimeline').isBoolean(),
       body('isEnabledAttachTitleHeader').isBoolean(),
@@ -560,13 +554,10 @@ module.exports = (crowi) => {
     adminRequired,
     async (req, res) => {
       try {
-        const isSidebarCollapsedMode = await configManager.getConfig(
+        const isSidebarCollapsedMode = configManager.getConfig(
           'customize:isSidebarCollapsedMode',
         );
-        const isSidebarClosedAtDockMode = await configManager.getConfig(
-          'customize:isSidebarClosedAtDockMode',
-        );
-        return res.apiv3({ isSidebarCollapsedMode, isSidebarClosedAtDockMode });
+        return res.apiv3({ isSidebarCollapsedMode });
       } catch (err) {
         const msg = 'Error occurred in getting sidebar';
         logger.error('Error', err);
@@ -613,19 +604,14 @@ module.exports = (crowi) => {
     async (req, res) => {
       const requestParams = {
         'customize:isSidebarCollapsedMode': req.body.isSidebarCollapsedMode,
-        'customize:isSidebarClosedAtDockMode':
-          req.body.isSidebarClosedAtDockMode,
       };
 
       try {
         await configManager.updateConfigs(requestParams);
         const customizedParams = {
-          isSidebarCollapsedMode: await configManager.getConfig(
+          isSidebarCollapsedMode: configManager.getConfig(
             'customize:isSidebarCollapsedMode',
           ),
-          isSidebarClosedAtDockMode: await configManager.getConfig(
-            'customize:isSidebarClosedAtDockMode',
-          ),
         };
 
         activityEvent.emit('update', res.locals.activity._id, {

+ 1 - 1
apps/app/src/server/routes/apiv3/page/index.ts

@@ -1041,7 +1041,7 @@ module.exports = (crowi: Crowi) => {
         return res.apiv3Err(err, 500);
       }
 
-      // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js'
+      // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js' provided by v6.1.0 - v7.0.15
       try {
         await normalizeLatestRevisionIfBroken(pageId);
       } catch (err) {

+ 1 - 1
apps/app/src/server/routes/apiv3/page/update-page.ts

@@ -238,7 +238,7 @@ export const updatePageHandlersFactory: UpdatePageHandlersFactory = (crowi) => {
       }
 
       if (currentPage != null) {
-        // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js'
+        // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js' provided by v6.1.0 - v7.0.15
         try {
           await normalizeLatestRevisionIfBroken(pageId);
         } catch (err) {

+ 7 - 26
apps/app/src/server/routes/apiv3/revisions.js

@@ -2,11 +2,13 @@ import { SCOPE } from '@growi/core/dist/interfaces';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import express from 'express';
-import { connection } from 'mongoose';
 
 import { accessTokenParser } from '~/server/middlewares/access-token-parser';
 import { Revision } from '~/server/models/revision';
-import { normalizeLatestRevisionIfBroken } from '~/server/service/revision/normalize-latest-revision-if-broken';
+import {
+  getAppliedAtForRevisionFilter,
+  normalizeLatestRevisionIfBroken,
+} from '~/server/service/revision/normalize-latest-revision-if-broken';
 import loggerFactory from '~/utils/logger';
 
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
@@ -17,9 +19,6 @@ const { query, param } = require('express-validator');
 
 const router = express.Router();
 
-const MIGRATION_FILE_NAME =
-  '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549';
-
 /**
  * @swagger
  * components:
@@ -85,24 +84,6 @@ module.exports = (crowi) => {
     ],
   };
 
-  let cachedAppliedAt = null;
-
-  const getAppliedAtOfTheMigrationFile = async () => {
-    if (cachedAppliedAt != null) {
-      return cachedAppliedAt;
-    }
-
-    const migrationCollection = connection.collection('migrations');
-    const migration = await migrationCollection.findOne({
-      fileName: { $regex: `^${MIGRATION_FILE_NAME}` },
-    });
-    const appliedAt = migration.appliedAt;
-
-    cachedAppliedAt = appliedAt;
-
-    return appliedAt;
-  };
-
   /**
    * @swagger
    *
@@ -176,7 +157,7 @@ module.exports = (crowi) => {
         );
       }
 
-      // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js'
+      // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js' provided by v6.1.0 - v7.0.15
       try {
         await normalizeLatestRevisionIfBroken(pageId);
       } catch (err) {
@@ -186,7 +167,7 @@ module.exports = (crowi) => {
       try {
         const page = await Page.findOne({ _id: pageId });
 
-        const appliedAt = await getAppliedAtOfTheMigrationFile();
+        const appliedAt = await getAppliedAtForRevisionFilter();
 
         const queryOpts = {
           offset,
@@ -202,7 +183,7 @@ module.exports = (crowi) => {
 
         const queryCondition = {
           pageId: page._id,
-          createdAt: { $gt: appliedAt },
+          ...(appliedAt != null && { createdAt: { $gt: appliedAt } }),
         };
 
         // https://redmine.weseek.co.jp/issues/151652

+ 0 - 5
apps/app/src/server/service/config-manager/config-definition.ts

@@ -223,7 +223,6 @@ export const CONFIG_KEYS = [
   'customize:showPageSideAuthors',
   'customize:isEnabledMarp',
   'customize:isSidebarCollapsedMode',
-  'customize:isSidebarClosedAtDockMode',
 
   // Markdown Settings
   'markdown:xss:tagWhitelist',
@@ -1006,10 +1005,6 @@ export const CONFIG_DEFINITIONS = {
   'customize:isSidebarCollapsedMode': defineConfig<boolean>({
     defaultValue: false,
   }),
-  'customize:isSidebarClosedAtDockMode': defineConfig<boolean>({
-    defaultValue: false,
-  }),
-
   // Markdown Settings
   'markdown:xss:tagWhitelist': defineConfig<string[]>({
     defaultValue: [],

+ 117 - 2
apps/app/src/server/service/revision/normalize-latest-revision-if-broken.integ.ts

@@ -1,16 +1,37 @@
 import { getIdStringForRef } from '@growi/core';
 import type { HydratedDocument } from 'mongoose';
-import mongoose, { Types } from 'mongoose';
+import mongoose, { connection, Types } from 'mongoose';
 
 import type { PageDocument, PageModel } from '~/server/models/page';
 import PageModelFactory from '~/server/models/page';
 import { Revision } from '~/server/models/revision';
 
-import { normalizeLatestRevisionIfBroken } from './normalize-latest-revision-if-broken';
+import {
+  __resetCacheForTesting,
+  getAppliedAtForRevisionFilter,
+  normalizeLatestRevisionIfBroken,
+} from './normalize-latest-revision-if-broken';
+
+const OLD_MIGRATION_FILE_NAME =
+  '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549';
+const NEW_MIGRATION_FILE_NAME =
+  '20211227060705-revision-path-to-page-id-schema-migration--fixed-8998';
 
 describe('normalizeLatestRevisionIfBroken', () => {
   beforeAll(async () => {
     await PageModelFactory(null);
+    // Insert migration record to simulate affected instance
+    const migrationCollection = connection.collection('migrations');
+    await migrationCollection.insertOne({
+      fileName: OLD_MIGRATION_FILE_NAME,
+      appliedAt: new Date('2024-01-01'),
+    });
+  });
+
+  afterAll(async () => {
+    // Clean up migration record
+    const migrationCollection = connection.collection('migrations');
+    await migrationCollection.deleteOne({ fileName: OLD_MIGRATION_FILE_NAME });
   });
 
   test('should update the latest revision', async () => {
@@ -131,3 +152,97 @@ describe('normalizeLatestRevisionIfBroken', () => {
     });
   });
 });
+
+describe('getAppliedAtForRevisionFilter', () => {
+  const migrationCollection = () => connection.collection('migrations');
+
+  beforeEach(() => {
+    __resetCacheForTesting();
+  });
+
+  afterEach(async () => {
+    // Clean up all migration records
+    await migrationCollection().deleteMany({
+      fileName: { $in: [OLD_MIGRATION_FILE_NAME, NEW_MIGRATION_FILE_NAME] },
+    });
+  });
+
+  test('should return null when only new migration exists (fresh installation)', async () => {
+    // Arrange
+    await migrationCollection().insertOne({
+      fileName: NEW_MIGRATION_FILE_NAME,
+      appliedAt: new Date('2024-06-01'),
+    });
+
+    // Act
+    const result = await getAppliedAtForRevisionFilter();
+
+    // Assert
+    expect(result).toBeNull();
+  });
+
+  test('should return appliedAt when old migration exists (affected instance)', async () => {
+    // Arrange
+    const appliedAt = new Date('2024-01-01');
+    await migrationCollection().insertOne({
+      fileName: OLD_MIGRATION_FILE_NAME,
+      appliedAt,
+    });
+
+    // Act
+    const result = await getAppliedAtForRevisionFilter();
+
+    // Assert
+    expect(result).toEqual(appliedAt);
+  });
+
+  test('should return appliedAt when both migrations exist (upgraded instance)', async () => {
+    // Arrange
+    const oldAppliedAt = new Date('2024-01-01');
+    await migrationCollection().insertOne({
+      fileName: OLD_MIGRATION_FILE_NAME,
+      appliedAt: oldAppliedAt,
+    });
+    await migrationCollection().insertOne({
+      fileName: NEW_MIGRATION_FILE_NAME,
+      appliedAt: new Date('2024-06-01'),
+    });
+
+    // Act
+    const result = await getAppliedAtForRevisionFilter();
+
+    // Assert
+    expect(result).toEqual(oldAppliedAt);
+  });
+
+  test('should return null when neither migration exists', async () => {
+    // Arrange - no migrations inserted
+
+    // Act
+    const result = await getAppliedAtForRevisionFilter();
+
+    // Assert
+    expect(result).toBeNull();
+  });
+
+  test('should cache the result', async () => {
+    // Arrange
+    const appliedAt = new Date('2024-01-01');
+    await migrationCollection().insertOne({
+      fileName: OLD_MIGRATION_FILE_NAME,
+      appliedAt,
+    });
+
+    // Act - call twice
+    const result1 = await getAppliedAtForRevisionFilter();
+    // Remove the migration record
+    await migrationCollection().deleteOne({
+      fileName: OLD_MIGRATION_FILE_NAME,
+    });
+    const result2 = await getAppliedAtForRevisionFilter();
+
+    // Assert - both should return the same cached value
+    expect(result1).toEqual(appliedAt);
+    expect(result2).toEqual(appliedAt);
+  });
+});

+ 87 - 1
apps/app/src/server/service/revision/normalize-latest-revision-if-broken.ts

@@ -9,14 +9,100 @@ const logger = loggerFactory(
   'growi:service:revision:normalize-latest-revision',
 );
 
+const OLD_MIGRATION_FILE_NAME =
+  '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549';
+const NEW_MIGRATION_FILE_NAME =
+  '20211227060705-revision-path-to-page-id-schema-migration--fixed-8998';
+
+let cachedAppliedAt: Date | null | undefined;
+let cachedIsAffected: boolean | undefined;
+
+/**
+ * Reset the cache for testing purposes.
+ * @internal This function is only for testing.
+ */
+export const __resetCacheForTesting = (): void => {
+  cachedAppliedAt = undefined;
+  cachedIsAffected = undefined;
+};
+
 /**
- * Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js'
+ * Check if this instance went through the problematic migration (v6.1.0 - v7.0.15).
+ *
+ * Condition logic:
+ * - If old migration (fixed-7549) does NOT exist AND new migration (fixed-8998) exists
+ *   → Return false (fresh installation, not affected)
+ * - If old migration (fixed-7549) exists
+ *   → Return true (went through problematic version, affected)
+ * - If neither migration exists
+ *   → Log warning and return false (not affected)
+ *
+ * @returns true if affected by the problematic migration, false otherwise
+ *
+ * @see https://dev.growi.org/69301054963f68dfcf2b7111
+ */
+const isAffectedByProblematicMigration = async (): Promise<boolean> => {
+  if (cachedIsAffected !== undefined) {
+    return cachedIsAffected;
+  }
+
+  const migrationCollection = mongoose.connection.collection('migrations');
+
+  const oldMigration = await migrationCollection.findOne({
+    fileName: { $regex: `^${OLD_MIGRATION_FILE_NAME}` },
+  });
+  const newMigration = await migrationCollection.findOne({
+    fileName: { $regex: `^${NEW_MIGRATION_FILE_NAME}` },
+  });
+
+  // Case: fresh installation (new migration only) → not affected
+  if (oldMigration == null && newMigration != null) {
+    cachedIsAffected = false;
+    cachedAppliedAt = null;
+    return false;
+  }
+
+  // Case: went through problematic version (old migration exists) → affected
+  if (oldMigration != null) {
+    cachedIsAffected = true;
+    cachedAppliedAt = oldMigration.appliedAt;
+    return true;
+  }
+
+  // Case: neither migration exists (unexpected, but handle gracefully) → not affected
+  logger.warn(
+    'Neither old nor new migration file found in migrations collection. This may indicate an incomplete migration state.',
+  );
+  cachedIsAffected = false;
+  cachedAppliedAt = null;
+  return false;
+};
+
+/**
+ * Get the appliedAt date for filtering revisions created before the problematic migration.
+ *
+ * @returns appliedAt date to filter revisions, or null if no filter is needed
+ *
+ * @see https://dev.growi.org/69301054963f68dfcf2b7111
+ */
+export const getAppliedAtForRevisionFilter = async (): Promise<Date | null> => {
+  await isAffectedByProblematicMigration();
+  return cachedAppliedAt ?? null;
+};
+
+/**
+ * Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js' provided by v6.1.0 - v7.0.15
  *
  * @ref https://github.com/growilabs/growi/pull/8998
  */
 export const normalizeLatestRevisionIfBroken = async (
   pageId: string | Types.ObjectId,
 ): Promise<void> => {
+  // Skip if not affected by the problematic migration
+  if (!(await isAffectedByProblematicMigration())) {
+    return;
+  }
+
   if (await Revision.exists({ pageId: { $eq: pageId } })) {
     return;
   }

+ 1 - 1
apps/app/src/server/service/yjs/sync-ydoc.ts

@@ -27,7 +27,7 @@ export const syncYDoc = async (
 ): Promise<void> => {
   const pageId = doc.name;
 
-  // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js'
+  // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js' provided by v6.1.0 - v7.0.15
   await normalizeLatestRevisionIfBroken(pageId);
 
   const revision = await Revision.findOne(

+ 1 - 1
apps/app/src/server/service/yjs/yjs.ts

@@ -143,7 +143,7 @@ class YjsService implements IYjsService {
       );
     };
 
-    // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js'
+    // Normalize the latest revision which was borken by the migration script '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549.js' provided by v6.1.0 - v7.0.15
     await normalizeLatestRevisionIfBroken(pageId);
 
     // get the latest revision createdAt

+ 0 - 17
apps/app/src/stores/admin/sidebar-config.tsx

@@ -7,11 +7,7 @@ import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 
 type SidebarConfigOption = {
   update: () => Promise<void>;
-
   setIsSidebarCollapsedMode: (isSidebarCollapsedMode: boolean) => void;
-  setIsSidebarClosedAtDockMode: (
-    isSidebarClosedAtDockMode: boolean | undefined,
-  ) => void;
 };
 
 export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> &
@@ -51,18 +47,5 @@ export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> &
       },
       [mutate],
     ),
-
-    setIsSidebarClosedAtDockMode: useCallback(
-      (isSidebarClosedAtDockMode) => {
-        // update isSidebarClosedAtDockMode in cache, not revalidate
-        mutate((prevData) => {
-          if (prevData == null) {
-            return;
-          }
-          return { ...prevData, isSidebarClosedAtDockMode };
-        }, false);
-      },
-      [mutate],
-    ),
   };
 };