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

Merge branch 'master' into dependabot/github_actions/amannn/action-semantic-pull-request-4.2.0

Luqman Grune 4 лет назад
Родитель
Сommit
27010516bd
36 измененных файлов с 138 добавлено и 76 удалено
  1. 2 2
      .devcontainer/docker-compose.yml
  2. 1 1
      lerna.json
  3. 1 1
      package.json
  4. 8 8
      packages/app/package.json
  5. 13 13
      packages/app/resource/cdn-manifests.js
  6. 1 1
      packages/app/resource/locales/ja_JP/translation.json
  7. 1 1
      packages/app/resource/locales/zh_CN/translation.json
  8. 3 0
      packages/app/src/components/Admin/CustomHeaderEditor.jsx
  9. 1 1
      packages/app/src/components/Common/Dropdown/PageItemControl.tsx
  10. 3 1
      packages/app/src/components/Fab.jsx
  11. 3 1
      packages/app/src/components/Hotkeys/Subscribers/CreatePage.jsx
  12. 3 0
      packages/app/src/components/PageEditor.jsx
  13. 8 5
      packages/app/src/components/PageList/PageListItemL.tsx
  14. 8 4
      packages/app/src/components/PagePathHierarchicalLink.jsx
  15. 1 1
      packages/app/src/components/PageRenameModal.tsx
  16. 3 2
      packages/app/src/server/middlewares/register-form-validator.ts
  17. 3 2
      packages/app/src/server/routes/apiv3/user-activation.ts
  18. 1 0
      packages/app/src/server/routes/search.js
  19. 7 3
      packages/app/src/server/service/search.ts
  20. 3 1
      packages/app/src/stores/search.tsx
  21. 1 0
      packages/app/src/styles/_vendor.scss
  22. 5 0
      packages/app/src/styles/style-presentation.scss
  23. 8 4
      packages/app/src/utils/page-delete-config.ts
  24. 0 1
      packages/app/test/cypress/integration/2-basic-features/open-page-create-modal.spec.ts
  25. 0 1
      packages/app/test/cypress/integration/2-basic-features/open-page-delete-modal.spec.ts
  26. 0 1
      packages/app/test/cypress/integration/2-basic-features/open-page-duplicate-modal.spec.ts
  27. 35 0
      packages/app/test/cypress/integration/2-basic-features/open-page-move-rename-modal.spec.ts
  28. 1 1
      packages/codemirror-textlint/package.json
  29. 1 1
      packages/core/package.json
  30. 1 1
      packages/plugin-attachment-refs/package.json
  31. 1 1
      packages/plugin-lsx/package.json
  32. 1 1
      packages/plugin-pukiwiki-like-linker/package.json
  33. 1 1
      packages/slack/package.json
  34. 1 1
      packages/slackbot-proxy/package.json
  35. 1 1
      packages/ui/package.json
  36. 7 13
      yarn.lock

+ 2 - 2
.devcontainer/docker-compose.yml

@@ -65,9 +65,9 @@ services:
       - /usr/share/elasticsearch/data
       - ../../growi-docker-compose/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
 
-  #need to adjust kibana version based on elasticsearch version
+  #need to adjust kibana version based on elasticsearch version (use same version as elasticsearch version)
   kibana:
-    image: docker.elastic.co/kibana/kibana:7.17.1
+    image: docker.elastic.co/kibana/kibana:7.16.1
     restart: unless-stopped
     environment:
       ELASTICSEARCH_HOSTS: 'http://elasticsearch:9200'

+ 1 - 1
lerna.json

@@ -1,7 +1,7 @@
 {
   "npmClient": "yarn",
   "useWorkspaces": true,
-  "version": "5.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "packages": [
     "packages/*"
   ]

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "5.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

