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

Merge branch 'master' into imprv/153641-recent-changes-update-on-create-update-delete

nHigashiWeseek 1 год назад
Родитель
Сommit
85a0b5b8b9
33 измененных файлов с 202 добавлено и 109 удалено
  1. 4 12
      .github/mergify.yml
  2. 2 2
      .github/workflows/ci-app.yml
  3. 25 1
      CHANGELOG.md
  4. 1 1
      apps/app/docker/README.md
  5. 2 2
      apps/app/package.json
  6. 3 3
      apps/app/src/client/components/Admin/G2GDataTransferExportForm.tsx
  7. 1 1
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx
  8. 1 1
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx
  9. 2 1
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  10. 6 2
      apps/app/src/client/components/PageComment.tsx
  11. 1 1
      apps/app/src/client/components/PageComment/ReplyComments.tsx
  12. 0 14
      apps/app/src/models/admin/growi-archive-import-option.js
  13. 18 0
      apps/app/src/models/admin/growi-archive-import-option.ts
  14. 0 0
      apps/app/src/models/admin/import-mode.ts
  15. 5 3
      apps/app/src/models/admin/import-option-for-pages.ts
  16. 0 13
      apps/app/src/models/admin/import-option-for-revisions.js
  17. 15 0
      apps/app/src/models/admin/import-option-for-revisions.ts
  18. 8 4
      apps/app/src/pages/[[...path]].page.tsx
  19. 1 1
      apps/app/src/server/routes/apiv3/import.js
  20. 4 3
      apps/app/src/server/routes/comment.js
  21. 5 4
      apps/app/src/server/service/g2g-transfer.ts
  22. 2 1
      apps/app/src/server/service/import/import-settings.ts
  23. 5 7
      apps/app/src/server/service/import/import.ts
  24. 0 2
      apps/app/src/server/service/import/index.ts
  25. 1 1
      apps/app/src/server/service/import/overwrite-params/index.ts
  26. 12 0
      apps/app/src/stores/page.tsx
  27. 3 2
      apps/app/turbo.json
  28. 1 1
      apps/slackbot-proxy/package.json
  29. 3 1
      apps/slackbot-proxy/src/services/LinkSharedService.ts
  30. 42 0
      apps/slackbot-proxy/turbo.json
  31. 1 1
      package.json
  32. 0 21
      turbo.json
  33. 28 3
      yarn.lock

+ 4 - 12
.github/mergify.yml

@@ -3,26 +3,18 @@ queue_rules:
     allow_inplace_checks: false
     allow_inplace_checks: false
     queue_conditions:
     queue_conditions:
       - '#check-failure = 0'
       - '#check-failure = 0'
-      - check-success = "check-title"
-      - or:
-        - and:
-            - check-success ~= ci-slackbot-proxy-
-        - and:
-            - check-success ~= ci-app-
+      - check-success ~= ci-app-
     merge_conditions:
     merge_conditions:
       - '#check-failure = 0'
       - '#check-failure = 0'
-      - or:
-        - and:
-            - check-success ~= ci-slackbot-proxy-
-        - and:
-            - check-success ~= ci-app-
-            - check-success ~= test-prod-node20 /
+      - check-success ~= ci-app-
+      - check-success ~= test-prod-node20 /
 
 
 pull_request_rules:
 pull_request_rules:
   - name: Automatic queue to merge
   - name: Automatic queue to merge
     conditions:
     conditions:
       - '#approved-reviews-by >= 1'
       - '#approved-reviews-by >= 1'
       - '#review-requested = 0'
       - '#review-requested = 0'
+      - check-success = "check-title"
     actions:
     actions:
       queue:
       queue:
 
 

+ 2 - 2
.github/workflows/ci-app.yml

@@ -206,11 +206,11 @@ jobs:
           yarn global add node-gyp
           yarn global add node-gyp
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
-      - name: turbo run dev:ci
+      - name: turbo run launch-dev:ci
         working-directory: ./apps/app
         working-directory: ./apps/app
         run: |
         run: |
           cp config/ci/.env.local.for-ci .env.development.local
           cp config/ci/.env.local.for-ci .env.development.local
-          turbo run dev:ci --env-mode=loose
+          turbo run launch-dev:ci --env-mode=loose
         env:
         env:
           MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi_dev
           MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi_dev
 
 

+ 25 - 1
CHANGELOG.md

@@ -1,9 +1,33 @@
 # Changelog
 # Changelog
 
 
-## [Unreleased](https://github.com/weseek/growi/compare/v7.0.17...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v7.0.18...HEAD)
 
 
 *Please do not manually update this file. We've automated the process.*
 *Please do not manually update this file. We've automated the process.*
 
 
