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

Merge branch 'master' into feat/questionnaire

ryoji-s 3 лет назад
Родитель
Сommit
cb555a5b76
41 измененных файлов с 329 добавлено и 1422 удалено
  1. 44 1
      CHANGELOG.md
  2. 1 1
      lerna.json
  3. 1 1
      package.json
  4. 1 1
      packages/app/docker/README.md
  5. 12 11
      packages/app/package.json
  6. 5 8
      packages/app/resource/locales/en_US/sandbox.md
  7. 5 8
      packages/app/resource/locales/ja_JP/sandbox.md
  8. 5 8
      packages/app/resource/locales/zh_CN/sandbox.md
  9. 2 2
      packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx
  10. 2 2
      packages/app/src/components/Page/PageView.tsx
  11. 1 1
      packages/app/src/components/PageAccessoriesModal.tsx
  12. 2 2
      packages/app/src/components/PageEditor.tsx
  13. 1 12
      packages/app/src/components/PageEditor/DrawioCommunicationHelper.ts
  14. 57 8
      packages/app/src/components/ReactMarkdownComponents/CodeBlock.tsx
  15. 2 2
      packages/app/src/components/ReactMarkdownComponents/DrawioViewerWithEditButton.tsx
  16. 17 0
      packages/app/src/components/ReactMarkdownComponents/NextLink.tsx
  17. 5 1
      packages/app/src/components/Script/DrawioViewerScript.tsx
  18. 13 5
      packages/app/src/pages/[[...path]].page.tsx
  19. 5 5
      packages/app/src/server/service/search-delegator/elasticsearch.ts
  20. 5 5
      packages/app/src/server/service/slack-command-handler/create-page-service.js
  21. 3 4
      packages/app/src/server/service/slack-command-handler/keep.js
  22. 3 1
      packages/app/src/server/service/slack-command-handler/note.js
  23. 5 4
      packages/app/src/services/renderer/renderer.tsx
  24. 0 8
      packages/app/src/stores/context.tsx
  25. 8 0
      packages/app/src/stores/page.tsx
  26. 5 0
      packages/app/src/styles/theme/_apply-colors-dark.scss
  27. 1 1
      packages/codemirror-textlint/package.json
  28. 1 1
      packages/core/package.json
  29. 1 1
      packages/core/src/interfaces/page.ts
  30. 1 1
      packages/hackmd/package.json
  31. 2 2
      packages/presentation/package.json
  32. 1 1
      packages/preset-themes/package.json
  33. 1 1
      packages/remark-drawio/package.json
  34. 15 30
      packages/remark-drawio/src/utils/embed.ts
  35. 1 4
      packages/remark-growi-directive/package.json
  36. 4 4
      packages/remark-lsx/package.json
  37. 5 2
      packages/remark-lsx/src/server/routes/lsx.js
  38. 1 1
      packages/slack/package.json
  39. 2 2
      packages/slackbot-proxy/package.json
  40. 2 2
      packages/ui/package.json
  41. 81 1268
      yarn.lock

+ 44 - 1
CHANGELOG.md

@@ -1,9 +1,52 @@
 # Changelog
 
-## [Unreleased](https://github.com/weseek/growi/compare/v6.0.9...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v6.0.13...HEAD)
 
 *Please do not manually update this file. We've automated the process.*
 