+ 8 - 8
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "5.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "license": "MIT",
   "scripts": {
     "//// for production": "",
@@ -62,11 +62,11 @@
     "@elastic/elasticsearch7": "npm:@elastic/elasticsearch@^7.17.0",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/codemirror-textlint": "^5.0.0-RC.12",
-    "@growi/plugin-attachment-refs": "^5.0.0-RC.12",
-    "@growi/plugin-lsx": "^5.0.0-RC.12",
-    "@growi/plugin-pukiwiki-like-linker": "^5.0.0-RC.12",
-    "@growi/slack": "^5.0.0-RC.12",
+    "@growi/codemirror-textlint": "^5.0.0-RC.15",
+    "@growi/plugin-attachment-refs": "^5.0.0-RC.15",
+    "@growi/plugin-lsx": "^5.0.0-RC.15",
+    "@growi/plugin-pukiwiki-like-linker": "^5.0.0-RC.15",
+    "@growi/slack": "^5.0.0-RC.15",
     "@promster/express": "^7.0.2",
     "@promster/server": "^7.0.4",
     "@slack/events-api": "^3.0.0",
@@ -167,7 +167,7 @@
   },
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.1.4",
-    "@growi/ui": "^5.0.0-RC.12",
+    "@growi/ui": "^5.0.0-RC.15",
     "@handsontable/react": "=2.1.0",
     "@types/compression": "^1.7.0",
     "@types/express": "^4.17.11",
@@ -179,7 +179,7 @@
     "browser-sync": "^2.27.7",
     "bunyan-debug": "^2.0.0",
     "cli": "~1.0.1",
-    "codemirror": "^5.63.0",
+    "codemirror": "^5.64.0",
     "colors": "=1.4.0",
     "connect-browser-sync": "^2.1.0",
     "core-js": "=2.6.9",

+ 13 - 13
packages/app/resource/cdn-manifests.js

@@ -55,28 +55,28 @@ module.exports = {
     },
     {
       name: 'codemirror-dialog',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/addon/dialog/dialog.min.js',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/addon/dialog/dialog.min.js',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-keymap-vim',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/keymap/vim.min.js',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/keymap/vim.min.js',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-keymap-emacs',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/keymap/emacs.min.js',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/keymap/emacs.min.js',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-keymap-sublime',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/keymap/sublime.min.js',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/keymap/sublime.min.js',
       args: {
         integrity: '',
       },
@@ -170,63 +170,63 @@ module.exports = {
     },
     {
       name: 'codemirror-dialog',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/addon/dialog/dialog.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/addon/dialog/dialog.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-eclipse',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/eclipse.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/eclipse.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-elegant',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/elegant.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/elegant.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-neo',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/neo.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/neo.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-mdn-like',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/mdn-like.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/mdn-like.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-material',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/material.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/material.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-dracula',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/dracula.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/dracula.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-monokai',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/monokai.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/monokai.min.css',
       args: {
         integrity: '',
       },
     },
     {
       name: 'codemirror-theme-twilight',
-      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.63.0/theme/twilight.min.css',
+      url: 'https://cdn.jsdelivr.net/npm/codemirror@5.64.0/theme/twilight.min.css',
       args: {
         integrity: '',
       },

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

@@ -214,7 +214,7 @@
     },
     "form_help": {
       "email": "この Wiki では以下のメールアドレスのみ登録可能です。",
-      "password": "パスワードには、6文字以上の半角英数字または記号等を設定してください。",
+      "password": "パスワードには、8文字以上の半角英数字または記号等を設定してください。",
       "user_id": "ユーザーIDは、ユーザーページのURLなどに利用されます。半角英数字と一部の記号のみ利用できます。"
     }
   },

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

@@ -212,7 +212,7 @@
 		},
 		"form_help": {
 			"email": "您必须有下面列出的电子邮件地址才能注册此wiki。",
-			"password": "密码长度必须至少为6个字符。",
+			"password": "密码长度必须至少为8个字符。",
 			"user_id": "您创建的网页的URL将包含您的用户ID。您的用户ID可以由字母、数字和一些符号组成。"
 		}
 	},

+ 3 - 0
packages/app/src/components/Admin/CustomHeaderEditor.jsx

@@ -7,6 +7,8 @@ require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/mode/htmlmixed/htmlmixed');
+require('codemirror/addon/hint/html-hint');
+require('codemirror/addon/edit/closetag');
 require('~/client/util/codemirror/autorefresh.ext');
 
 require('jquery-ui/ui/widgets/resizable');
