jam411 3 лет назад
Родитель
Сommit
d199946134
38 измененных файлов с 302 добавлено и 291 удалено
  1. 20 1
      CHANGELOG.md
  2. 1 1
      lerna.json
  3. 1 1
      package.json
  4. 2 2
      packages/app/bin/github-actions/update-readme.sh
  5. 2 2
      packages/app/docker/README.md
  6. 7 7
      packages/app/package.json
  7. 1 1
      packages/app/src/components/Admin/UserGroupDetail/UpdateParentConfirmModal.tsx
  8. 2 6
      packages/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx
  9. 13 7
      packages/app/src/components/Comments.tsx
  10. 2 1
      packages/app/src/components/CustomNavigation/CustomTabContent.tsx
  11. 107 86
      packages/app/src/components/Page/DisplaySwitcher.tsx
  12. 3 7
      packages/app/src/components/PageComment.tsx
  13. 21 14
      packages/app/src/components/PageComment/Comment.tsx
  14. 6 10
      packages/app/src/components/PageComment/CommentEditor.module.scss
  15. 19 18
      packages/app/src/components/PageComment/CommentEditor.tsx
  16. 0 47
      packages/app/src/components/PageComment/CommentEditorLazyRenderer.tsx
  17. 9 0
      packages/app/src/components/PageComment/CommentPreview.module.scss
  18. 12 4
      packages/app/src/components/PageComment/CommentPreview.tsx
  19. 0 4
      packages/app/src/components/PageComment/ReplyComments.tsx
  20. 2 0
      packages/app/src/components/PageComment/_comment-inheritance.scss
  21. 2 9
      packages/app/src/components/PageEditor.tsx
  22. 2 1
      packages/app/src/components/PageEditor/Editor.module.scss
  23. 27 45
      packages/app/src/components/PageEditor/Editor.tsx
  24. 1 0
      packages/app/src/components/PageEditor/_page-editor-inheritance.scss
  25. 10 4
      packages/app/src/components/PageStatusAlert.jsx
  26. 9 0
      packages/app/src/components/Sidebar/RecentChanges.module.scss
  27. 1 1
      packages/app/src/components/Sidebar/RecentChanges.tsx
  28. 5 2
      packages/app/src/interfaces/editor-methods.ts
  29. 1 1
      packages/app/src/interfaces/ui.ts
  30. 5 0
      packages/app/src/server/routes/apiv3/page.js
  31. 1 1
      packages/app/test/cypress/integration/10-install/install.spec.ts
  32. 1 1
      packages/codemirror-textlint/package.json
  33. 1 1
      packages/core/package.json
  34. 1 1
      packages/plugin-attachment-refs/package.json
  35. 1 1
      packages/plugin-lsx/package.json
  36. 1 1
      packages/slack/package.json
  37. 2 2
      packages/slackbot-proxy/package.json
  38. 1 1
      packages/ui/package.json

+ 20 - 1
CHANGELOG.md

@@ -1,9 +1,28 @@
 # Changelog
 
-## [Unreleased](https://github.com/weseek/growi/compare/v5.1.3...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v5.1.4...HEAD)
 
 *Please do not manually update this file. We've automated the process.*
 