+## [v6.0.13](https://github.com/weseek/growi/compare/v6.0.12...v6.0.13) - 2023-04-03
+
+### 🐛 Bug Fixes
+
+- fix: The "note" and "keep" commands of the GROWI bot are not functioning (#7529) @miya
+- fix: The "search" command of the GROWI bot is not functioning (#7525) @miya
+- fix: Lsx filter and except option do not work when the path includes special characters (#7523) @yuki-takei
+
+## [v6.0.12](https://github.com/weseek/growi/compare/v6.0.11...v6.0.12) - 2023-03-30
+
+### 🐛 Bug Fixes
+
+- fix: DrawioViewer script URL (#7518) @yuki-takei
+
+## [v6.0.11](https://github.com/weseek/growi/compare/v6.0.10...v6.0.11) - 2023-03-29
+
+### 🚀 Improvement
+
+- imprv: ToC placeholder (#7506) @yuki-takei
+
+### 🐛 Bug Fixes
+
+- fix: Support draw.io v21.1.0 (support both of compressed/uncompressed data) (#7515) @yuki-takei
+- fix: The same level template page is being applied to lower level pages unintentionally (#7510) @miya
+- fix: Supress `activeTab` prop type error (#7504) @jam411
+
+## [v6.0.10](https://github.com/weseek/growi/compare/v6.0.9...v6.0.10) - 2023-03-23
+
+### 🚀 Improvement
+
+- imprv: Reverse switch for display or hidden page settings (#7483) @yuki-takei
+
+### 🐛 Bug Fixes
+
+- fix: CodeBlock string is be `[object Object]` if searched (#7484) @jam411
+- fix: Show handsontable edit modal color in dark theme  (#7497) @yukendev
+- fix: Error when transitioning to a user home page where creator does not exist (#7499) @miya
+- fix: Attachment links do not work correctly (#7498) @jam411
+- fix: Language selection dropdown in installer does not reflect browser language setting (#7494) @miya
+- fix:  Search results are not highlighted when searching for quoteted words (PageListItemL) (#7491) @miya
+- fix: Responses 500 status code when invalid regular expressions are inputted to lsx's execpt option (#7488) @jam411
+- fix: Page paths in search results are not displayed correctly (#7463) @miya
+
 ## [v6.0.9](https://github.com/weseek/growi/compare/v6.0.8...v6.0.9) - 2023-03-14
 
 ### 💎 Features

+ 1 - 1
lerna.json

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

+ 1 - 1
package.json

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

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

@@ -10,7 +10,7 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 
-* [`6.0.9`, `6.0`, `6`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v6.0.9/packages/app/docker/Dockerfile)
+* [`6.0.13`, `6.0`, `6`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v6.0.13/packages/app/docker/Dockerfile)
 * [`5.1.7`, `5.1`, `5`](https://github.com/weseek/growi/blob/v5.1.7/packages/app/docker/Dockerfile)
 * [`5.1.7-nocdn`, `5.1-nocdn`, `5-nocdn`](https://github.com/weseek/growi/blob/v5.1.7/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)

+ 12 - 11
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "license": "MIT",
   "scripts": {
     "//// for production": "",
@@ -66,14 +66,14 @@
     "@elastic/elasticsearch7": "npm:@elastic/elasticsearch@^7.17.0",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/codemirror-textlint": "^6.0.10-RC.0",
-    "@growi/core": "^6.0.10-RC.0",
-    "@growi/hackmd": "^6.0.10-RC.0",
-    "@growi/preset-themes": "^6.0.10-RC.0",
-    "@growi/remark-drawio": "^6.0.10-RC.0",
-    "@growi/remark-growi-directive": "^6.0.10-RC.0",
-    "@growi/remark-lsx": "^6.0.10-RC.0",
-    "@growi/slack": "^6.0.10-RC.0",
+    "@growi/codemirror-textlint": "^6.0.14-RC.0",
+    "@growi/core": "^6.0.14-RC.0",
+    "@growi/hackmd": "^6.0.14-RC.0",
+    "@growi/preset-themes": "^6.0.14-RC.0",
+    "@growi/remark-drawio": "^6.0.14-RC.0",
+    "@growi/remark-growi-directive": "^6.0.14-RC.0",
+    "@growi/remark-lsx": "^6.0.14-RC.0",
+    "@growi/slack": "^6.0.14-RC.0",
     "@promster/express": "^7.0.6",
     "@promster/server": "^7.0.8",
     "@slack/web-api": "^6.2.4",
@@ -182,6 +182,7 @@
     "remark-emoji": "^3.0.2",
     "remark-gfm": "^3.0.1",
     "remark-math": "^5.1.1",
+    "remark-toc": "^8.0.1",
     "remark-wiki-link": "^1.0.4",
     "socket.io": "^4.2.0",
     "stream-to-promise": "^3.0.0",
@@ -206,8 +207,8 @@
     "handsontable": "v7.0.0 or above is no loger MIT lisence."
   },
   "devDependencies": {
-    "@growi/presentation": "^6.0.10-RC.0",
-    "@growi/ui": "^6.0.10-RC.0",
+    "@growi/presentation": "^6.0.14-RC.0",
+    "@growi/ui": "^6.0.14-RC.0",
     "@handsontable/react": "=2.1.0",
     "@icon/themify-icons": "1.0.1-alpha.3",
     "@next/bundle-analyzer": "^13.2.3",

+ 5 - 8
packages/app/resource/locales/en_US/sandbox.md

@@ -1,16 +1,13 @@
-<div class="card">
-  <div class="card-body">
+# :memo: Table of Contents
 
-# Table of Contents
+Add `ToC` after some `#` signs.
+`Table of Contents` or `Table-of-Contents` is also OK.
 
 ```
-@[toc]
+# ToC
 ```
 
-@[toc]
-
-  </div>
-</div>
+## ToC
 
 # :memo: Block Elements
 

+ 5 - 8
packages/app/resource/locales/ja_JP/sandbox.md

@@ -1,16 +1,13 @@
-<div class="card">
-  <div class="card-body">
+# :memo: 目次
 
-# 目次
+いくつかの `#` 記号に続けて `ToC` という文字列を記述します。  
+`Table of Contents` または `Table-of-Contents` でも構いません。
 
 ```
-@[toc]
+# ToC
 ```
 
-@[toc]
-
-  </div>
-</div>
+## ToC
 
 # :memo: Block Elements
 

+ 5 - 8
packages/app/resource/locales/zh_CN/sandbox.md

@@ -1,16 +1,13 @@
-<div class="card">
-  <div class="card-body">
+# :memo: Table of Contents
 
-# Table of Contents
+Add `ToC` after some `#` signs.
+`Table of Contents` or `Table-of-Contents` is also OK.
 
 ```
-@[toc]
+# ToC
 ```
 
-@[toc]
-
-  </div>
-</div>
+## ToC
 
 # :memo: Block Elements
 

+ 2 - 2
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -17,7 +17,7 @@ import {
 import { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
 import {
   useCurrentPathname,
-  useCurrentUser, useIsGuestUser, useIsSharedUser, useShareLinkId, useTemplateTagData, useIsContainerFluid, useIsIdenticalPath,
+  useCurrentUser, useIsGuestUser, useIsSharedUser, useShareLinkId, useIsContainerFluid, useIsIdenticalPath,
 } from '~/stores/context';
 import { usePageTagsForEditors } from '~/stores/editor';
 import {
@@ -25,7 +25,7 @@ import {
   usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, usePagePresentationModal,
 } from '~/stores/modal';
 import {
-  useSWRMUTxCurrentPage, useSWRxTagsInfo, useCurrentPageId, useIsNotFound,
+  useSWRMUTxCurrentPage, useSWRxTagsInfo, useCurrentPageId, useIsNotFound, useTemplateTagData,
 } from '~/stores/page';
 import {
   EditorMode, useDrawerMode, useEditorMode, useIsAbleToShowPageManagement, useIsAbleToShowTagLabel,

+ 2 - 2
packages/app/src/components/Page/PageView.tsx

@@ -124,7 +124,7 @@ export const PageView = (props: Props): JSX.Element => {
         <div id="comments-container" ref={commentsContainerRef}>
           <Comments pageId={page._id} pagePath={pagePath} revision={page.revision} onLoaded={() => setCommentsLoaded(true)} />
         </div>
-        { isUsersHomePagePath && (
+        { (isUsersHomePagePath && page.creator != null) && (
           <UsersHomePageFooter creatorId={page.creator._id}/>
         ) }
         <PageContentFooter page={page} />
@@ -158,7 +158,7 @@ export const PageView = (props: Props): JSX.Element => {
       { specialContents }
       { specialContents == null && (
         <>
-          { isUsersHomePagePath && <UserInfo author={page?.creator} /> }
+          { (isUsersHomePagePath && page?.creator != null) && <UserInfo author={page.creator} /> }
           <div className={`mb-5 ${isMobile ? `page-mobile ${styles['page-mobile']}` : ''}`}>
             <Contents />
           </div>

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

@@ -114,7 +114,7 @@ const PageAccessoriesModal = (): JSX.Element => {
     </div>
   ), [close, isWindowExpanded]);
 
-  if (status == null) {
+  if (status == null || activeTab == null) {
     return <></>;
   }
 

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

@@ -21,7 +21,7 @@ import { IEditorMethods } from '~/interfaces/editor-methods';
 import { OptionsToSave } from '~/interfaces/page-operation';
 import { SocketEventName } from '~/interfaces/websocket';
 import {
-  useCurrentPathname, useIsEnabledAttachTitleHeader, useTemplateBodyData,
+  useCurrentPathname, useIsEnabledAttachTitleHeader,
   useIsEditable, useIsUploadableFile, useIsUploadableImage, useIsIndentSizeForced,
 } from '~/stores/context';
 import {
@@ -32,7 +32,7 @@ import {
 } from '~/stores/editor';
 import { useConflictDiffModal } from '~/stores/modal';
 import {
-  useCurrentPagePath, useSWRMUTxCurrentPage, useSWRxCurrentPage, useSWRxTagsInfo, useCurrentPageId, useIsNotFound, useIsLatestRevision,
+  useCurrentPagePath, useSWRMUTxCurrentPage, useSWRxCurrentPage, useSWRxTagsInfo, useCurrentPageId, useIsNotFound, useIsLatestRevision, useTemplateBodyData,
 } from '~/stores/page';
 import { mutatePageTree } from '~/stores/page-listing';
 import {

+ 1 - 12
packages/app/src/components/PageEditor/DrawioCommunicationHelper.ts

@@ -1,5 +1,3 @@
-import { extractCodeFromMxfile } from '@growi/remark-drawio';
-
 import loggerFactory from '~/utils/logger';
 
 
@@ -63,16 +61,7 @@ export class DrawioCommunicationHelper {
 
     // restore diagram data
     if (event.data === 'ready') {
-      let code = drawioMxFile;
-      try {
-        code = extractCodeFromMxfile(drawioMxFile);
-      }
-      // catch error if drawioMxFile is not XML
-      catch (err) {
-        // do nothing because drawioMxFile might be base64 code
-      }
-
-      event.source?.postMessage(code, { targetOrigin: '*' });
+      event.source?.postMessage(drawioMxFile, { targetOrigin: '*' });
       return;
     }
 

+ 57 - 8
packages/app/src/components/ReactMarkdownComponents/CodeBlock.tsx

@@ -1,3 +1,5 @@
+import { ReactNode } from 'react';
+
 import type { CodeComponent } from 'react-markdown/lib/ast-to-react';
 import { PrismAsyncLight } from 'react-syntax-highlighter';
 import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
@@ -5,6 +7,60 @@ import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
 import styles from './CodeBlock.module.scss';
 
 
+function extractChildrenToIgnoreReactNode(children: ReactNode): ReactNode {
+
+  if (children == null) {
+    return children;
+  }
+
+  // Single element array
+  if (Array.isArray(children) && children.length === 1) {
+    return extractChildrenToIgnoreReactNode(children[0]);
+  }
+
+  // Multiple element array
+  if (Array.isArray(children) && children.length > 1) {
+    return children.map(node => extractChildrenToIgnoreReactNode(node)).join('');
+  }
+
+  // object
+  if (typeof children === 'object') {
+    const grandChildren = (children as any).children ?? (children as any).props.children;
+    return extractChildrenToIgnoreReactNode(grandChildren);
+  }
+
+  return String(children).replace(/\n$/, '');
+}
+
+function CodeBlockSubstance({ lang, children }: { lang: string, children: ReactNode }): JSX.Element {
+  // return alternative element
+  //   in order to fix "CodeBlock string is be [object Object] if searched"
+  // see: https://github.com/weseek/growi/pull/7484
+  //
+  // Note: You can also remove this code if the user requests to see the code highlighted in Prism as-is.
+  const isSimpleString = Array.isArray(children) && children.length === 1 && typeof children[0] === 'string';
+  if (!isSimpleString) {
+    return (
+      <div className="code-highlighted" style={oneDark['pre[class*="language-"]']}>
+        <code className={`language-${lang}`} style={oneDark['code[class*="language-"]']}>
+          {children}
+        </code>
+      </div>
+    );
+  }
+
+  return (
+    <PrismAsyncLight
+      className="code-highlighted"
+      PreTag="div"
+      style={oneDark}
+      language={lang}
+    >
+      {extractChildrenToIgnoreReactNode(children)}
+    </PrismAsyncLight>
+  );
+}
+
 export const CodeBlock: CodeComponent = ({ inline, className, children }) => {
 
   if (inline) {
@@ -22,14 +78,7 @@ export const CodeBlock: CodeComponent = ({ inline, className, children }) => {
       {name != null && (
         <cite className={`code-highlighted-title ${styles['code-highlighted-title']}`}>{name}</cite>
       )}
-      <PrismAsyncLight
-        className="code-highlighted"
-        PreTag="div"
-        style={oneDark}
-        language={lang}
-      >
-        {String(children).replace(/\n$/, '')}
-      </PrismAsyncLight>
+      <CodeBlockSubstance lang={lang}>{children}</CodeBlockSubstance>
     </>
   );
 };

+ 2 - 2
packages/app/src/components/ReactMarkdownComponents/DrawioViewerWithEditButton.tsx

@@ -4,7 +4,7 @@ import EventEmitter from 'events';
 
 import {
   DrawioEditByViewerProps,
-  DrawioViewer, DrawioViewerProps, extractCodeFromMxfile,
+  DrawioViewer, DrawioViewerProps,
 } from '@growi/remark-drawio';
 import { useTranslation } from 'next-i18next';
 
@@ -33,7 +33,7 @@ export const DrawioViewerWithEditButton = React.memo((props: DrawioViewerProps):
 
   const editButtonClickHandler = useCallback(() => {
     const data: DrawioEditByViewerProps = {
-      bol, eol, drawioMxFile: extractCodeFromMxfile(mxfile),
+      bol, eol, drawioMxFile: mxfile,
     };
     globalEmitter.emit('launchDrawioModal', data);
   }, [bol, eol, mxfile]);

+ 17 - 0
packages/app/src/components/ReactMarkdownComponents/NextLink.tsx

@@ -22,6 +22,10 @@ const isExternalLink = (href: string, siteUrl: string | undefined): boolean => {
   }
 };
 
+const isAttached = (href: string): boolean => {
+  return href.startsWith('/attachment/');
+};
+
 type Props = Omit<LinkProps, 'href'> & {
   children: React.ReactNode,
   href?: string,
@@ -59,6 +63,19 @@ export const NextLink = (props: Props): JSX.Element => {
     );
   }
 
+  // when href is an attachment file
+  if (isAttached(href)) {
+    const dlhref = href.replace('/attachment/', '/download/');
+    return (
+      <span>
+        <a href={href} className={className} target="_blank" rel="noopener noreferrer" {...dataAttributes}>
+          {children}
+        </a>&nbsp;
+        <a href={dlhref} className="attachment-download"><i className='icon-cloud-download'></i></a>
+      </span>
+    );
+  }
+
   return (
     <Link {...rest} href={href} prefetch={false} legacyBehavior>
       <a href={href} className={className} {...dataAttributes}>{children}</a>

+ 5 - 1
packages/app/src/components/Script/DrawioViewerScript.tsx

@@ -3,12 +3,16 @@ import { useCallback } from 'react';
 import type { IGraphViewerGlobal } from '@growi/remark-drawio';
 import Script from 'next/script';
 
+import { useDrawioUri } from '~/stores/context';
+
 declare global {
   // eslint-disable-next-line vars-on-top, no-var
   var GraphViewer: IGraphViewerGlobal;
 }
 
 export const DrawioViewerScript = (): JSX.Element => {
+  const { data: drawioUri } = useDrawioUri();
+
   const loadedHandler = useCallback(() => {
     // disable useResizeSensor and checkVisibleState
     //   for preventing resize event by viewer.min.js
@@ -31,7 +35,7 @@ export const DrawioViewerScript = (): JSX.Element => {
   return (
     <Script
       type="text/javascript"
-      src="https://www.draw.io/js/viewer.min.js"
+      src={(new URL('/js/viewer.min.js', drawioUri)).toString()}
       onLoad={loadedHandler}
     />
   );

+ 13 - 5
packages/app/src/pages/[[...path]].page.tsx

@@ -35,7 +35,7 @@ import {
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
   useDrawioUri, useHackmdUri, useDefaultIndentSize, useIsIndentSizeForced,
-  useIsAclEnabled, useIsSearchPage, useTemplateTagData, useTemplateBodyData, useIsEnabledAttachTitleHeader,
+  useIsAclEnabled, useIsSearchPage, useIsEnabledAttachTitleHeader,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPathname,
   useIsSlackConfigured, useRendererConfig,
   useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage, useIsContainerFluid, useIsNotCreatable,
@@ -43,7 +43,7 @@ import {
 import { useEditingMarkdown } from '~/stores/editor';
 import { useHasDraftOnHackmd, usePageIdOnHackmd, useRevisionIdHackmdSynced } from '~/stores/hackmd';
 import {
-  useSWRxCurrentPage, useSWRxIsGrantNormalized, useCurrentPageId, useIsNotFound, useIsLatestRevision,
+  useSWRxCurrentPage, useSWRxIsGrantNormalized, useCurrentPageId, useIsNotFound, useIsLatestRevision, useTemplateTagData, useTemplateBodyData,
 } from '~/stores/page';
 import { useRedirectFrom } from '~/stores/page-redirect';
 import { useRemoteRevisionId } from '~/stores/remote-latest-page';
@@ -202,9 +202,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   useIsEnabledStaleNotification(props.isEnabledStaleNotification);
   useIsSearchPage(false);
 
-  useTemplateTagData(props.templateTagData);
-  useTemplateBodyData(props.templateBodyData);
-
   useIsEnabledAttachTitleHeader(props.isEnabledAttachTitleHeader);
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
@@ -253,6 +250,9 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   const { mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
   const { mutate: mutateRevisionIdHackmdSynced } = useRevisionIdHackmdSynced();
 
+  const { mutate: mutateTemplateTagData } = useTemplateTagData();
+  const { mutate: mutateTemplateBodyData } = useTemplateBodyData();
+
   useSetupGlobalSocket();
   useSetupGlobalSocketForPage(pageId);
 
@@ -302,6 +302,14 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
     mutateIsLatestRevision(props.isLatestRevision);
   }, [mutateIsLatestRevision, props.isLatestRevision]);
 
+  useEffect(() => {
+    mutateTemplateTagData(props.templateTagData);
+  }, [props.templateTagData, mutateTemplateTagData]);
+
+  useEffect(() => {
+    mutateTemplateBodyData(props.templateBodyData);
+  }, [props.templateBodyData, mutateTemplateBodyData]);
+
   const title = generateCustomTitleForPage(props, pagePath);
 
   return (

+ 5 - 5
packages/app/src/server/service/search-delegator/elasticsearch.ts

@@ -963,17 +963,17 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
     }
   }
 
-  async search(data: SearchableData<ESQueryTerms>, user, userGroups, option): Promise<ISearchResult<unknown>> {
+  async search(data: SearchableData<ESQueryTerms>, user, userGroups, option?): Promise<ISearchResult<unknown>> {
     const { queryString, terms } = data;
 
     if (terms == null) {
       throw Error('Cannnot process search since terms is undefined.');
     }
 
-    const from = option.offset || null;
-    const size = option.limit || null;
-    const sort = option.sort || null;
-    const order = option.order || null;
+    const from = option?.offset ?? null;
+    const size = option?.limit ?? null;
+    const sort = option?.sort ?? null;
+    const order = option?.order ?? null;
     const query = this.createSearchQuery();
     this.appendCriteriaForQueryString(query, terms);
 

+ 5 - 5
packages/app/src/server/service/slack-command-handler/create-page-service.js

@@ -11,17 +11,17 @@ class CreatePageService {
     this.crowi = crowi;
   }
 
-  async createPageInGrowi(interactionPayloadAccessor, path, contentsBody, respondUtil, userId) {
-    const Page = this.crowi.model('Page');
+  async createPageInGrowi(interactionPayloadAccessor, path, contentsBody, respondUtil, user) {
     const reshapedContentsBody = reshapeContentsBody(contentsBody);
 
     // sanitize path
     const sanitizedPath = this.crowi.xss.process(path);
     const normalizedPath = pathUtils.normalizePath(sanitizedPath);
 
-    // generate a dummy id because Operation to create a page needs ObjectId
-    const dummyObjectIdOfUser = userId != null ? userId : new mongoose.Types.ObjectId();
-    const page = await this.crowi.pageService.create(normalizedPath, reshapedContentsBody, dummyObjectIdOfUser, {});
+    // Since an ObjectId is required for creating a page, if a user does not exist, a dummy user will be generated
+    const userOrDummyUser = user != null ? user : { _id: new mongoose.Types.ObjectId() };
+
+    const page = await this.crowi.pageService.create(normalizedPath, reshapedContentsBody, userOrDummyUser, {});
 
     // Send a message when page creation is complete
     const growiUri = this.crowi.appService.getSiteUrl();

+ 3 - 4
packages/app/src/server/service/slack-command-handler/keep.js

@@ -35,7 +35,6 @@ module.exports = (crowi) => {
     const channelId = payload.channel.id; // this must exist since the type is always block_actions
     const user = await User.findUserBySlackMemberId(payload.user.id);
 
-    const userId = user != null ? user._id : null;
     // validate form
     const { path, oldest, newest } = await this.keepValidateForm(client, payload, interactionPayloadAccessor);
     // get messages
@@ -45,7 +44,7 @@ module.exports = (crowi) => {
 
     const contentsBody = cleanedContents.join('');
     // create and send url message
-    await this.keepCreatePageAndSendPreview(client, interactionPayloadAccessor, path, userId, contentsBody, respondUtil);
+    await this.keepCreatePageAndSendPreview(client, interactionPayloadAccessor, path, user, contentsBody, respondUtil);
   };
 
   handler.keepValidateForm = async function(client, payload, interactionPayloadAccessor) {
@@ -197,8 +196,8 @@ module.exports = (crowi) => {
     return cleanedContents;
   };
 
-  handler.keepCreatePageAndSendPreview = async function(client, interactionPayloadAccessor, path, userId, contentsBody, respondUtil) {
-    await createPageService.createPageInGrowi(interactionPayloadAccessor, path, contentsBody, respondUtil, userId);
+  handler.keepCreatePageAndSendPreview = async function(client, interactionPayloadAccessor, path, user, contentsBody, respondUtil) {
+    await createPageService.createPageInGrowi(interactionPayloadAccessor, path, contentsBody, respondUtil, user);
 
     // TODO: contentsBody text characters must be less than 3001
     // send preview to dm

+ 3 - 1
packages/app/src/server/service/slack-command-handler/note.js

@@ -17,6 +17,7 @@ module.exports = (crowi) => {
     type: 'conversations_select',
     default_to_current_conversation: true,
   };
+  const { User } = crowi.models;
 
   handler.handleCommand = async(growiCommand, client, body, respondUtil) => {
     await respondUtil.respond({
@@ -44,12 +45,13 @@ module.exports = (crowi) => {
   };
 
   handler.createPage = async function(client, interactionPayload, interactionPayloadAccessor, respondUtil) {
+    const user = await User.findUserBySlackMemberId(interactionPayload.user.id);
     const path = interactionPayloadAccessor.getStateValues()?.path.path_input.value;
     const contentsBody = interactionPayloadAccessor.getStateValues()?.contents.contents_input.value;
     if (path == null || contentsBody == null) {
       throw new SlackCommandHandlerError('All parameters are required.');
     }
-    await createPageService.createPageInGrowi(interactionPayloadAccessor, path, contentsBody, respondUtil);
+    await createPageService.createPageInGrowi(interactionPayloadAccessor, path, contentsBody, respondUtil, user);
     await respondUtil.deleteOriginal();
   };
 

+ 5 - 4
packages/app/src/services/renderer/renderer.tsx

@@ -19,6 +19,7 @@ import breaks from 'remark-breaks';
 import emoji from 'remark-emoji';
 import gfm from 'remark-gfm';
 import math from 'remark-math';
+import toc from 'remark-toc';
 import deepmerge from 'ts-deepmerge';
 import type { PluggableList, Pluggable, PluginTuple } from 'unified';
 
@@ -38,10 +39,9 @@ import * as addLineNumberAttribute from './rehype-plugins/add-line-number-attrib
 import * as keywordHighlighter from './rehype-plugins/keyword-highlighter';
 import { relativeLinks } from './rehype-plugins/relative-links';
 import { relativeLinksByPukiwikiLikeLinker } from './rehype-plugins/relative-links-by-pukiwiki-like-linker';
-import * as toc from './rehype-plugins/relocate-toc';
+import * as relocateToc from './rehype-plugins/relocate-toc';
 import * as plantuml from './remark-plugins/plantuml';
 import { pukiwikiLikeLinker } from './remark-plugins/pukiwiki-like-linker';
-import * as table from './remark-plugins/table';
 import * as xsvToTable from './remark-plugins/xsv-to-table';
 
 // import EasyGrid from './PreProcessor/EasyGrid';
@@ -124,6 +124,7 @@ const verifySanitizePlugin = (options: RendererOptions, shouldBeTheLastItem = tr
 const generateCommonOptions = (pagePath: string|undefined): RendererOptions => {
   return {
     remarkPlugins: [
+      [toc, { maxDepth: 3, tight: true, prefix: 'mdcont-' }],
       gfm,
       emoji,
       pukiwikiLikeLinker,
@@ -188,7 +189,7 @@ export const generateViewOptions = (
     [lsxGrowiPlugin.rehypePlugin, { pagePath, isSharedPage: config.isSharedPage }],
     rehypeSanitizePlugin,
     katex,
-    [toc.rehypePluginStore, { storeTocNode }],
+    [relocateToc.rehypePluginStore, { storeTocNode }],
   );
 
   // add components
@@ -232,7 +233,7 @@ export const generateTocOptions = (config: RendererConfig, tocNode: HtmlElementN
 
   // add rehype plugins
   rehypePlugins.push(
-    [toc.rehypePluginRestore, { tocNode }],
+    [relocateToc.rehypePluginRestore, { tocNode }],
     rehypeSanitizePlugin,
   );
 

+ 0 - 8
packages/app/src/stores/context.tsx

@@ -56,14 +56,6 @@ export const useIsNotCreatable = (initialData?: boolean): SWRResponse<boolean, E
   return useContextSWR<boolean, Error>('isNotCreatable', initialData, { fallbackData: false });
 };
 
-export const useTemplateTagData = (initialData?: string[]): SWRResponse<string[], Error> => {
-  return useContextSWR<string[], Error>('templateTagData', initialData);
-};
-
-export const useTemplateBodyData = (initialData?: string): SWRResponse<string, Error> => {
-  return useContextSWR<string, Error>('templateBodyData', initialData);
-};
-
 export const useIsSharedUser = (initialData?: boolean): SWRResponse<boolean, Error> => {
   return useContextSWR<boolean, Error>('isSharedUser', initialData);
 };

+ 8 - 0
packages/app/src/stores/page.tsx

@@ -37,6 +37,14 @@ export const useIsNotFound = (initialData?: boolean): SWRResponse<boolean, Error
   return useStaticSWR<boolean, Error>('isNotFound', initialData, { fallbackData: false });
 };
 
+export const useTemplateTagData = (initialData?: string[]): SWRResponse<string[], Error> => {
+  return useStaticSWR<string[], Error>('templateTagData', initialData);
+};
+
+export const useTemplateBodyData = (initialData?: string): SWRResponse<string, Error> => {
+  return useStaticSWR<string, Error>('templateBodyData', initialData);
+};
+
 export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|null): SWRResponse<IPagePopulatedToShowRevision|null> => {
   const key = 'currentPage';
 

+ 5 - 0
packages/app/src/styles/theme/_apply-colors-dark.scss

@@ -504,6 +504,11 @@
   /*
   * GROWI HandsontableModal
   */
+
+  .handsontable td {
+    color: black;
+  }
+
   .grw-hot-modal-navbar {
     background-color: var(--dark);
   }

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

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

+ 1 - 1
packages/core/package.json

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

+ 1 - 1
packages/core/src/interfaces/page.ts

@@ -40,7 +40,7 @@ export type IPagePopulatedToList = Omit<IPageHasId, 'lastUpdateUser'> & {
 
 export type IPagePopulatedToShowRevision = Omit<IPageHasId, 'lastUpdateUser'|'creator'|'deleteUser'|'grantedGroup'|'revision'|'author'> & {
   lastUpdateUser: IUserHasId,
-  creator: IUserHasId,
+  creator: IUserHasId | null,
   deleteUser: IUserHasId,
   grantedGroup: IUserGroupHasId,
   revision: IRevisionHasId,

+ 1 - 1
packages/hackmd/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/hackmd",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "description": "GROWI js and css files to use hackmd",
   "license": "MIT",
   "main": "dist/index.js",

+ 2 - 2
packages/presentation/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/presentation",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "description": "GROWI plugin for presentation",
   "license": "MIT",
   "keywords": ["growi", "growi-plugin"],
@@ -15,7 +15,7 @@
     "test": ""
   },
   "dependencies": {
-    "@growi/core": "^6.0.10-RC.0"
+    "@growi/core": "^6.0.14-RC.0"
   },
   "devDependencies": {
     "@marp-team/marp-core": "^3.4.2",

+ 1 - 1
packages/preset-themes/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@growi/preset-themes",
   "description": "GROWI preset themes",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "license": "MIT",
   "main": "dist/libs/index.js",
   "files": [

+ 1 - 1
packages/remark-drawio/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/remark-drawio",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "description": "remark plugin to draw diagrams with draw.io (diagrams.net)",
   "license": "MIT",
   "keywords": [

+ 15 - 30
packages/remark-drawio/src/utils/embed.ts

@@ -2,21 +2,13 @@
 import pako from 'pako';
 import xmldoc from 'xmldoc';
 
-export const extractCodeFromMxfile = (input: string): string => {
-  const doc = new xmldoc.XmlDocument(input);
-  return doc.valueWithPath('diagram');
+const validateUncompressedData = (input: string): boolean => {
+  return new RegExp('/<mxGraphModel/').test(input);
 };
 
-const validateInputData = (input: string): boolean => {
+const validateCompressedData = (input: string): boolean => {
   let data = input;
 
-  try {
-    data = extractCodeFromMxfile(data);
-  }
-  catch (e) {
-    // ignore
-  }
-
   try {
     data = Buffer.from(data, 'base64').toString('binary');
   }
@@ -65,27 +57,20 @@ export const generateMxgraphData = (code: string): string => {
     return '';
   }
 
-  validateInputData(trimedCode);
-
-  let xml;
-  try {
-    // may be XML Format <mxfile><diagram> ... </diagram></mxfile>
-    const doc = new xmldoc.XmlDocument(trimedCode);
-    const diagram = doc.valueWithPath('diagram');
-    if (diagram) {
-      xml = trimedCode;
-    }
-  }
-  catch (e) {
-    // may be NOT XML Format
-    xml = `
-<mxfile version="6.8.9" editor="www.draw.io" type="atlas">
-  <mxAtlasLibraries/>
-  <diagram>${trimedCode}</diagram>
-</mxfile>
-`;
+  // Evaluate the code is whether uncompressed data that are generated by v21.1.0 or above
+  // see: https://github.com/jgraph/drawio/issues/3106#issuecomment-1479352026
+  const isUncompressedData = validateUncompressedData(trimedCode);
+  if (isUncompressedData) {
+    validateCompressedData(trimedCode);
   }
 
+  const xml = `
+    <mxfile version="6.8.9" editor="www.draw.io" type="atlas">
+      <mxAtlasLibraries/>
+      <diagram>${isUncompressedData ? xmldoc.XmlDocument(trimedCode) : trimedCode}</diagram>
+    </mxfile>
+  `;
+
   // see options: https://drawio.freshdesk.com/support/solutions/articles/16000042542-embed-html
   const mxGraphData = {
     editable: false,

+ 1 - 4
packages/remark-growi-directive/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/remark-growi-directive",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "description": "remark plugin to support GROWI plugin (forked from remark-directive@2.0.1)",
   "license": "MIT",
   "keywords": [
@@ -48,10 +48,7 @@
     "is-hidden": "^2.0.0",
     "mdast-util-from-markdown": "^1.0.0",
     "micromark": "^3.0.0",
-    "micromark-build": "^1.0.0",
     "remark": "^14.0.0",
-    "remark-cli": "^10.0.0",
-    "remark-preset-wooorm": "^9.0.0",
     "rimraf": "^3.0.0",
     "tape": "^5.0.0",
     "to-vfile": "^7.0.0",

+ 4 - 4
packages/remark-lsx/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/remark-lsx",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "description": "GROWI plugin to list pages",
   "license": "MIT",
   "keywords": ["growi", "growi-plugin"],
@@ -20,9 +20,9 @@
     "test": ""
   },
   "dependencies": {
-    "@growi/core": "^6.0.10-RC.0",
-    "@growi/remark-growi-directive": "^6.0.10-RC.0",
-    "@growi/ui": "^6.0.10-RC.0",
+    "@growi/core": "^6.0.14-RC.0",
+    "@growi/remark-growi-directive": "^6.0.14-RC.0",
+    "@growi/ui": "^6.0.14-RC.0",
     "swr": "^2.0.3"
   },
   "devDependencies": {

+ 5 - 2
packages/remark-lsx/src/server/routes/lsx.js

@@ -1,3 +1,4 @@
+import escapeStringRegexp from 'escape-string-regexp';
 import createError, { isHttpError } from 'http-errors';
 
 const { pathUtils, pagePathUtils, customTagUtils } = require('@growi/core');
@@ -103,14 +104,16 @@ class Lsx {
       throw createError(400, 'filter option require value in regular expression.');
     }
 
+    const pagePathForRegexp = escapeStringRegexp(addTrailingSlash(pagePath));
+
     let filterPath = '';
     try {
       if (optionsFilter.charAt(0) === '^') {
         // move '^' to the first of path
-        filterPath = new RegExp(`^${addTrailingSlash(pagePath)}${optionsFilter.slice(1, optionsFilter.length)}`);
+        filterPath = new RegExp(`^${pagePathForRegexp}${optionsFilter.slice(1, optionsFilter.length)}`);
       }
       else {
-        filterPath = new RegExp(`^${addTrailingSlash(pagePath)}.*${optionsFilter}`);
+        filterPath = new RegExp(`^${pagePathForRegexp}.*${optionsFilter}`);
       }
     }
     catch (err) {

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slack",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-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": "6.0.10-slackbot-proxy.0",
+  "version": "6.0.14-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": "^6.0.10-RC.0",
+    "@growi/slack": "^6.0.14-RC.0",
     "@slack/oauth": "^2.0.1",
     "@slack/web-api": "^6.2.4",
     "@tsed/common": "^6.43.0",

+ 2 - 2
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/ui",
-  "version": "6.0.10-RC.0",
+  "version": "6.0.14-RC.0",
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "keywords": ["growi"],
@@ -17,7 +17,7 @@
     "test": "jest --verbose"
   },
   "dependencies": {
-    "@growi/core": "^6.0.10-RC.0"
+    "@growi/core": "^6.0.14-RC.0"
   },
   "devDependencies": {
     "eslint-plugin-regex": "^1.8.0",

Разница между файлами не показана из-за своего большого размера
+ 81 - 1268
yarn.lock


Некоторые файлы не были показаны из-за большого количества измененных файлов