@@ -22,6 +24,7 @@ export default class CustomHeaderEditor extends React.Component {
         detach
         options={{
           mode: 'htmlmixed',
+          autoCloseTags: true,
           lineNumbers: true,
           tabSize: 2,
           indentUnit: 2,

+ 1 - 1
packages/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -150,7 +150,7 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
 
         {/* Move/Rename */}
         { !forceHideMenuItems?.includes(MenuItemType.RENAME) && isEnableActions && pageInfo.isMovable && (
-          <DropdownItem onClick={renameItemClickedHandler}>
+          <DropdownItem onClick={renameItemClickedHandler} data-testid="open-page-move-rename-modal-btn">
             <i className="icon-fw  icon-action-redo"></i>
             {t(isInstantRename ? 'Rename' : 'Move/Rename')}
           </DropdownItem>

+ 3 - 1
packages/app/src/components/Fab.jsx

@@ -12,6 +12,7 @@ import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { withUnstatedContainers } from './UnstatedUtils';
 import CreatePageIcon from './Icons/CreatePageIcon';
 import ReturnTopIcon from './Icons/ReturnTopIcon';
+import { useCurrentPagePath } from '~/stores/context';
 
 const logger = loggerFactory('growi:cli:Fab');
 
@@ -20,6 +21,7 @@ const Fab = (props) => {
   const { currentUser } = appContainer;
 
   const { open: openCreateModal } = usePageCreateModal();
+  const { data: currentPath = '' } = useCurrentPagePath();
 
   const [animateClasses, setAnimateClasses] = useState('invisible');
   const [buttonClasses, setButtonClasses] = useState('');
@@ -57,7 +59,7 @@ const Fab = (props) => {
           <button
             type="button"
             className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 waves-effect waves-light ${buttonClasses}`}
-            onClick={() => openCreateModal()}
+            onClick={() => openCreateModal(currentPath)}
           >
             <CreatePageIcon />
           </button>

+ 3 - 1
packages/app/src/components/Hotkeys/Subscribers/CreatePage.jsx

@@ -2,14 +2,16 @@ import React, { useEffect } from 'react';
 import PropTypes from 'prop-types';
 
 import { usePageCreateModal } from '~/stores/modal';
+import { useCurrentPagePath } from '~/stores/context';
 
 const CreatePage = React.memo((props) => {
 
   const { open: openCreateModal } = usePageCreateModal();
+  const { data: currentPath = '' } = useCurrentPagePath();
 
   // setup effect
   useEffect(() => {
-    openCreateModal();
+    openCreateModal(currentPath);
 
     // remove this
     props.onDeleteRender(this);

+ 3 - 0
packages/app/src/components/PageEditor.jsx

@@ -328,6 +328,8 @@ class PageEditor extends React.Component {
     const noCdn = envUtils.toBoolean(config.env.NO_CDN);
     const emojiStrategy = this.props.appContainer.getEmojiStrategy();
 
+    const { path } = this.props.pageContainer.state;
+
     return (
       <div className="d-flex flex-wrap">
         <div className="page-editor-editor-container flex-grow-1 flex-basis-0 mw-0">
@@ -349,6 +351,7 @@ class PageEditor extends React.Component {
         <div className="d-none d-lg-block page-editor-preview-container flex-grow-1 flex-basis-0 mw-0">
           <Preview
             markdown={this.state.markdown}
+            pagePath={path}
             // eslint-disable-next-line no-return-assign
             inputRef={(el) => { return this.previewElement = el }}
             isMathJaxEnabled={this.state.isMathJaxEnabled}

+ 8 - 5
packages/app/src/components/PageList/PageListItemL.tsx

@@ -88,9 +88,12 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
   const elasticSearchResult = isIPageSearchMeta(pageMeta) ? pageMeta.elasticSearchResult : null;
   const revisionShortBody = isIPageInfoForListing(pageMeta) ? pageMeta.revisionShortBody : null;
 
-  const dPagePath: DevidedPagePath = new DevidedPagePath(elasticSearchResult?.highlightedPath || pageData.path, true);
+  const dPagePath: DevidedPagePath = new DevidedPagePath(pageData.path, false);
   const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
-  const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
+
+  const dPagePathHighlighted: DevidedPagePath = new DevidedPagePath(elasticSearchResult?.highlightedPath || pageData.path, true);
+  const linkedPagePathHighlightedFormer = new LinkedPagePath(dPagePathHighlighted.former);
+  const linkedPagePathHighlightedLatter = new LinkedPagePath(dPagePathHighlighted.latter);
 
   const lastUpdateDate = format(new Date(pageData.updatedAt), 'yyyy/MM/dd HH:mm:ss');
 
@@ -191,7 +194,7 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
               {/* page path */}
               <PagePathHierarchicalLink
                 linkedPagePath={linkedPagePathFormer}
-                shouldDangerouslySetInnerHTML={shouldDangerouslySetInnerHTMLForPaths}
+                linkedPagePathByHtml={linkedPagePathHighlightedFormer}
               />
               { showPageUpdatedTime && (
                 <span className="page-list-updated-at text-muted">Last update: {lastUpdateDate}</span>
@@ -213,11 +216,11 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
                           className="page-segment"
                           href={encodeURI(urljoin('/', pageData._id))}
                           // eslint-disable-next-line react/no-danger
-                          dangerouslySetInnerHTML={{ __html: linkedPagePathLatter.pathName }}
+                          dangerouslySetInnerHTML={{ __html: linkedPagePathHighlightedLatter.pathName }}
                         >
                         </a>
                       )
-                      : <a className="page-segment" href={encodeURI(urljoin('/', pageData._id))}>{linkedPagePathLatter.pathName}</a>
+                      : <a className="page-segment" href={encodeURI(urljoin('/', pageData._id))}>{linkedPagePathHighlightedLatter.pathName}</a>
                     }
                   </span>
                 </span>

+ 8 - 4
packages/app/src/components/PagePathHierarchicalLink.jsx

@@ -6,9 +6,10 @@ import urljoin from 'url-join';
 import LinkedPagePath from '../models/linked-page-path';
 
 
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 const PagePathHierarchicalLink = (props) => {
   const {
-    linkedPagePath, basePath, isInTrash, shouldDangerouslySetInnerHTML,
+    linkedPagePath, linkedPagePathByHtml, basePath, isInTrash,
   } = props;
   // render root element
   if (linkedPagePath.isRoot) {
@@ -41,6 +42,8 @@ const PagePathHierarchicalLink = (props) => {
   const isParentRoot = linkedPagePath.parent?.isRoot;
   const isSeparatorRequired = isParentExists && !isParentRoot;
 
+  const shouldDangerouslySetInnerHTML = linkedPagePathByHtml != null;
+
   const href = encodeURI(urljoin(basePath || '/', linkedPagePath.href));
 
   // eslint-disable-next-line react/prop-types
@@ -55,10 +58,10 @@ const PagePathHierarchicalLink = (props) => {
       { isParentExists && (
         <PagePathHierarchicalLink
           linkedPagePath={linkedPagePath.parent}
+          linkedPagePathByHtml={linkedPagePathByHtml?.parent}
           basePath={basePath}
           isInTrash={isInTrash || linkedPagePath.isInTrash}
           isInnerElem
-          shouldDangerouslySetInnerHTML={shouldDangerouslySetInnerHTML}
         />
       ) }
       { isSeparatorRequired && (
@@ -67,7 +70,8 @@ const PagePathHierarchicalLink = (props) => {
 
       {
         shouldDangerouslySetInnerHTML
-          ? <a className="page-segment" href={href} dangerouslySetInnerHTML={{ __html: linkedPagePath.pathName }}></a>
+          // eslint-disable-next-line react/no-danger
+          ? <a className="page-segment" href={href} dangerouslySetInnerHTML={{ __html: linkedPagePathByHtml.pathName }}></a>
           : <a className="page-segment" href={href}>{linkedPagePath.pathName}</a>
       }
 
@@ -77,9 +81,9 @@ const PagePathHierarchicalLink = (props) => {
 
 PagePathHierarchicalLink.propTypes = {
   linkedPagePath: PropTypes.instanceOf(LinkedPagePath).isRequired,
+  linkedPagePathByHtml: PropTypes.instanceOf(LinkedPagePath), // Not required
   basePath: PropTypes.string,
   isInTrash: PropTypes.bool,
-  shouldDangerouslySetInnerHTML: PropTypes.bool,
 
   // !!INTERNAL USE ONLY!!
   isInnerElem: PropTypes.bool,

+ 1 - 1
packages/app/src/components/PageRenameModal.tsx

@@ -203,7 +203,7 @@ const PageRenameModal = (): JSX.Element => {
 
 
   return (
-    <Modal size="lg" isOpen={isOpened} toggle={closeRenameModal} autoFocus={false}>
+    <Modal size="lg" isOpen={isOpened} toggle={closeRenameModal} data-testid="page-rename-modal" autoFocus={false}>
       <ModalHeader tag="h4" toggle={closeRenameModal} className="bg-primary text-light">
         { t('modal_rename.label.Move/Rename page') }
       </ModalHeader>

+ 3 - 2
packages/app/src/server/middlewares/register-form-validator.ts

@@ -1,5 +1,6 @@
 import { body, validationResult } from 'express-validator';
 
+const PASSOWRD_MINIMUM_NUMBER = 8;
 // form rules
 export const registerRules = () => {
   return [
@@ -18,8 +19,8 @@ export const registerRules = () => {
     body('registerForm.password')
       .matches(/^[\x20-\x7F]*$/)
       .withMessage('Password has invalid character')
-      .isLength({ min: 6 })
-      .withMessage('Password minimum character should be more than 6 characters')
+      .isLength({ min: PASSOWRD_MINIMUM_NUMBER })
+      .withMessage('Password minimum character should be more than 8 characters')
       .not()
       .isEmpty()
       .withMessage('Password field is required'),

+ 3 - 2
packages/app/src/server/routes/apiv3/user-activation.ts

@@ -3,6 +3,7 @@ import * as express from 'express';
 import { body, validationResult } from 'express-validator';
 import ErrorV3 from '../../models/vo/error-apiv3';
 
+const PASSOWRD_MINIMUM_NUMBER = 8;
 // validation rules for complete registration form
 export const completeRegistrationRules = () => {
   return [
@@ -17,8 +18,8 @@ export const completeRegistrationRules = () => {
     body('password')
       .matches(/^[\x20-\x7F]*$/)
       .withMessage('Password has invalid character')
-      .isLength({ min: 6 })
-      .withMessage('Password minimum character should be more than 6 characters')
+      .isLength({ min: PASSOWRD_MINIMUM_NUMBER })
+      .withMessage('Password minimum character should be more than 8 characters')
       .not()
       .isEmpty()
       .withMessage('Password field is required'),

+ 1 - 0
packages/app/src/server/routes/search.js

@@ -159,6 +159,7 @@ module.exports = function(crowi, app) {
       result = await searchService.formatSearchResult(searchResult, delegatorName, user, userGroups);
     }
     catch (err) {
+      logger.error(err);
       return res.json(ApiResponse.error(err));
     }
     return res.json(ApiResponse.success(result));

+ 7 - 3
packages/app/src/server/service/search.ts

@@ -408,8 +408,8 @@ class SearchService implements SearchQueryParser, SearchResolver {
         const isHtmlInPath = highlightData['path.en'] != null || highlightData['path.ja'] != null;
 
         elasticSearchResult = {
-          snippet: typeof snippet === 'string' ? filterXss.process(snippet) : null,
-          highlightedPath: typeof pathMatch === 'string' ? filterXss.process(pathMatch) : null,
+          snippet: snippet != null && typeof snippet[0] === 'string' ? filterXss.process(snippet) : null,
+          highlightedPath: pathMatch != null && typeof pathMatch[0] === 'string' ? filterXss.process(pathMatch) : null,
           isHtmlInPath,
         };
       }
@@ -439,11 +439,15 @@ class SearchService implements SearchQueryParser, SearchResolver {
     }
 
     if (testGrant === Page.GRANT_OWNER) {
+      if (user == null) return false;
+
       return user._id.toString() === testGrantedUser.toString();
     }
 
     if (testGrant === Page.GRANT_USER_GROUP) {
-      return userGroups.map(d => d.toString()).include(testGrantedGroup.toString());
+      if (userGroups == null) return false;
+
+      return userGroups.map(id => id.toString()).includes(testGrantedGroup.toString());
     }
 
     return true;

+ 3 - 1
packages/app/src/stores/search.tsx

@@ -69,8 +69,10 @@ export const useSWRxFullTextSearch = (
   };
   const rawQuery = createSearchQuery(keyword ?? '', fixedConfigurations.includeTrashPages, fixedConfigurations.includeUserPages);
 
+  const isKeywordValid = keyword != null && keyword.length > 0;
+
   const swrResult = useSWRImmutable(
-    keyword == null ? null : ['/search', keyword, fixedConfigurations, termNumber],
+    isKeywordValid ? ['/search', keyword, fixedConfigurations, termNumber] : null,
     (endpoint, keyword, fixedConfigurations) => {
       const {
         limit, offset, sort, order,

+ 1 - 0
packages/app/src/styles/_vendor.scss

@@ -18,6 +18,7 @@
 
 // import CodeMirror styles
 @import '~codemirror/lib/codemirror.css';
+@import '~codemirror/addon/hint/show-hint.css';
 @import '~codemirror/theme/elegant.css';
 @import '~codemirror/theme/eclipse.css';
 

+ 5 - 0
packages/app/src/styles/style-presentation.scss

@@ -12,6 +12,11 @@
     font-family: Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif !important;
   }
 
+  .present {
+    max-height: 100%;
+    overflow-y: scroll;
+  }
+
   .slides > section {
     //text-align: left;
     padding: 0;

+ 8 - 4
packages/app/src/utils/page-delete-config.ts

@@ -52,11 +52,15 @@ export const validateDeleteConfigs = (
  * @returns [(value for single), (value for recursive)]
  */
 export const prepareDeleteConfigValuesForCalc = (
-    confForSingle: IPageDeleteConfigValueToProcessValidation, confForRecursive: IPageDeleteConfigValue,
+    confForSingle: IPageDeleteConfigValueToProcessValidation | undefined, confForRecursive: IPageDeleteConfigValue | undefined,
 ): [IPageDeleteConfigValueToProcessValidation, IPageDeleteConfigValueToProcessValidation] => {
-  if (confForRecursive === Value.Inherit) {
-    return [confForSingle, confForSingle];
+  // convert undefined to default values
+  const confForSingleToReturn = confForSingle ?? Value.Anyone;
+  const confForRecursiveToReturn = confForRecursive ?? Value.Inherit;
+
+  if (confForRecursiveToReturn === Value.Inherit) {
+    return [confForSingleToReturn, confForSingleToReturn];
   }
 
-  return [confForSingle, confForRecursive];
+  return [confForSingleToReturn, confForRecursiveToReturn];
 };

+ 0 - 1
packages/app/test/cypress/integration/2-basic-features/open-page-create-modal.spec.ts

@@ -19,7 +19,6 @@ context('Open PageCreateModal', () => {
   beforeEach(() => {
     if (connectSid != null) {
       cy.setCookie('connect.sid', connectSid);
-      cy.visit('/');
     }
   });
 

+ 0 - 1
packages/app/test/cypress/integration/2-basic-features/open-page-delete-modal.spec.ts

@@ -19,7 +19,6 @@ context('Open Page Delete Modal', () => {
   beforeEach(() => {
     if (connectSid != null) {
       cy.setCookie('connect.sid', connectSid);
-      cy.visit('/');
     }
   });
 

+ 0 - 1
packages/app/test/cypress/integration/2-basic-features/open-page-duplicate-modal.spec.ts

@@ -19,7 +19,6 @@ context('Open Page Duplicate Modal', () => {
   beforeEach(() => {
     if (connectSid != null) {
       cy.setCookie('connect.sid', connectSid);
-      cy.visit('/');
     }
   });
 

+ 35 - 0
packages/app/test/cypress/integration/2-basic-features/open-page-move-rename-modal.spec.ts

@@ -0,0 +1,35 @@
+context('Open Page Move Rename Modal', () => {
+
+  const ssPrefix = 'access-to-page-move-rename-modal';
+
+  let connectSid: string | undefined;
+
+  before(() => {
+    // login
+    cy.fixture("user-admin.json").then(user => {
+      cy.login(user.username, user.password);
+    });
+    cy.getCookie('connect.sid').then(cookie => {
+      connectSid = cookie?.value;
+    });
+    // collapse sidebar
+    cy.collapseSidebar(true);
+  });
+
+  beforeEach(() => {
+    if (connectSid != null) {
+      cy.setCookie('connect.sid', connectSid);
+    }
+  });
+
+  it('PageMoveRenameModal is shown successfully', () => {
+     cy.visit('/Sandbox/Bootstrap4', {  });
+     cy.get('#grw-subnav-container').within(() => {
+       cy.getByTestid('open-page-item-control-btn').click();
+       cy.getByTestid('open-page-move-rename-modal-btn').click();
+    });
+
+     cy.getByTestid('page-rename-modal').should('be.visible').screenshot(`${ssPrefix}-open-bootstrap4`);
+  });
+
+});

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

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

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/core",
-  "version": "5.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "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.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "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.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "description": "GROWI plugin to list pages",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-pukiwiki-like-linker/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-pukiwiki-like-linker",
-  "version": "5.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "description": "GROWI plugin to add PukiwikiLikeLinker",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/slack/package.json

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

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

@@ -25,7 +25,7 @@
   },
   "dependencies": {
     "@godaddy/terminus": "^4.9.0",
-    "@growi/slack": "^5.0.0-RC.12",
+    "@growi/slack": "^5.0.0-RC.15",
     "@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.0.0-RC.12",
+  "version": "5.0.0-RC.15",
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "keywords": [

+ 7 - 13
yarn.lock

@@ -5841,10 +5841,10 @@ code-point-at@^1.0.0:
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
 
-codemirror@^5.63.0:
-  version "5.63.0"
-  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.63.0.tgz#070a699108badd9c118b7261ac2e9793acdbb149"
-  integrity sha512-KlLWRPggDg2rBD1Mx7/EqEhaBdy+ybBCVh/efgjBDsPpMeEu6MbTAJzIT4TuCzvmbTEgvKOGzVT6wdBTNusqrg==
+codemirror@^5.64.0:
+  version "5.64.0"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.64.0.tgz#182eec65b62178e3cd1de8f9d88ab819cfe5f625"
+  integrity sha512-fqr6CtDQdJ6iNMbD8NX2gH2G876nNDk+TO1rrYkgWnqQdO3O1Xa9tK6q+psqhJJgE5SpbaDcgdfLmukoUVE8pg==
 
 collapse-white-space@^1.0.2:
   version "1.0.5"
@@ -15176,10 +15176,6 @@ pacote@^11.2.6:
     ssri "^8.0.1"
     tar "^6.1.0"
 
-pako@1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.3.tgz#5f515b0c6722e1982920ae8005eacb0b7ca73ccf"
-
 pako@^1.0.6:
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
@@ -15711,11 +15707,9 @@ pkg-up@^2.0.0:
     find-up "^2.1.0"
 
 plantuml-encoder@^1.2.5:
-  version "1.2.5"
-  resolved "https://registry.yarnpkg.com/plantuml-encoder/-/plantuml-encoder-1.2.5.tgz#6b8e5b9e1a1dbd88b3fd9fb46f734eec7d44b548"
-  dependencies:
-    pako "1.0.3"
-    utf8-bytes "0.0.1"
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/plantuml-encoder/-/plantuml-encoder-1.4.0.tgz#7899302cf785de956bf1a167e15420feee5975f7"
+  integrity sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==
 
 pluralize@8.0.0, pluralize@^8.0.0:
   version "8.0.0"