+## [v5.1.4](https://github.com/weseek/growi/compare/v5.1.3...v5.1.4) - 2022-09-12
+
+### 💎 Features
+
+- feat:  Truncate long path when recent changes is in S size (#6263) @mudana-grune
+- feat: In-app notifications when removing descendants of subscribed pages (#6192) @Shunm634-source
+- feat: Not increment ordered list number in CodeMirror (#6462) @mudana-grune
+
+### 🚀 Improvement
+
+- imprv: Added page URL to mail subject (#6554) @hakumizuki
+
+### 🐛 Bug Fixes
+
+- fix: Cannot update user group without parent (#6530) @kaoritokashiki
+- fix: Make PageTree input not draggable when editting (#6525) @hakumizuki
+- fix: Pagetree input hit enter (#6526) @hakumizuki
+- fix: Disallow retrieval of revision data that does not match the page (#6537) @miya
+
 ## [v5.1.3](https://github.com/weseek/growi/compare/v5.1.2...v5.1.3) - 2022-08-28
 
 ### 💎 Features

+ 1 - 1
lerna.json

@@ -1,7 +1,7 @@
 {
   "npmClient": "yarn",
   "useWorkspaces": true,
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "packages": [
     "packages/*"
   ]

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

+ 2 - 2
packages/app/bin/github-actions/update-readme.sh

@@ -2,5 +2,5 @@
 
 cd docker
 
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`5\.1\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}\2\3${RELEASED_VERSION}\4/" README.md
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`5\.1-nocdn\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}-nocdn\2\3${RELEASED_VERSION}\4/" README.md
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`5\.1\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/packages\/app\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}\2\3${RELEASED_VERSION}\4/" README.md
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`5\.1-nocdn\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/packages\/app\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}-nocdn\2\3${RELEASED_VERSION}\4/" README.md

+ 2 - 2
packages/app/docker/README.md

@@ -10,8 +10,8 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 
-* [`5.1.3`, `5.1`, `5`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v5.1.3/packages/app/docker/Dockerfile)
-* [`5.1.3-nocdn`, `5.1-nocdn`, `5-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v5.1.3/packages/app/docker/Dockerfile)
+* [`5.1.4`, `5.1`, `5`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v5.1.4/packages/app/docker/Dockerfile)
+* [`5.1.4-nocdn`, `5.1-nocdn`, `5-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v5.1.4/packages/app/docker/Dockerfile)
 * [`5.0.11`, `5.0` (Dockerfile)](https://github.com/weseek/growi/blob/v5.0.11/packages/app/docker/Dockerfile)
 * [`5.0.11-nocdn`, `5.0-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v5.0.11/packages/app/docker/Dockerfile)
 * [`4.5.23`, `4.5`, `4`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.23/packages/app/docker/Dockerfile)

+ 7 - 7
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "license": "MIT",
   "scripts": {
     "//// for production": "",
@@ -63,11 +63,11 @@
     "@elastic/elasticsearch7": "npm:@elastic/elasticsearch@^7.17.0",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/codemirror-textlint": "^5.1.4-RC.0",
-    "@growi/core": "^5.1.4-RC.0",
-    "@growi/plugin-attachment-refs": "^5.1.4-RC.0",
-    "@growi/plugin-lsx": "^5.1.4-RC.0",
-    "@growi/slack": "^5.1.4-RC.0",
+    "@growi/codemirror-textlint": "^5.1.5-RC.0",
+    "@growi/core": "^5.1.5-RC.0",
+    "@growi/plugin-attachment-refs": "^5.1.5-RC.0",
+    "@growi/plugin-lsx": "^5.1.5-RC.0",
+    "@growi/slack": "^5.1.5-RC.0",
     "@promster/express": "^7.0.2",
     "@promster/server": "^7.0.4",
     "@slack/events-api": "^3.0.0",
@@ -191,7 +191,7 @@
   },
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.1.4",
-    "@growi/ui": "^5.1.4-RC.0",
+    "@growi/ui": "^5.1.5-RC.0",
     "@handsontable/react": "=2.1.0",
     "@icon/themify-icons": "1.0.1-alpha.3",
     "@next/bundle-analyzer": "^12.2.3",

+ 1 - 1
packages/app/src/components/Admin/UserGroupDetail/UpdateParentConfirmModal.tsx

@@ -30,7 +30,7 @@ export const UpdateParentConfirmModal: FC = () => {
         <i className="icon icon-warning"></i> {t('admin:user_group_management.update_parent_confirm_modal.header')}
       </ModalHeader>
       {
-        targetGroup != null && updateData != null && updateData?.parent !== undefined ? (
+        targetGroup != null && updateData != null ? (
           <>
             <ModalBody>
               <div className="mb-2">

+ 2 - 6
packages/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -105,15 +105,11 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
   }, []);
 
   const updateUserGroup = useCallback(async(userGroup: IUserGroupHasId, update: Partial<IUserGroupHasId>, forceUpdateParents: boolean) => {
-    if (update.parent == null) {
-      throw Error('"parent" attr must not be null');
-    }
-
     const parentId = typeof update.parent === 'string' ? update.parent : update.parent?._id;
     const res = await apiv3Put<{ userGroup: IUserGroupHasId }>(`/user-groups/${userGroup._id}`, {
       name: update.name,
       description: update.description,
-      parentId,
+      parentId: parentId ?? null,
       forceUpdateParents,
     });
     const { userGroup: updatedUserGroup } = res.data;
@@ -138,7 +134,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
   );
 
   const onClickSubmitForm = useCallback(async(targetGroup: IUserGroupHasId, userGroupData: Partial<IUserGroupHasId>): Promise<void> => {
-    if (userGroupData?.parent === undefined || typeof userGroupData?.parent === 'string') {
+    if (typeof userGroupData?.parent === 'string') {
       toastError(t('Something went wrong. Please try again.'));
       return;
     }

+ 13 - 7
packages/app/src/components/Comments.tsx

@@ -1,13 +1,19 @@
 import React from 'react';
 
 import { IRevisionHasId } from '@growi/core';
+import dynamic from 'next/dynamic';
+
 
 import { PageComment } from '~/components/PageComment';
-import { useCommentPreviewOptions } from '~/stores/renderer';
+import { useSWRxPageComment } from '~/stores/comment';
 
 import { useIsTrashPage, useCurrentUser } from '../stores/context';
 
-import { CommentEditorLazyRenderer } from './PageComment/CommentEditorLazyRenderer';
+import { CommentEditorProps } from './PageComment/CommentEditor';
+
+
+const CommentEditor = dynamic<CommentEditorProps>(() => import('./PageComment/CommentEditor').then(mod => mod.CommentEditor), { ssr: false });
+
 
 type CommentsProps = {
   pageId: string,
@@ -18,12 +24,11 @@ export const Comments = (props: CommentsProps): JSX.Element => {
 
   const { pageId, revision } = props;
 
-  const { data: rendererOptions } = useCommentPreviewOptions();
+  const { mutate } = useSWRxPageComment(pageId);
   const { data: isDeleted } = useIsTrashPage();
   const { data: currentUser } = useCurrentUser();
 
-  // TODO: Implement or refactor Skelton if server-side rendering
-  if (rendererOptions == null || isDeleted == null) {
+  if (pageId == null) {
     return <></>;
   }
 
@@ -44,9 +49,10 @@ export const Comments = (props: CommentsProps): JSX.Element => {
           </div>
           { !isDeleted && (
             <div id="page-comment-write">
-              <CommentEditorLazyRenderer
+              <CommentEditor
                 pageId={pageId}
-                rendererOptions={rendererOptions}
+                isForNewComment
+                onCommentButtonClicked={mutate}
               />
             </div>
           )}

+ 2 - 1
packages/app/src/components/CustomNavigation/CustomTabContent.tsx

@@ -1,4 +1,5 @@
 import React, { useEffect, useState } from 'react';
+
 import PropTypes from 'prop-types';
 import {
   TabContent, TabPane,
@@ -18,7 +19,7 @@ const CustomTabContent = (props: Props): JSX.Element => {
 
   const { activeTab, navTabMapping, additionalClassNames } = props;
 
-  const [activatedContent, setActivatedContent] = useState<Set<string>>(new Set<string>());
+  const [activatedContent, setActivatedContent] = useState(new Set([activeTab]));
 
   // add activated content to Set
   useEffect(() => {

+ 107 - 86
packages/app/src/components/Page/DisplaySwitcher.tsx

@@ -1,9 +1,8 @@
-import React from 'react';
+import React, { useMemo } from 'react';
 
 import { pagePathUtils } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
-import { TabContent, TabPane } from 'reactstrap';
 
 // import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import {
@@ -14,6 +13,7 @@ import { useSWRxCurrentPage } from '~/stores/page';
 import { EditorMode, useEditorMode } from '~/stores/ui';
 
 import CountBadge from '../Common/CountBadge';
+import CustomTabContent from '../CustomNavigation/CustomTabContent';
 import PageListIcon from '../Icons/PageListIcon';
 import { Page } from '../Page';
 // import PageEditorByHackmd from '../PageEditorByHackmd';
@@ -24,8 +24,6 @@ import UserInfo from '../User/UserInfo';
 import styles from './DisplaySwitcher.module.scss';
 
 
-const WIKI_HEADER_LINK = 120;
-
 const { isTopPage } = pagePathUtils;
 
 
@@ -35,109 +33,132 @@ const HashChanged = dynamic(() => import('../EventListeneres/HashChanged'), { ss
 const ContentLinkButtons = dynamic(() => import('../ContentLinkButtons'), { ssr: false });
 const NotFoundPage = dynamic(() => import('../NotFoundPage'), { ssr: false });
 
-const DisplaySwitcher = React.memo((): JSX.Element => {
-  const { t } = useTranslation();
 
-  // get element for smoothScroll
-  // const getCommentListDom = useMemo(() => { return document.getElementById('page-comments-list') }, []);
+const PageView = React.memo((): JSX.Element => {
+  const { t } = useTranslation();
 
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: isSharedUser } = useIsSharedUser();
   const { data: shareLinkId } = useShareLinkId();
   const { data: isUserPage } = useIsUserPage();
-  const { data: isEditable } = useIsEditable();
   const { data: pageUser } = usePageUser();
   const { data: isNotFound } = useIsNotFound();
-  const { data: isNotCreatable } = useIsNotCreatable();
   const { data: currentPage } = useSWRxCurrentPage(shareLinkId ?? undefined);
-
-  const { data: editorMode } = useEditorMode();
-
   const { open: openDescendantPageListModal } = useDescendantsPageListModal();
 
-  const isViewMode = editorMode === EditorMode.View;
   const isTopPagePath = isTopPage(currentPagePath ?? '');
 
-  const revision = currentPage?.revision;
-
   return (
-    <>
-      <TabContent activeTab={editorMode}>
-        <TabPane tabId={EditorMode.View}>
-          <div className="d-flex flex-column flex-lg-row">
-
-            <div className="flex-grow-1 flex-basis-0 mw-0">
-              { isUserPage && <UserInfo pageUser={pageUser} />}
-              { !isNotFound && <Page /> }
-              { isNotFound && <NotFoundPage /> }
-            </div>
-
-            { !isNotFound && (
-              <div className="grw-side-contents-container">
-                <div className="grw-side-contents-sticky-container">
-
-                  {/* Page list */}
-                  <div className={`grw-page-accessories-control ${styles['grw-page-accessories-control']}`}>
-                    { currentPagePath != null && !isSharedUser && (
-                      <button
-                        type="button"
-                        className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
-                        onClick={() => openDescendantPageListModal(currentPagePath)}
-                        data-testid="pageListButton"
-                      >
-                        <div className="grw-page-accessories-control-icon">
-                          <PageListIcon />
-                        </div>
-                        {t('page_list')}
-                        <CountBadge count={currentPage?.descendantCount} offset={1} />
-                      </button>
-                    ) }
-                  </div>
-
-                  {/* Comments */}
-                  {/* { getCommentListDom != null && !isTopPagePath && ( */}
-                  { !isTopPagePath && (
-                    <div className={`mt-2 grw-page-accessories-control ${styles['grw-page-accessories-control']}`}>
-                      <button
-                        type="button"
-                        className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
-                        // onClick={() => smoothScrollIntoView(getCommentListDom, WIKI_HEADER_LINK)}
-                      >
-                        <i className="icon-fw icon-bubbles grw-page-accessories-control-icon"></i>
-                        <span>Comments</span>
-                        <CountBadge count={currentPage?.commentCount} />
-                      </button>
-                    </div>
-                  ) }
-
-                  <div className="d-none d-lg-block">
-                    <TableOfContents />
-                    <ContentLinkButtons />
+    <div className="d-flex flex-column flex-lg-row">
+
+      <div className="flex-grow-1 flex-basis-0 mw-0">
+        { isUserPage && <UserInfo pageUser={pageUser} />}
+        { !isNotFound && <Page /> }
+        { isNotFound && <NotFoundPage /> }
+      </div>
+
+      { !isNotFound && (
+        <div className="grw-side-contents-container">
+          <div className="grw-side-contents-sticky-container">
+
+            {/* Page list */}
+            <div className={`grw-page-accessories-control ${styles['grw-page-accessories-control']}`}>
+              { currentPagePath != null && !isSharedUser && (
+                <button
+                  type="button"
+                  className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
+                  onClick={() => openDescendantPageListModal(currentPagePath)}
+                  data-testid="pageListButton"
+                >
+                  <div className="grw-page-accessories-control-icon">
+                    <PageListIcon />
                   </div>
+                  {t('page_list')}
+                  <CountBadge count={currentPage?.descendantCount} offset={1} />
+                </button>
+              ) }
+            </div>
 
-                </div>
+            {/* Comments */}
+            {/* { getCommentListDom != null && !isTopPagePath && ( */}
+            { !isTopPagePath && (
+              <div className={`mt-2 grw-page-accessories-control ${styles['grw-page-accessories-control']}`}>
+                <button
+                  type="button"
+                  className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
+                  // onClick={() => smoothScrollIntoView(getCommentListDom, WIKI_HEADER_LINK)}
+                >
+                  <i className="icon-fw icon-bubbles grw-page-accessories-control-icon"></i>
+                  <span>Comments</span>
+                  <CountBadge count={currentPage?.commentCount} />
+                </button>
               </div>
             ) }
 
-          </div>
-        </TabPane>
-        { isEditable && (
-          <TabPane tabId={EditorMode.Editor}>
-            <div data-testid="page-editor" id="page-editor">
-              <PageEditor />
-            </div>
-          </TabPane>
-        ) }
-        { isEditable && (
-          <TabPane tabId={EditorMode.HackMD}>
-            <div id="page-editor-with-hackmd">
-              {/* <PageEditorByHackmd /> */}
+            <div className="d-none d-lg-block">
+              <TableOfContents />
+              <ContentLinkButtons />
             </div>
-          </TabPane>
-        ) }
-      </TabContent>
-      { isEditable && !isViewMode && <EditorNavbarBottom /> }
 
+          </div>
+        </div>
+      ) }
+    </div>
+  );
+});
+PageView.displayName = 'PageView';
+
+
+const DisplaySwitcher = React.memo((): JSX.Element => {
+  // get element for smoothScroll
+  // const getCommentListDom = useMemo(() => { return document.getElementById('page-comments-list') }, []);
+
+  const { data: isEditable } = useIsEditable();
+
+  const { data: editorMode = EditorMode.View } = useEditorMode();
+
+  const isViewMode = editorMode === EditorMode.View;
+
+  const navTabMapping = useMemo(() => {
+    return {
+      [EditorMode.View]: {
+        Content: () => (
+          <div data-testid="page-view" id="page-view">
+            <PageView />
+          </div>
+        ),
+      },
+      [EditorMode.Editor]: {
+        Content: () => (
+          isEditable
+            ? (
+              <div data-testid="page-editor" id="page-editor">
+                <PageEditor />
+              </div>
+            )
+            : <></>
+        ),
+      },
+      [EditorMode.HackMD]: {
+        Content: () => (
+          isEditable
+            ? (
+              <div id="page-editor-with-hackmd">
+                {/* <PageEditorByHackmd /> */}
+              </div>
+            )
+            : <></>
+        ),
+      },
+    };
+  }, [isEditable]);
+
+
+  return (
+    <>
+      <CustomTabContent activeTab={editorMode} navTabMapping={navTabMapping} />
+
+      { isEditable && !isViewMode && <EditorNavbarBottom /> }
       { isEditable && <HashChanged></HashChanged> }
     </>
   );

+ 3 - 7
packages/app/src/components/PageComment.tsx

@@ -8,7 +8,6 @@ import { Button } from 'reactstrap';
 
 import { toastError } from '~/client/util/apiNotification';
 import { apiPost } from '~/client/util/apiv1-client';
-import { useCommentPreviewOptions } from '~/stores/renderer';
 
 import { ICommentHasId, ICommentHasIdList } from '../interfaces/comment';
 import { useSWRxPageComment } from '../stores/comment';
@@ -27,7 +26,7 @@ const DeleteCommentModal = dynamic<DeleteCommentModalProps>(
 );
 
 export type PageCommentProps = {
-  pageId?: string,
+  pageId: string,
   revision: string | IRevisionHasId,
   currentUser: any,
   isReadOnly: boolean,
@@ -43,7 +42,6 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
   } = props;
 
   const { data: comments, mutate } = useSWRxPageComment(pageId);
-  const { data: rendererOptions } = useCommentPreviewOptions();
 
   const [commentToBeDeleted, setCommentToBeDeleted] = useState<ICommentHasId | null>(null);
   const [isDeleteConfirmModalShown, setIsDeleteConfirmModalShown] = useState<boolean>(false);
@@ -130,7 +128,7 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
   let commentTitleClasses = 'border-bottom py-3 mb-3';
   commentTitleClasses = titleAlign != null ? `${commentTitleClasses} text-${titleAlign}` : `${commentTitleClasses} text-center`;
 
-  if (commentsFromOldest == null || commentsExceptReply == null || rendererOptions == null) {
+  if (commentsFromOldest == null || commentsExceptReply == null) {
     if (hideIfEmpty) {
       return <></>;
     }
@@ -151,7 +149,6 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
       isReadOnly={isReadOnly}
       deleteBtnClicked={onClickDeleteButton}
       onComment={mutate}
-      rendererOptions={rendererOptions}
     />
   );
 
@@ -164,7 +161,6 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
       replyList={replyComments}
       deleteBtnClicked={onClickDeleteButton}
       onComment={mutate}
-      rendererOptions={rendererOptions}
     />
   );
 
@@ -204,7 +200,7 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
                     )}
                     {(!isReadOnly && showEditorIds.has(comment._id)) && (
                       <CommentEditor
-                        rendererOptions={rendererOptions}
+                        pageId={pageId}
                         replyTo={comment._id}
                         onCancelButtonClicked={() => {
                           removeShowEditorId(comment._id);

+ 21 - 14
packages/app/src/components/PageComment/Comment.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
 
 import { UserPicture } from '@growi/ui';
 import { format } from 'date-fns';
@@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import { UncontrolledTooltip } from 'reactstrap';
 
-import { RendererOptions } from '~/services/renderer/renderer';
+import { useCommentPreviewOptions } from '~/stores/renderer';
 
 import { ICommentHasId } from '../../interfaces/comment';
 import { IUser } from '../../interfaces/user';
@@ -30,7 +30,6 @@ type CommentProps = {
   isReadOnly: boolean,
   deleteBtnClicked: (comment: ICommentHasId) => void,
   onComment: () => void,
-  rendererOptions: RendererOptions,
 }
 
 export const Comment = (props: CommentProps): JSX.Element => {
@@ -40,6 +39,7 @@ export const Comment = (props: CommentProps): JSX.Element => {
   } = props;
 
   const { t } = useTranslation();
+  const { data: rendererOptions } = useCommentPreviewOptions();
 
   const [markdown, setMarkdown] = useState('');
   const [isReEdit, setIsReEdit] = useState(false);
@@ -53,6 +53,7 @@ export const Comment = (props: CommentProps): JSX.Element => {
 
   useEffect(() => {
     setMarkdown(comment.comment);
+
     const isCurrentRevision = () => {
       return comment.revision === revisionId;
     };
@@ -98,18 +99,24 @@ export const Comment = (props: CommentProps): JSX.Element => {
     return <span style={{ whiteSpace: 'pre-wrap' }}>{comment}</span>;
   };
 
-  const renderRevisionBody = () => {
-    return (
-      <RevisionRenderer
-        rendererOptions={rendererOptions}
-        markdown={markdown}
-        additionalClassName="comment"
-      />
-    );
-  };
+  const commentBody = useMemo(() => {
+    if (rendererOptions == null) {
+      return <></>;
+    }
+
+    return isMarkdown
+      ? (
+        <RevisionRenderer
+          rendererOptions={rendererOptions}
+          markdown={markdown}
+          additionalClassName="comment"
+          pagePath={currentPagePath}
+        />
+      )
+      : renderText(comment.comment);
+  }, [comment, isMarkdown, markdown, rendererOptions]);
 
   const rootClassName = getRootClassName(comment);
-  const commentBody = isMarkdown ? renderRevisionBody() : renderText(comment.comment);
   const revHref = `?revision=${comment.revision}`;
   const editedDateId = `editedDate-${comment._id}`;
   const editedDateFormatted = isEdited ? format(updatedAt, 'yyyy/MM/dd HH:mm') : null;
@@ -118,7 +125,7 @@ export const Comment = (props: CommentProps): JSX.Element => {
     <div className={`${styles['comment-styles']}`}>
       { (isReEdit && !isReadOnly) ? (
         <CommentEditor
-          rendererOptions={rendererOptions}
+          pageId={comment._id}
           replyTo={undefined}
           currentCommentId={commentId}
           commentBody={comment.comment}

+ 6 - 10
packages/app/src/components/PageComment/CommentEditor.module.scss

@@ -1,5 +1,6 @@
 @use '~/styles/bootstrap/init' as bs;
-@use './_comment-inheritance';
+@use './comment-inheritance';
+@use '../PageEditor/page-editor-inheritance';
 
 // display cheatsheet for comment form only
 .comment-editor-styles :global {
@@ -30,14 +31,9 @@
     }
   }
 
-  // TODO: Refacotr Soft-coding
-  .page-comment-commenteditorlazyrenderer-body-skelton {
-    position: relative;
-    padding: 2.258rem 2rem;
-    margin-left: 4.5em;
-    line-height: 1.5;
-    @include bs.media-breakpoint-down(xs) {
-      margin-left: 3.5em;
-    }
+  .page-comment-editor-skelton {
+    height: comment-inheritance.$codemirror-default-height;
+    margin-top: page-editor-inheritance.$navbar-editor-height;
+    margin-bottom: bs.$line-height-base + bs.$btn-padding-y;
   }
 }

+ 19 - 18
packages/app/src/components/PageComment/CommentEditor.tsx

@@ -3,30 +3,39 @@ import React, {
 } from 'react';
 
 import { UserPicture } from '@growi/ui';
+import dynamic from 'next/dynamic';
 import {
   Button, TabContent, TabPane,
 } from 'reactstrap';
 import * as toastr from 'toastr';
 
 import { apiPostForm } from '~/client/util/apiv1-client';
-import { RendererOptions } from '~/services/renderer/renderer';
+import { IEditorMethods } from '~/interfaces/editor-methods';
 import { useSWRxPageComment } from '~/stores/comment';
 import {
-  useCurrentPagePath, useCurrentPageId, useCurrentUser, useRevisionId, useIsSlackConfigured,
+  useCurrentPagePath, useCurrentUser, useRevisionId, useIsSlackConfigured,
   useIsUploadableFile, useIsUploadableImage,
 } from '~/stores/context';
 import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 
 import { CustomNavTab } from '../CustomNavigation/CustomNav';
 import NotAvailableForGuest from '../NotAvailableForGuest';
-import Editor from '../PageEditor/Editor';
-import { SlackNotification } from '../SlackNotification';
+import { Skelton } from '../Skelton';
+
 
 import { CommentPreview } from './CommentPreview';
 
 import styles from './CommentEditor.module.scss';
 
 
+const SlackNotification = dynamic(() => import('../SlackNotification').then(mod => mod.SlackNotification), { ssr: false });
+const Editor = dynamic(() => import('../PageEditor/Editor'),
+  {
+    ssr: false,
+    loading: () => <Skelton additionalClass="grw-skelton page-comment-editor-skelton" />,
+  });
+
+
 const navTabMapping = {
   comment_editor: {
     Icon: () => <i className="icon-settings" />,
@@ -41,7 +50,7 @@ const navTabMapping = {
 };
 
 export type CommentEditorProps = {
-  rendererOptions: RendererOptions,
+  pageId: string,
   isForNewComment?: boolean,
   replyTo?: string,
   currentCommentId?: string,
@@ -50,23 +59,17 @@ export type CommentEditorProps = {
   onCommentButtonClicked?: () => void,
 }
 
-type EditorRef = {
-  setValue: (value: string) => void,
-  insertText: (text: string) => void,
-  terminateUploadingState: () => void,
-}
 
 export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
 
   const {
-    rendererOptions, isForNewComment, replyTo,
+    pageId, isForNewComment, replyTo,
     currentCommentId, commentBody, onCancelButtonClicked, onCommentButtonClicked,
   } = props;
 
   const { data: currentUser } = useCurrentUser();
   const { data: currentPagePath } = useCurrentPagePath();
-  const { data: currentPageId } = useCurrentPageId();
-  const { update: updateComment, post: postComment } = useSWRxPageComment(currentPageId);
+  const { update: updateComment, post: postComment } = useSWRxPageComment(pageId);
   const { data: revisionId } = useRevisionId();
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
@@ -80,7 +83,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
   const [error, setError] = useState();
   const [slackChannels, setSlackChannels] = useState(slackChannelsData?.toString());
 
-  const editorRef = useRef<EditorRef>(null);
+  const editorRef = useRef<IEditorMethods>(null);
 
   const handleSelect = useCallback((activeTab: string) => {
     setActiveTab(activeTab);
@@ -173,7 +176,6 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
     if (editorRef.current == null) { return }
 
     const pagePath = currentPagePath;
-    const pageId = currentPageId;
     const endpoint = '/attachments.add';
     const formData = new FormData();
     formData.append('file', file);
@@ -199,7 +201,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
     finally {
       editorRef.current.terminateUploadingState();
     }
-  }, [apiErrorHandler, currentPageId, currentPagePath]);
+  }, [apiErrorHandler, currentPagePath, pageId]);
 
   const getCommentHtml = useCallback(() => {
     if (currentPagePath == null) {
@@ -208,12 +210,11 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
 
     return (
       <CommentPreview
-        rendererOptions={rendererOptions}
         markdown={comment}
         path={currentPagePath}
       />
     );
-  }, [currentPagePath, comment, rendererOptions]);
+  }, [currentPagePath, comment]);
 
   const renderBeforeReady = useCallback((): JSX.Element => {
     return (

+ 0 - 47
packages/app/src/components/PageComment/CommentEditorLazyRenderer.tsx

@@ -1,47 +0,0 @@
-import React from 'react';
-
-import dynamic from 'next/dynamic';
-
-import { RendererOptions } from '~/services/renderer/renderer';
-
-import { useSWRxPageComment } from '../../stores/comment';
-import { Skelton } from '../Skelton';
-
-import { CommentEditorProps } from './CommentEditor';
-
-import CommentEditorStyles from './CommentEditor.module.scss';
-
-const CommentEditor = dynamic<CommentEditorProps>(() => import('./CommentEditor').then(mod => mod.CommentEditor),
-  {
-    ssr: false,
-    loading: () => <div className={`${CommentEditorStyles['comment-editor-styles']} form page-comment-form`}>
-      <div className='comment-form'>
-        <div className='comment-form-user'>
-          <Skelton additionalClass='rounded-circle picture' roundedPill />
-        </div>
-        <Skelton additionalClass="page-comment-commenteditorlazyrenderer-body-skelton grw-skelton" />
-      </div>
-    </div>,
-  });
-
-type Props = {
-  pageId?: string,
-  rendererOptions: RendererOptions,
-}
-
-export const CommentEditorLazyRenderer = (props: Props): JSX.Element => {
-
-  const { pageId, rendererOptions } = props;
-
-  const { mutate } = useSWRxPageComment(pageId);
-
-  return (
-    <CommentEditor
-      rendererOptions={rendererOptions}
-      isForNewComment
-      replyTo={undefined}
-      onCommentButtonClicked={mutate}
-    />
-  );
-
-};

+ 9 - 0
packages/app/src/components/PageComment/CommentPreview.module.scss

@@ -0,0 +1,9 @@
+@use '~/styles/bootstrap/init' as bs;
+@use './comment-inheritance';
+@use '../PageEditor/page-editor-inheritance';
+
+.grw-comment-preview {
+  min-height: page-editor-inheritance.$navbar-editor-height
+    + comment-inheritance.$codemirror-default-height
+    + bs.$line-height-base;
+}

+ 12 - 4
packages/app/src/components/PageComment/CommentPreview.tsx

@@ -1,20 +1,28 @@
-import { RendererOptions } from '~/services/renderer/renderer';
+import { useCommentPreviewOptions } from '~/stores/renderer';
 
 import RevisionRenderer from '../Page/RevisionRenderer';
 
 
+import styles from './CommentPreview.module.scss';
+
+
 type CommentPreviewPorps = {
-  rendererOptions: RendererOptions,
   markdown: string,
   path: string,
 }
 
 export const CommentPreview = (props: CommentPreviewPorps): JSX.Element => {
 
-  const { rendererOptions, markdown, path } = props;
+  const { markdown, path } = props;
+
+  const { data: rendererOptions } = useCommentPreviewOptions();
+
+  if (rendererOptions == null) {
+    return <></>;
+  }
 
   return (
-    <div className="page-comment-preview-body">
+    <div className={`grw-comment-preview ${styles['grw-comment-preview']}`}>
       <RevisionRenderer
         rendererOptions={rendererOptions}
         markdown={markdown}

+ 0 - 4
packages/app/src/components/PageComment/ReplyComments.tsx

@@ -3,8 +3,6 @@ import React, { useState } from 'react';
 
 import { Collapse } from 'reactstrap';
 
-import { RendererOptions } from '~/services/renderer/renderer';
-
 import { ICommentHasId, ICommentHasIdList } from '../../interfaces/comment';
 import { IUser } from '../../interfaces/user';
 import { useIsAllReplyShown } from '../../stores/context';
@@ -22,7 +20,6 @@ type ReplycommentsProps = {
   replyList: ICommentHasIdList,
   deleteBtnClicked: (comment: ICommentHasId) => void,
   onComment: () => void,
-  rendererOptions: RendererOptions,
 }
 
 export const ReplyComments = (props: ReplycommentsProps): JSX.Element => {
@@ -46,7 +43,6 @@ export const ReplyComments = (props: ReplycommentsProps): JSX.Element => {
           isReadOnly={isReadOnly}
           deleteBtnClicked={deleteBtnClicked}
           onComment={onComment}
-          rendererOptions={rendererOptions}
         />
       </div>
     );

+ 2 - 0
packages/app/src/components/PageComment/_comment-inheritance.scss

@@ -32,3 +32,5 @@
     height: 2em;
   }
 }
+
+$codemirror-default-height: 300px;

+ 2 - 9
packages/app/src/components/PageEditor.tsx

@@ -11,6 +11,7 @@ import { throttle, debounce } from 'throttle-debounce';
 import { saveOrUpdate } from '~/client/services/page-operation';
 import { apiGet, apiPostForm } from '~/client/util/apiv1-client';
 import { getOptionsToSave } from '~/client/util/editor';
+import { IEditorMethods } from '~/interfaces/editor-methods';
 import {
   useIsEditable, useIsIndentSizeForced, useCurrentPagePath, useCurrentPathname, useCurrentPageId, useIsUploadableFile, useIsUploadableImage,
 } from '~/stores/context';
@@ -39,14 +40,6 @@ const logger = loggerFactory('growi:PageEditor');
 declare const globalEmitter: EventEmitter;
 
 
-type EditorRef = {
-  setValue: (markdown: string) => void,
-  setCaretLine: (line: number) => void,
-  insertText: (text: string) => void,
-  forceToFocus: () => void,
-  terminateUploadingState: () => void,
-}
-
 // for scrolling
 let lastScrolledDateWithCursor: Date | null = null;
 let isOriginOfScrollSyncEditor = false;
@@ -84,7 +77,7 @@ const PageEditor = React.memo((): JSX.Element => {
   const slackChannels = useMemo(() => (slackChannelsData ? slackChannelsData.toString() : ''), [slackChannelsData]);
 
 
-  const editorRef = useRef<EditorRef>(null);
+  const editorRef = useRef<IEditorMethods>(null);
   const previewRef = useRef<HTMLDivElement>(null);
 
   const setMarkdownWithDebounce = useMemo(() => debounce(100, throttle(150, (value: string, isClean: boolean) => {

+ 2 - 1
packages/app/src/components/PageEditor/Editor.module.scss

@@ -1,5 +1,6 @@
 @use '~/styles/mixins' as ms;
 @use '~/styles/bootstrap/init' as bs;
+@use './page-editor-inheritance';
 
 
 .editor-container :global {
@@ -127,7 +128,7 @@
 
   // for Navbar editor
   .navbar-editor {
-    height: 30px;
+    height: page-editor-inheritance.$navbar-editor-height;
     padding: 0;
 
     border-bottom: 1px solid transparent;

+ 27 - 45
packages/app/src/components/PageEditor/Editor.tsx

@@ -1,5 +1,5 @@
 import React, {
-  useState, useRef, useImperativeHandle, useCallback, useMemo,
+  useState, useRef, useImperativeHandle, useCallback, ForwardRefRenderFunction, forwardRef,
 } from 'react';
 
 import Dropzone from 'react-dropzone';
@@ -22,7 +22,7 @@ import TextAreaEditor from './TextAreaEditor';
 
 import styles from './Editor.module.scss';
 
-type EditorPropsType = {
+export type EditorPropsType = {
   value?: string,
   isGfmMode?: boolean,
   noCdn?: boolean,
@@ -44,7 +44,7 @@ type DropzoneRef = {
   open: () => void
 }
 
-const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
+const Editor: ForwardRefRenderFunction<IEditorMethods, EditorPropsType> = (props, ref): JSX.Element => {
   const {
     onUpload, isUploadable, isUploadableFile, indentSize, isGfmMode = true,
   } = props;
@@ -66,45 +66,29 @@ const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
     return isMobile ? taEditorRef.current : cmEditorRef.current;
   }, [isMobile]);
 
-  const methods: Partial<IEditorMethods> = useMemo(() => {
-    return {
-      forceToFocus: () => {
-        editorSubstance()?.forceToFocus();
-      },
-      setValue: (newValue: string) => {
-        editorSubstance()?.setValue(newValue);
-      },
-      setGfmMode: (bool: boolean) => {
-        editorSubstance()?.setGfmMode(bool);
-      },
-      setCaretLine: (line: number) => {
-        editorSubstance()?.setCaretLine(line);
-      },
-      setScrollTopByLine: (line: number) => {
-        editorSubstance()?.setScrollTopByLine(line);
-      },
-      insertText: (text: string) => {
-        editorSubstance()?.insertText(text);
-      },
-      getNavbarItems: (): JSX.Element[] => {
-        // concat common items and items specific to CodeMirrorEditor or TextAreaEditor
-        const navbarItems = editorSubstance()?.getNavbarItems() ?? [];
-        return navbarItems;
-      },
-    };
-  }, [editorSubstance]);
-
   // methods for ref
   useImperativeHandle(ref, () => ({
-    forceToFocus: methods.forceToFocus,
-    setValue: methods.setValue,
-    setGfmMode: methods.setGfmMode,
-    setCaretLine: methods.setCaretLine,
-    setScrollTopByLine: methods.setScrollTopByLine,
-    insertText: methods.insertText,
+    forceToFocus: () => {
+      editorSubstance()?.forceToFocus();
+    },
+    setValue: (newValue: string) => {
+      editorSubstance()?.setValue(newValue);
+    },
+    setGfmMode: (bool: boolean) => {
+      editorSubstance()?.setGfmMode(bool);
+    },
+    setCaretLine: (line: number) => {
+      editorSubstance()?.setCaretLine(line);
+    },
+    setScrollTopByLine: (line: number) => {
+      editorSubstance()?.setScrollTopByLine(line);
+    },
+    insertText: (text: string) => {
+      editorSubstance()?.insertText(text);
+    },
     /**
-   * remove overlay and set isUploading to false
-   */
+     * remove overlay and set isUploading to false
+     */
     terminateUploadingState: () => {
       setDropzoneActive(false);
       setIsUploading(false);
@@ -239,14 +223,14 @@ const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
     return (
       <div className="m-0 navbar navbar-default navbar-editor" style={{ minHeight: 'unset' }}>
         <ul className="pl-2 nav nav-navbar">
-          { methods.getNavbarItems?.().map((item, idx) => {
+          { (editorSubstance()?.getNavbarItems() ?? []).map((item, idx) => {
             // eslint-disable-next-line react/no-array-index-key
             return <li key={`navbarItem-${idx}`}>{item}</li>;
           }) }
         </ul>
       </div>
     );
-  }, [methods]);
+  }, [editorSubstance]);
 
   const renderCheatsheetModal = useCallback(() => {
     const hideCheatsheetModal = () => {
@@ -355,8 +339,6 @@ const Editor = React.forwardRef((props: EditorPropsType, ref): JSX.Element => {
       </div>
     </>
   );
-});
-
-Editor.displayName = 'Editor';
+};
 
-export default Editor;
+export default forwardRef(Editor);

+ 1 - 0
packages/app/src/components/PageEditor/_page-editor-inheritance.scss

@@ -0,0 +1 @@
+$navbar-editor-height: 30px;

+ 10 - 4
packages/app/src/components/PageStatusAlert.jsx

@@ -1,10 +1,11 @@
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import AppContainer from '~/client/services/AppContainer';
 import PageContainer from '~/client/services/PageContainer';
+import Username from '~/components/User/Username';
 
 import { withUnstatedContainers } from './UnstatedUtils';
 
@@ -82,9 +83,14 @@ class PageStatusAlert extends React.Component {
       isConflictOnEdit = markdownOnEdit !== pageContainer.state.markdown;
     }
 
-    const label1 = isConflictOnEdit
-      ? t('modal_resolve_conflict.file_conflicting_with_newer_remote')
-      : `${pageContainer.state.lastUpdateUsername} ${t('edited this page')}`;
+    // TODO: re-impl with Next.js way
+    // const usernameComponentToString = ReactDOMServer.renderToString(<Username user={pageContainer.state.lastUpdateUser} />);
+
+    // const label1 = isConflictOnEdit
+    //   ? t('modal_resolve_conflict.file_conflicting_with_newer_remote')
+    //   // eslint-disable-next-line react/no-danger
+    //   : <span dangerouslySetInnerHTML={{ __html: `${usernameComponentToString} ${t('edited this page')}` }} />;
+    const label1 = '(TBD -- 2022.09.13)';
 
     return [
       ['bg-warning'],

+ 9 - 0
packages/app/src/components/Sidebar/RecentChanges.module.scss

@@ -38,4 +38,13 @@
   .icon-lock {
     font-size: 14px;
   }
+
+  // For truncate-text
+  .flex-grow-1 {
+    min-width: 0;
+  }
+
+  .truncate-text {
+    max-width: fit-content;
+  }
 }

+ 1 - 1
packages/app/src/components/Sidebar/RecentChanges.tsx

@@ -117,7 +117,7 @@ const SmallPageItem = memo(({ page }: PageItemProps): JSX.Element => {
         <UserPicture user={page.lastUpdateUser} size="md" noTooltip />
         <div className="flex-grow-1 ml-2">
           { !dPagePath.isRoot && <FormerLink /> }
-          <h5 className="my-0">
+          <h5 className="my-0 text-truncate">
             <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
             {locked}
           </h5>

+ 5 - 2
packages/app/src/interfaces/editor-methods.ts

@@ -1,15 +1,18 @@
 export interface IEditorMethods {
   forceToFocus: () => void,
   setValue: (newValue: string) => void,
-  setGfmMode: (bool: boolean) => void,
   setCaretLine: (line: number) => void,
   setScrollTopByLine: (line: number) => void,
+  insertText: (text: string) => void,
+  terminateUploadingState: () => void,
+}
+
+export interface IEditorInnerMethods {
   getStrFromBol(): void,
   getStrToEol: () => void,
   getStrFromBolToSelectedUpperPos: () => void,
   replaceBolToCurrentPos: (text: string) => void,
   replaceLine: (text: string) => void,
-  insertText: (text: string) => void,
   insertLinebreak: () => void,
   dispatchSave: () => void,
   dispatchPasteFiles: (event: Event) => void,

+ 1 - 1
packages/app/src/interfaces/ui.ts

@@ -12,7 +12,7 @@ export type SidebarContentsType = typeof SidebarContentsType[keyof typeof Sideba
 
 export type ICustomTabContent = {
   Content: () => JSX.Element,
-  i18n: string,
+  i18n?: string,
   Icon?: () => JSX.Element,
   index?: number,
   isLinkEnabled?: boolean | ((content: ICustomTabContent) => boolean),

+ 5 - 0
packages/app/src/server/routes/apiv3/page.js

@@ -599,6 +599,11 @@ module.exports = (crowi) => {
 
       const Revision = crowi.model('Revision');
       revision = await Revision.findById(revisionIdForFind);
+
+      // Error if pageId and revison's pageIds do not match
+      if (page._id.toString() !== revision.pageId.toString()) {
+        return res.apiv3Err(new ErrorV3("Haven't the right to see the page."), 403);
+      }
     }
     catch (err) {
       logger.error('Failed to get page data', err);

+ 1 - 1
packages/app/test/cypress/integration/10-install/install.spec.ts

@@ -53,7 +53,7 @@ context('Installing', () => {
     cy.getByTestid('btnSubmit').click();
 
     cy.screenshot(`${ssPrefix}-installed`, {
-      blackout: ['#grw-sidebar-contents-wrapper'],
+      blackout: ['#grw-sidebar-contents-wrapper','[data-line="2"]:eq(0) > a > img', '[data-hide-in-vrt=true]'],
     });
   });
 

+ 1 - 1
packages/codemirror-textlint/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/codemirror-textlint",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "scripts": {

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/core",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "description": "GROWI Core Libraries",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-attachment-refs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-attachment-refs",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-lsx/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-lsx",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "description": "GROWI plugin to list pages",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slack",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "typings": "dist/index.d.ts",

+ 2 - 2
packages/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slackbot-proxy",
-  "version": "5.1.4-slackbot-proxy.0",
+  "version": "5.1.5-slackbot-proxy.0",
   "license": "MIT",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
@@ -26,7 +26,7 @@
   },
   "dependencies": {
     "@godaddy/terminus": "^4.9.0",
-    "@growi/slack": "^5.1.4-RC.0",
+    "@growi/slack": "^5.1.5-RC.0",
     "@slack/oauth": "^2.0.1",
     "@slack/web-api": "^6.2.4",
     "@tsed/common": "^6.43.0",

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/ui",
-  "version": "5.1.4-RC.0",
+  "version": "5.1.5-RC.0",
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "keywords": [