+## [v7.0.18](https://github.com/weseek/growi/compare/v7.0.17...v7.0.18) - 2024-09-09
+
+### 🚀 Improvement
+
+* imprv: Prevent looping to update a hook for TrashPageAlert (#9066) @yuki-takei
+* imprv: Display page tree in page select modal with scrollbar (#9023) @kazutoweseek
+
+### 🐛 Bug Fixes
+
+* fix: issue that material symbols icons are not displayed in ReplyComments component (#9076) @WNomunomu
+* fix: Unable to navigate to the data transfer page (#9071) @miya
+* fix: Page content does not update when switching revisions (#9072) @miya
+* fix: Supress rendering too many invisible DropdownMenu components (#9073) @yuki-takei
+* fix: Return error when grant is string for PUT /_api/v3/page (#9069) @arafubeatbox
+* fix: Scrolling may not occurs when clicking on the edit button next to the header (#9043) @reiji-h
+* fix: API v3 Page update (#9053) @maeshinshin
+* fix: Input text becomes empty when opening the ReadOnlyEditor (#9059) @miya
+* fix: Show pages with grants that are set to be visible in security settings on RecentChanges and PageTree as well (#9044) @miya
+
+### 🧰 Maintenance
+
+* support: Omit Cypress (#9065) @miya
+* ci(deps): bump unzip-stream from 0.3.1 to 0.3.2 (#9049) @dependabot
+
 ## [v7.0.17](https://github.com/weseek/growi/compare/v7.0.16...v7.0.17) - 2024-08-26
 ## [v7.0.17](https://github.com/weseek/growi/compare/v7.0.16...v7.0.17) - 2024-08-26
 
 
 ### 🚀 Improvement
 ### 🚀 Improvement

+ 1 - 1
apps/app/docker/README.md

@@ -10,7 +10,7 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 ------------------------------------------------
 
 
-* [`7.0.17`, `7.0`, `7`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v7.0.17/apps/app/docker/Dockerfile)
+* [`7.0.18`, `7.0`, `7`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v7.0.18/apps/app/docker/Dockerfile)
 * [`6.3.2`, `6.3`, `6` (Dockerfile)](https://github.com/weseek/growi/blob/v6.3.2/apps/app/docker/Dockerfile)
 * [`6.3.2`, `6.3`, `6` (Dockerfile)](https://github.com/weseek/growi/blob/v6.3.2/apps/app/docker/Dockerfile)
 * [`6.2.4`, `6.2` (Dockerfile)](https://github.com/weseek/growi/blob/v6.2.4/apps/app/docker/Dockerfile)
 * [`6.2.4`, `6.2` (Dockerfile)](https://github.com/weseek/growi/blob/v6.2.4/apps/app/docker/Dockerfile)
 * [`6.1.15`, `6.1` (Dockerfile)](https://github.com/weseek/growi/blob/v6.1.15/apps/app/docker/Dockerfile)
 * [`6.1.15`, `6.1` (Dockerfile)](https://github.com/weseek/growi/blob/v6.1.15/apps/app/docker/Dockerfile)

+ 2 - 2
apps/app/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/app",
   "name": "@growi/app",
-  "version": "7.0.18-RC.0",
+  "version": "7.0.19-RC.0",
   "license": "MIT",
   "license": "MIT",
   "private": "true",
   "private": "true",
   "scripts": {
   "scripts": {
@@ -26,7 +26,7 @@
     "dev:migrate:up": "yarn dev:migrate-mongo up -f config/migrate-mongo-config.js",
     "dev:migrate:up": "yarn dev:migrate-mongo up -f config/migrate-mongo-config.js",
     "dev:migrate:down": "yarn dev:migrate-mongo down -f config/migrate-mongo-config.js",
     "dev:migrate:down": "yarn dev:migrate-mongo down -f config/migrate-mongo-config.js",
     "//// for CI": "",
     "//// for CI": "",
-    "dev:ci": "yarn cross-env NODE_ENV=development yarn ts-node src/server/app.ts --ci",
+    "launch-dev:ci": "yarn cross-env NODE_ENV=development yarn dev:migrate && yarn ts-node src/server/app.ts --ci",
     "lint:typecheck": "npx -y tspc",
     "lint:typecheck": "npx -y tspc",
     "lint:eslint": "yarn eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
     "lint:eslint": "yarn eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
     "lint:styles": "stylelint \"src/**/*.scss\"",
     "lint:styles": "stylelint \"src/**/*.scss\"",

+ 3 - 3
apps/app/src/client/components/Admin/G2GDataTransferExportForm.tsx

@@ -4,7 +4,7 @@ import React, {
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
+import { GrowiArchiveImportOption } from '~/models/admin/growi-archive-import-option';
 import { ImportOptionForPages } from '~/models/admin/import-option-for-pages';
 import { ImportOptionForPages } from '~/models/admin/import-option-for-pages';
 import { ImportOptionForRevisions } from '~/models/admin/import-option-for-revisions';
 import { ImportOptionForRevisions } from '~/models/admin/import-option-for-revisions';
 
 
@@ -22,7 +22,7 @@ const GROUPS_CONFIG = [
 ];
 ];
 const ALL_GROUPED_COLLECTIONS = GROUPS_PAGE.concat(GROUPS_USER).concat(GROUPS_CONFIG);
 const ALL_GROUPED_COLLECTIONS = GROUPS_PAGE.concat(GROUPS_USER).concat(GROUPS_CONFIG);
 
 
-const IMPORT_OPTION_CLASS_MAPPING = {
+const IMPORT_OPTION_CLASS_MAPPING: Record<string, typeof GrowiArchiveImportOption> = {
   pages: ImportOptionForPages,
   pages: ImportOptionForPages,
   revisions: ImportOptionForRevisions,
   revisions: ImportOptionForRevisions,
 };
 };
@@ -188,7 +188,7 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
         ? MODE_RESTRICTED_COLLECTION[collectionName][0]
         ? MODE_RESTRICTED_COLLECTION[collectionName][0]
         : DEFAULT_MODE;
         : DEFAULT_MODE;
       const ImportOption = IMPORT_OPTION_CLASS_MAPPING[collectionName] || GrowiArchiveImportOption;
       const ImportOption = IMPORT_OPTION_CLASS_MAPPING[collectionName] || GrowiArchiveImportOption;
-      initialOptionsMap[collectionName] = new ImportOption(initialMode);
+      initialOptionsMap[collectionName] = new ImportOption(collectionName, initialMode);
     });
     });
     updateOptionsMap(initialOptionsMap);
     updateOptionsMap(initialOptionsMap);
   }, [allCollectionNames, updateOptionsMap]);
   }, [allCollectionNames, updateOptionsMap]);

+ 1 - 1
apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx

@@ -11,7 +11,7 @@ import {
   ModalFooter,
   ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
+import { GrowiArchiveImportOption } from '~/models/admin/growi-archive-import-option';
 
 
 // import { toastSuccess, toastError } from '~/client/util/toastr';
 // import { toastSuccess, toastError } from '~/client/util/toastr';
 
 

+ 1 - 1
apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { Progress } from 'reactstrap';
 import { Progress } from 'reactstrap';
 
 
-import GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
+import { GrowiArchiveImportOption } from '~/models/admin/growi-archive-import-option';
 
 
 
 
 const MODE_ATTR_MAP = {
 const MODE_ATTR_MAP = {

+ 2 - 1
apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
 
 
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
+import { GrowiArchiveImportOption } from '~/models/admin/growi-archive-import-option';
 import { ImportOptionForPages } from '~/models/admin/import-option-for-pages';
 import { ImportOptionForPages } from '~/models/admin/import-option-for-pages';
 import { ImportOptionForRevisions } from '~/models/admin/import-option-for-revisions';
 import { ImportOptionForRevisions } from '~/models/admin/import-option-for-revisions';
 import { useAdminSocket } from '~/stores/socket-io';
 import { useAdminSocket } from '~/stores/socket-io';
@@ -27,6 +27,7 @@ const GROUPS_CONFIG = [
 ];
 ];
 const ALL_GROUPED_COLLECTIONS = GROUPS_PAGE.concat(GROUPS_USER).concat(GROUPS_CONFIG);
 const ALL_GROUPED_COLLECTIONS = GROUPS_PAGE.concat(GROUPS_USER).concat(GROUPS_CONFIG);
 
 
+/** @type Record<string, typeof GrowiArchiveImportOption> */
 const IMPORT_OPTION_CLASS_MAPPING = {
 const IMPORT_OPTION_CLASS_MAPPING = {
   pages: ImportOptionForPages,
   pages: ImportOptionForPages,
   revisions: ImportOptionForRevisions,
   revisions: ImportOptionForRevisions,

+ 6 - 2
apps/app/src/client/components/PageComment.tsx

@@ -93,8 +93,12 @@ export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps):
       onDeleteCommentAfterOperation();
       onDeleteCommentAfterOperation();
     }
     }
     catch (error: unknown) {
     catch (error: unknown) {
-      setErrorMessageOnDelete(error as string);
-      toastError(`error: ${error}`);
+      const message = error instanceof Error
+        ? error.message
+        : (error as any).toString();
+
+      setErrorMessageOnDelete(message);
+      toastError(message);
     }
     }
   }, [commentToBeDeleted, onDeleteCommentAfterOperation]);
   }, [commentToBeDeleted, onDeleteCommentAfterOperation]);
 
 

+ 1 - 1
apps/app/src/client/components/PageComment/ReplyComments.tsx

@@ -69,7 +69,7 @@ export const ReplyComments = (props: ReplycommentsProps): JSX.Element => {
 
 
   const areThereHiddenReplies = (replyList.length > 2);
   const areThereHiddenReplies = (replyList.length > 2);
   const toggleButtonIconName = isOlderRepliesShown ? 'expand_less' : 'more_vert';
   const toggleButtonIconName = isOlderRepliesShown ? 'expand_less' : 'more_vert';
-  const toggleButtonIcon = <span className="material-icons-outlined me-1">{toggleButtonIconName}</span>;
+  const toggleButtonIcon = <span className="material-symbols-outlined me-1">{toggleButtonIconName}</span>;
   const toggleButtonLabel = isOlderRepliesShown ? '' : 'more';
   const toggleButtonLabel = isOlderRepliesShown ? '' : 'more';
   const shownReplies = replyList.slice(replyList.length - 2, replyList.length);
   const shownReplies = replyList.slice(replyList.length - 2, replyList.length);
   const hiddenReplies = replyList.slice(0, replyList.length - 2);
   const hiddenReplies = replyList.slice(0, replyList.length - 2);

+ 0 - 14
apps/app/src/models/admin/growi-archive-import-option.js

@@ -1,14 +0,0 @@
-class GrowiArchiveImportOption {
-
-  constructor(collectionName, mode, initProps = {}) {
-    this.collectionName = collectionName;
-    this.mode = mode;
-
-    Object.entries(initProps).forEach(([key, value]) => {
-      this[key] = value;
-    });
-  }
-
-}
-
-module.exports = GrowiArchiveImportOption;

+ 18 - 0
apps/app/src/models/admin/growi-archive-import-option.ts

@@ -0,0 +1,18 @@
+import { ImportMode } from './import-mode';
+
+export class GrowiArchiveImportOption {
+
+  collectionName: string;
+
+  mode: ImportMode;
+
+  constructor(collectionName: string, mode: ImportMode = ImportMode.insert, initProps = {}) {
+    this.collectionName = collectionName;
+    this.mode = mode;
+
+    Object.entries(initProps).forEach(([key, value]) => {
+      this[key] = value;
+    });
+  }
+
+}

+ 0 - 0
apps/app/src/server/service/import/import-mode.ts → apps/app/src/models/admin/import-mode.ts


+ 5 - 3
apps/app/src/models/admin/import-option-for-pages.ts

@@ -1,4 +1,6 @@
-import GrowiArchiveImportOption from './growi-archive-import-option';
+import { ImportMode } from '~/models/admin/import-mode';
+
+import { GrowiArchiveImportOption } from './growi-archive-import-option';
 
 
 const DEFAULT_PROPS = {
 const DEFAULT_PROPS = {
   isOverwriteAuthorWithCurrentUser: false,
   isOverwriteAuthorWithCurrentUser: false,
@@ -20,8 +22,8 @@ export class ImportOptionForPages extends GrowiArchiveImportOption {
 
 
   initPageMetadatas;
   initPageMetadatas;
 
 
-  constructor(collectionName: string, mode: string, initProps) {
-    super(collectionName, mode, initProps || DEFAULT_PROPS);
+  constructor(collectionName: string, mode: ImportMode = ImportMode.insert, initProps = DEFAULT_PROPS) {
+    super(collectionName, mode, initProps);
   }
   }
 
 
 }
 }

+ 0 - 13
apps/app/src/models/admin/import-option-for-revisions.js

@@ -1,13 +0,0 @@
-const GrowiArchiveImportOption = require('./growi-archive-import-option');
-
-const DEFAULT_PROPS = {
-  isOverwriteAuthorWithCurrentUser: false,
-};
-
-export class ImportOptionForRevisions extends GrowiArchiveImportOption {
-
-  constructor(collectionName, mode, initProps) {
-    super(collectionName, mode, initProps || DEFAULT_PROPS);
-  }
-
-}

+ 15 - 0
apps/app/src/models/admin/import-option-for-revisions.ts

@@ -0,0 +1,15 @@
+import { ImportMode } from '~/models/admin/import-mode';
+
+import { GrowiArchiveImportOption } from './growi-archive-import-option';
+
+const DEFAULT_PROPS = {
+  isOverwriteAuthorWithCurrentUser: false,
+};
+
+export class ImportOptionForRevisions extends GrowiArchiveImportOption {
+
+  constructor(collectionName: string, mode: ImportMode = ImportMode.insert, initProps = DEFAULT_PROPS) {
+    super(collectionName, mode, initProps);
+  }
+
+}

+ 8 - 4
apps/app/src/pages/[[...path]].page.tsx

@@ -245,6 +245,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   const { pageWithMeta } = props;
   const { pageWithMeta } = props;
 
 
   const pageId = pageWithMeta?.data._id;
   const pageId = pageWithMeta?.data._id;
+  const revisionId = pageWithMeta?.data.revision?._id;
   const revisionBody = pageWithMeta?.data.revision?.body;
   const revisionBody = pageWithMeta?.data.revision?.body;
 
 
   useCurrentPathname(props.currentPathname);
   useCurrentPathname(props.currentPathname);
@@ -277,7 +278,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
       return;
       return;
     }
     }
 
 
-    if (currentPageId != null && !props.isNotFound) {
+    if (currentPageId != null && revisionId != null && !props.isNotFound) {
       const mutatePageData = async() => {
       const mutatePageData = async() => {
         const pageData = await mutateCurrentPage();
         const pageData = await mutateCurrentPage();
         mutateEditingMarkdown(pageData?.revision?.body);
         mutateEditingMarkdown(pageData?.revision?.body);
@@ -288,7 +289,10 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
       // Because pageWIthMeta does not contain revision.body
       // Because pageWIthMeta does not contain revision.body
       mutatePageData();
       mutatePageData();
     }
     }
-  }, [currentPageId, mutateCurrentPage, mutateCurrentPageYjsDataFromApi, mutateEditingMarkdown, props.isNotFound, props.skipSSR]);
+  }, [
+    revisionId, currentPageId, mutateCurrentPage,
+    mutateCurrentPageYjsDataFromApi, mutateEditingMarkdown, props.isNotFound, props.skipSSR,
+  ]);
 
 
   // sync pathname by Shallow Routing https://nextjs.org/docs/routing/shallow-routing
   // sync pathname by Shallow Routing https://nextjs.org/docs/routing/shallow-routing
   useEffect(() => {
   useEffect(() => {
@@ -308,8 +312,8 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   }, [mutateEditingMarkdown, revisionBody, props.currentPathname]);
   }, [mutateEditingMarkdown, revisionBody, props.currentPathname]);
 
 
   useEffect(() => {
   useEffect(() => {
-    mutateRemoteRevisionId(pageWithMeta?.data.revision?._id);
-  }, [mutateRemoteRevisionId, pageWithMeta?.data.revision?._id]);
+    mutateRemoteRevisionId(revisionId);
+  }, [mutateRemoteRevisionId, revisionId]);
 
 
   useEffect(() => {
   useEffect(() => {
     mutateCurrentPageId(pageId ?? null);
     mutateCurrentPageId(pageId ?? null);

+ 1 - 1
apps/app/src/server/routes/apiv3/import.js

@@ -268,7 +268,7 @@ export default function route(crowi) {
     const importSettingsMap = {};
     const importSettingsMap = {};
     fileStatsToImport.forEach(({ fileName, collectionName }) => {
     fileStatsToImport.forEach(({ fileName, collectionName }) => {
       // instanciate GrowiArchiveImportOption
       // instanciate GrowiArchiveImportOption
-      /** @type {GrowiArchiveImportOption} */
+      /** @type {import('~/models/admin/growi-archive-import-option').GrowiArchiveImportOption} */
       const option = options.find(opt => opt.collectionName === collectionName);
       const option = options.find(opt => opt.collectionName === collectionName);
 
 
       // generate options
       // generate options

+ 4 - 3
apps/app/src/server/routes/comment.js

@@ -1,4 +1,5 @@
 
 
+import { getIdStringForRef } from '@growi/core';
 import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 
 
 import { Comment, CommentEvent, commentEvent } from '~/features/comment/server';
 import { Comment, CommentEvent, commentEvent } from '~/features/comment/server';
@@ -56,7 +57,6 @@ module.exports = function(crowi, app) {
   const logger = loggerFactory('growi:routes:comment');
   const logger = loggerFactory('growi:routes:comment');
   const User = crowi.model('User');
   const User = crowi.model('User');
   const Page = crowi.model('Page');
   const Page = crowi.model('Page');
-  const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
   const ApiResponse = require('../util/apiResponse');
   const ApiResponse = require('../util/apiResponse');
 
 
   const activityEvent = crowi.event('activity');
   const activityEvent = crowi.event('activity');
@@ -465,6 +465,7 @@ module.exports = function(crowi, app) {
     }
     }
 
 
     try {
     try {
+      /** @type {import('mongoose').HydratedDocument<import('~/interfaces/comment').IComment>} */
       const comment = await Comment.findById(commentId).exec();
       const comment = await Comment.findById(commentId).exec();
 
 
       if (comment == null) {
       if (comment == null) {
@@ -472,12 +473,12 @@ module.exports = function(crowi, app) {
       }
       }
 
 
       // check whether accessible
       // check whether accessible
-      const pageId = comment.page;
+      const pageId = getIdStringForRef(comment.page);
       const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
       const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
       if (!isAccessible) {
       if (!isAccessible) {
         throw new Error('Current user is not accessible to this page.');
         throw new Error('Current user is not accessible to this page.');
       }
       }
-      if (req.user._id !== comment.creator.toString()) {
+      if (getIdStringForRef(req.user) !== getIdStringForRef(comment.creator)) {
         throw new Error('Current user is not operatable to this comment.');
         throw new Error('Current user is not operatable to this comment.');
       }
       }
 
 

+ 5 - 4
apps/app/src/server/service/g2g-transfer.ts

@@ -10,9 +10,10 @@ import FormData from 'form-data';
 import mongoose, { Types as MongooseTypes } from 'mongoose';
 import mongoose, { Types as MongooseTypes } from 'mongoose';
 
 
 import { G2G_PROGRESS_STATUS } from '~/interfaces/g2g-transfer';
 import { G2G_PROGRESS_STATUS } from '~/interfaces/g2g-transfer';
-import GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
+import { GrowiArchiveImportOption } from '~/models/admin/growi-archive-import-option';
+import { ImportMode } from '~/models/admin/import-mode';
 import TransferKeyModel from '~/server/models/transfer-key';
 import TransferKeyModel from '~/server/models/transfer-key';
-import { getImportService, ImportMode, type ImportSettings } from '~/server/service/import';
+import { getImportService, type ImportSettings } from '~/server/service/import';
 import { createBatchStream } from '~/server/util/batch-stream';
 import { createBatchStream } from '~/server/util/batch-stream';
 import axios from '~/utils/axios';
 import axios from '~/utils/axios';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -609,12 +610,12 @@ export class G2GTransferReceiverService implements Receiver {
   ): { [key: string]: ImportSettings; } {
   ): { [key: string]: ImportSettings; } {
     const importSettingsMap = {};
     const importSettingsMap = {};
     innerFileStats.forEach(({ fileName, collectionName }) => {
     innerFileStats.forEach(({ fileName, collectionName }) => {
-      const options = new GrowiArchiveImportOption(null, optionsMap[collectionName]);
+      const options = new GrowiArchiveImportOption(collectionName, undefined, optionsMap[collectionName]);
 
 
       if (collectionName === 'configs' && options.mode !== ImportMode.flushAndInsert) {
       if (collectionName === 'configs' && options.mode !== ImportMode.flushAndInsert) {
         throw new Error('`flushAndInsert` is only available as an import setting for configs collection');
         throw new Error('`flushAndInsert` is only available as an import setting for configs collection');
       }
       }
-      if (collectionName === 'pages' && options.mode === 'insert') {
+      if (collectionName === 'pages' && options.mode === ImportMode.insert) {
         throw new Error('`insert` is not available as an import setting for pages collection');
         throw new Error('`insert` is not available as an import setting for pages collection');
       }
       }
       if (collectionName === 'attachmentFiles.chunks') {
       if (collectionName === 'attachmentFiles.chunks') {

+ 2 - 1
apps/app/src/server/service/import/import-settings.ts

@@ -1,4 +1,5 @@
-import type { ImportMode } from './import-mode';
+import type { ImportMode } from '~/models/admin/import-mode';
+
 import type { OverwriteFunction } from './overwrite-function';
 import type { OverwriteFunction } from './overwrite-function';
 
 
 export type OverwriteParams = { [propertyName: string]: OverwriteFunction | unknown }
 export type OverwriteParams = { [propertyName: string]: OverwriteFunction | unknown }

+ 5 - 7
apps/app/src/server/service/import/import.ts

@@ -13,6 +13,7 @@ import mongoose from 'mongoose';
 import streamToPromise from 'stream-to-promise';
 import streamToPromise from 'stream-to-promise';
 import unzipStream from 'unzip-stream';
 import unzipStream from 'unzip-stream';
 
 
+import { ImportMode } from '~/models/admin/import-mode';
 import type Crowi from '~/server/crowi';
 import type Crowi from '~/server/crowi';
 import { setupIndependentModels } from '~/server/crowi/setup-models';
 import { setupIndependentModels } from '~/server/crowi/setup-models';
 import type CollectionProgress from '~/server/models/vo/collection-progress';
 import type CollectionProgress from '~/server/models/vo/collection-progress';
@@ -25,7 +26,6 @@ import { configManager } from '../config-manager';
 import type { ConvertMap } from './construct-convert-map';
 import type { ConvertMap } from './construct-convert-map';
 import { constructConvertMap } from './construct-convert-map';
 import { constructConvertMap } from './construct-convert-map';
 import { getModelFromCollectionName } from './get-model-from-collection-name';
 import { getModelFromCollectionName } from './get-model-from-collection-name';
-import { ImportMode } from './import-mode';
 import type { ImportSettings, OverwriteParams } from './import-settings';
 import type { ImportSettings, OverwriteParams } from './import-settings';
 import { keepOriginal } from './overwrite-function';
 import { keepOriginal } from './overwrite-function';
 
 
@@ -303,14 +303,12 @@ export class ImportService {
 
 
   /**
   /**
    * process bulk operation
    * process bulk operation
-   * @param {object} bulk MongoDB Bulk instance
-   * @param {string} collectionName collection name
-   * @param {object} document
-   * @param {ImportSettings} importSettings
+   * @param bulk MongoDB Bulk instance
+   * @param collectionName collection name
    */
    */
-  bulkOperate(bulk, collectionName, document, importSettings) {
+  bulkOperate(bulk, collectionName: string, document, importSettings: ImportSettings) {
     // insert
     // insert
-    if (importSettings.mode !== 'upsert') {
+    if (importSettings.mode !== ImportMode.upsert) {
       return bulk.insert(document);
       return bulk.insert(document);
     }
     }
 
 

+ 0 - 2
apps/app/src/server/service/import/index.ts

@@ -18,6 +18,4 @@ export const getImportService = (): ImportService => {
   return instance;
   return instance;
 };
 };
 
 
-
-export * from './import-mode';
 export * from './import-settings';
 export * from './import-settings';

+ 1 - 1
apps/app/src/server/service/import/overwrite-params/index.ts

@@ -1,4 +1,4 @@
-import type GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
+import type { GrowiArchiveImportOption } from '~/models/admin/growi-archive-import-option';
 import { isImportOptionForPages } from '~/models/admin/import-option-for-pages';
 import { isImportOptionForPages } from '~/models/admin/import-option-for-pages';
 
 
 import type { OverwriteParams } from '../import-settings';
 import type { OverwriteParams } from '../import-settings';

+ 12 - 0
apps/app/src/stores/page.tsx

@@ -57,10 +57,14 @@ export const useTemplateBodyData = (initialData?: string): SWRResponse<string, E
 export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|null): SWRResponse<IPagePopulatedToShowRevision|null> => {
 export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|null): SWRResponse<IPagePopulatedToShowRevision|null> => {
   const key = 'currentPage';
   const key = 'currentPage';
 
 
+  const { data: isLatestRevision } = useIsLatestRevision();
+
   const { cache } = useSWRConfig();
   const { cache } = useSWRConfig();
 
 
   // Problem 1: https://github.com/weseek/growi/pull/7772/files#diff-4c1708c4f959974166c15435c6b35950ba01bbf35e7e4b8e99efeb125a8000a7
   // Problem 1: https://github.com/weseek/growi/pull/7772/files#diff-4c1708c4f959974166c15435c6b35950ba01bbf35e7e4b8e99efeb125a8000a7
   // Problem 2: https://redmine.weseek.co.jp/issues/141027
   // Problem 2: https://redmine.weseek.co.jp/issues/141027
+  // Problem 3: https://redmine.weseek.co.jp/issues/153618
+  // Problem 4: https://redmine.weseek.co.jp/issues/153759
   const shouldMutate = (() => {
   const shouldMutate = (() => {
     if (initialData === undefined) {
     if (initialData === undefined) {
       return false;
       return false;
@@ -81,6 +85,14 @@ export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|nu
       return true;
       return true;
     }
     }
 
 
+    // mutate when opening a previous revision.
+    if (!isLatestRevision
+        && cachedData.revision?._id != null && initialData.revision?._id != null
+        && cachedData.revision._id !== initialData.revision._id
+    ) {
+      return true;
+    }
+
     return false;
     return false;
   })();
   })();
 
 

+ 3 - 2
apps/app/turbo.json

@@ -49,8 +49,9 @@
       "cache": false,
       "cache": false,
       "persistent": true
       "persistent": true
     },
     },
-    "dev:ci": {
-      "dependsOn": ["^dev", "dev:migrate", "dev:styles-prebuilt"],
+
+    "launch-dev:ci": {
+      "dependsOn": ["^dev", "dev:styles-prebuilt"],
       "cache": false
       "cache": false
     },
     },
 
 

+ 1 - 1
apps/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/slackbot-proxy",
   "name": "@growi/slackbot-proxy",
-  "version": "7.0.18-slackbot-proxy.0",
+  "version": "7.0.19-slackbot-proxy.0",
   "license": "MIT",
   "license": "MIT",
   "private": "true",
   "private": "true",
   "scripts": {
   "scripts": {

+ 3 - 1
apps/slackbot-proxy/src/services/LinkSharedService.ts

@@ -3,7 +3,9 @@ import type { WebClient } from '@slack/web-api';
 import { Inject, Service } from '@tsed/di';
 import { Inject, Service } from '@tsed/di';
 import axios from 'axios';
 import axios from 'axios';
 
 
-import type { RelationRepository } from '~/repositories/relation';
+// needed to import class (not type) for injection
+// eslint-disable-next-line @typescript-eslint/consistent-type-imports
+import { RelationRepository } from '~/repositories/relation';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('slackbot-proxy:services:LinkSharedService');
 const logger = loggerFactory('slackbot-proxy:services:LinkSharedService');

+ 42 - 0
apps/slackbot-proxy/turbo.json

@@ -0,0 +1,42 @@
+{
+  "$schema": "https://turbo.build/schema.json",
+  "extends": ["//"],
+  "tasks": {
+
+    "clean": {
+      "dependsOn": ["@growi/slack#clean"],
+      "cache": false
+    },
+
+    "build": {
+      "dependsOn": ["@growi/slack#build"],
+      "outputs": ["dist/**"],
+      "outputLogs": "new-only"
+    },
+
+    "dev": {
+      "dependsOn": ["@growi/slack#dev"],
+      "cache": false,
+      "persistent": true
+    },
+    "dev:ci": {
+      "dependsOn": ["@growi/slack#dev"],
+      "cache": false
+    },
+
+    "lint": {
+      "dependsOn": ["@growi/slack#dev"]
+    },
+
+    "test": {
+      "dependsOn": ["@growi/slack#dev"],
+      "outputLogs": "new-only"
+    },
+
+    "version": {
+      "cache": false,
+      "dependsOn": ["^version", "//#version"]
+    }
+
+  }
+}

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "growi",
   "name": "growi",
-  "version": "7.0.18-RC.0",
+  "version": "7.0.19-RC.0",
   "description": "Team collaboration software using markdown",
   "description": "Team collaboration software using markdown",
   "license": "MIT",
   "license": "MIT",
   "private": "true",
   "private": "true",

+ 0 - 21
turbo.json

@@ -34,11 +34,6 @@
       "outputs": ["dist/**"],
       "outputs": ["dist/**"],
       "outputLogs": "new-only"
       "outputLogs": "new-only"
     },
     },
-    "@growi/slackbot-proxy#build": {
-      "dependsOn": ["@growi/slack#build"],
-      "outputs": ["dist/**"],
-      "outputLogs": "new-only"
-    },
     "build": {
     "build": {
       "outputs": ["dist/**"],
       "outputs": ["dist/**"],
       "inputs": [
       "inputs": [
@@ -66,15 +61,6 @@
       "outputs": ["dist/**"],
       "outputs": ["dist/**"],
       "outputLogs": "new-only"
       "outputLogs": "new-only"
     },
     },
-    "@growi/slackbot-proxy#dev": {
-      "dependsOn": ["@growi/slack#dev"],
-      "cache": false,
-      "persistent": true
-    },
-    "@growi/slackbot-proxy#dev:ci": {
-      "dependsOn": ["@growi/slack#dev"],
-      "cache": false
-    },
     "dev": {
     "dev": {
       "outputs": ["dist/**"],
       "outputs": ["dist/**"],
       "inputs": [
       "inputs": [
@@ -108,16 +94,9 @@
     "@growi/ui#lint": {
     "@growi/ui#lint": {
       "dependsOn": ["@growi/core#dev"]
       "dependsOn": ["@growi/core#dev"]
     },
     },
-    "@growi/slackbot-proxy#lint": {
-      "dependsOn": ["@growi/slack#dev"]
-    },
     "lint": {
     "lint": {
     },
     },
 
 
-    "@growi/slackbot-proxy#test": {
-      "dependsOn": ["@growi/slack#dev"],
-      "outputLogs": "new-only"
-    },
     "@growi/preset-templates#test": {
     "@growi/preset-templates#test": {
       "dependsOn": ["@growi/pluginkit#dev"],
       "dependsOn": ["@growi/pluginkit#dev"],
       "outputLogs": "new-only"
       "outputLogs": "new-only"

+ 28 - 3
yarn.lock

@@ -16970,7 +16970,7 @@ string-template@>=1.0.0:
   resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
   resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
   integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=
   integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=
 
 
-"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
   version "4.2.3"
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -16988,6 +16988,15 @@ string-width@=4.2.2:
     is-fullwidth-code-point "^3.0.0"
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.0"
     strip-ansi "^6.0.0"
 
 
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
 string-width@^5.0.1, string-width@^5.1.2:
 string-width@^5.0.1, string-width@^5.1.2:
   version "5.1.2"
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -17071,7 +17080,7 @@ stringify-entities@^4.0.0:
     character-entities-html4 "^2.0.0"
     character-entities-html4 "^2.0.0"
     character-entities-legacy "^3.0.0"
     character-entities-legacy "^3.0.0"
 
 
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
   version "6.0.1"
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -17085,6 +17094,13 @@ strip-ansi@^3.0.0:
   dependencies:
   dependencies:
     ansi-regex "^2.0.0"
     ansi-regex "^2.0.0"
 
 
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-ansi@^7.0.1, strip-ansi@^7.1.0:
 strip-ansi@^7.0.1, strip-ansi@^7.1.0:
   version "7.1.0"
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -18866,7 +18882,7 @@ word-wrap@^1.2.3:
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
   integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
   integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
 
 
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
   version "7.0.0"
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -18884,6 +18900,15 @@ wrap-ansi@^6.2.0:
     string-width "^4.1.0"
     string-width "^4.1.0"
     strip-ansi "^6.0.0"
     strip-ansi "^6.0.0"
 
 
+wrap-ansi@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 wrap-ansi@^8.1.0:
 wrap-ansi@^8.1.0:
   version "8.1.0"
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"