Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master' into imprv/theme-color-box

Yuki Takei 3 anni fa
parent
commit
beca7a21e0

+ 28 - 0
packages/app/src/client/services/page-operation.ts

@@ -2,7 +2,10 @@ import { SubscriptionStatusType, Nullable } from '@growi/core';
 import urljoin from 'url-join';
 
 import { OptionsToSave } from '~/interfaces/page-operation';
+import { useCurrentPageId } from '~/stores/context';
 import { useIsEnabledUnsavedWarning } from '~/stores/editor';
+import { useSWRxCurrentPage } from '~/stores/page';
+import { useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
 import loggerFactory from '~/utils/logger';
 
 import { toastError } from '../util/apiNotification';
@@ -171,3 +174,28 @@ export const useSaveOrUpdate = (): SaveOrUpdateFunction => {
     return res;
   };
 };
+
+export const useUpdateStateAfterSave = () => {
+  const { mutate: mutateCurrentPageId } = useCurrentPageId();
+  const { mutate: mutateCurrentPage } = useSWRxCurrentPage();
+  const { setRemoteLatestPageData } = useSetRemoteLatestPageData();
+
+  // update swr 'currentPageId', 'currentPage', remote states
+  return async(pageId: string) => {
+    await mutateCurrentPageId(pageId);
+    const updatedPage = await mutateCurrentPage();
+
+    if (updatedPage == null) { return }
+
+    const remoterevisionData = {
+      remoteRevisionId: updatedPage.revision._id,
+      remoteRevisionBody: updatedPage.revision.body,
+      remoteRevisionLastUpdateUser: updatedPage.lastUpdateUser,
+      remoteRevisionLastUpdatedAt: updatedPage.updatedAt,
+      revisionIdHackmdSynced: updatedPage.revisionHackmdSynced.toString(),
+      hasDraftOnHackmd: updatedPage.hasDraftOnHackmd,
+    };
+
+    setRemoteLatestPageData(remoterevisionData);
+  };
+};

+ 4 - 0
packages/app/src/components/NotAvailableForGuest.tsx

@@ -16,6 +16,10 @@ export const NotAvailableForGuest = ({ children }: NotAvailableForGuestProps): J
   const { data: isGuestUser } = useIsGuestUser();
   const isDisabled = !!isGuestUser;
 
+  if (!isGuestUser) {
+    return children;
+  }
+
   const id = `grw-not-available-for-guest-${Math.random().toString(32).substring(2)}`;
 
   return (

+ 8 - 7
packages/app/src/components/PageEditor.tsx

@@ -13,7 +13,7 @@ import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
 import { throttle, debounce } from 'throttle-debounce';
 
-import { useSaveOrUpdate } from '~/client/services/page-operation';
+import { useUpdateStateAfterSave, useSaveOrUpdate } from '~/client/services/page-operation';
 import { apiGet, apiPostForm } from '~/client/util/apiv1-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { IEditorMethods } from '~/interfaces/editor-methods';
@@ -31,6 +31,7 @@ import {
 } from '~/stores/editor';
 import { useConflictDiffModal } from '~/stores/modal';
 import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import { useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
 import { usePreviewOptions } from '~/stores/renderer';
 import {
   EditorMode,
@@ -93,6 +94,8 @@ const PageEditor = React.memo((): JSX.Element => {
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
   const saveOrUpdate = useSaveOrUpdate();
 
+  const updateStateAfterSave = useUpdateStateAfterSave();
+
   const currentRevisionId = currentPage?.revision?._id;
 
   const initialValue = useMemo(() => {
@@ -245,11 +248,10 @@ const PageEditor = React.memo((): JSX.Element => {
       await router.push(`/${page._id}`);
     }
     else {
-      await mutateCurrentPageId(page._id);
-      await mutateCurrentPage();
+      updateStateAfterSave(page._id);
     }
     mutateEditorMode(EditorMode.View);
-  }, [editorMode, save, isNotFound, mutateEditorMode, router, mutateCurrentPageId, mutateCurrentPage]);
+  }, [editorMode, save, isNotFound, mutateEditorMode, router, useUpdateStateAfterSave]);
 
   const saveWithShortcut = useCallback(async() => {
     if (editorMode !== EditorMode.Editor) {
@@ -258,11 +260,10 @@ const PageEditor = React.memo((): JSX.Element => {
 
     const page = await save();
     if (page != null) {
+      updateStateAfterSave(page._id);
       toastSuccess(t('toaster.save_succeeded'));
-      await mutateCurrentPageId(page._id);
-      await mutateCurrentPage();
     }
-  }, [editorMode, mutateCurrentPage, mutateCurrentPageId, save, t]);
+  }, [editorMode, save, t, useUpdateStateAfterSave]);
 
 
   /**

+ 11 - 11
packages/app/src/components/PageEditorByHackmd.tsx

@@ -10,7 +10,7 @@ import { useRouter } from 'next/router';
 import { useTranslation } from 'react-i18next';
 import urljoin from 'url-join';
 
-import { useSaveOrUpdate } from '~/client/services/page-operation';
+import { useUpdateStateAfterSave, useSaveOrUpdate } from '~/client/services/page-operation';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import { apiPost } from '~/client/util/apiv1-client';
 import { IResHackmdIntegrated, IResHackmdDiscard } from '~/interfaces/hackmd';
@@ -25,7 +25,7 @@ import {
   usePageIdOnHackmd, useHasDraftOnHackmd, useRevisionIdHackmdSynced, useIsHackmdDraftUpdatingInRealtime,
 } from '~/stores/hackmd';
 import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
-import { useRemoteRevisionId } from '~/stores/remote-latest-page';
+import { useRemoteRevisionId, useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
 import {
   EditorMode,
   useEditorMode, useSelectedGrant,
@@ -86,7 +86,9 @@ export const PageEditorByHackmd = (): JSX.Element => {
   const { data: revisionIdHackmdSynced, mutate: mutateRevisionIdHackmdSynced } = useRevisionIdHackmdSynced();
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
   const { data: isHackmdDraftUpdatingInRealtime, mutate: mutateIsHackmdDraftUpdatingInRealtime } = useIsHackmdDraftUpdatingInRealtime();
-  const { data: remoteRevisionId, mutate: mutateRemoteRevisionId } = useRemoteRevisionId(revision?._id);
+  const { data: remoteRevisionId, mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
+
+  const updateStateAfterSave = useUpdateStateAfterSave();
 
   const hackmdEditorRef = useRef<HackEditorRef>(null);
 
@@ -123,8 +125,7 @@ export const PageEditorByHackmd = (): JSX.Element => {
         await router.push(`/${page._id}`);
       }
       else {
-        await mutateCurrentPageId(page._id);
-        await mutatePageData();
+        updateStateAfterSave(page._id);
       }
       setIsInitialized(false);
       mutateEditorMode(EditorMode.View);
@@ -134,7 +135,7 @@ export const PageEditorByHackmd = (): JSX.Element => {
       toastError(error.message);
     }
   // eslint-disable-next-line max-len
-  }, [editorMode, isSlackEnabled, currentPathname, slackChannels, grant, revision, revisionIdHackmdSynced, pageTags, saveOrUpdate, pageId, currentPagePath, mutatePageData, mutateTagsInfo, isNotFound, mutateEditorMode, router, mutateCurrentPageId]);
+  }, [editorMode, isSlackEnabled, currentPathname, slackChannels, grant, revision, revisionIdHackmdSynced, pageTags, saveOrUpdate, pageId, currentPagePath, mutatePageData, mutateTagsInfo, isNotFound, mutateEditorMode, router, useUpdateStateAfterSave]);
 
   // set handler to save and reload Page
   useEffect(() => {
@@ -255,9 +256,7 @@ export const PageEditorByHackmd = (): JSX.Element => {
       mutatePageData(res);
 
       // set updated data
-      mutateRemoteRevisionId(res.revision._id);
-      mutateRevisionIdHackmdSynced(res.page.revisionHackmdSynced);
-      mutateHasDraftOnHackmd(res.page.hasDraftOnHackmd);
+      updateStateAfterSave(res._id);
       mutateTagsInfo();
 
       logger.debug('success to save');
@@ -269,8 +268,9 @@ export const PageEditorByHackmd = (): JSX.Element => {
       toastError(error.message);
     }
   }, [
-    currentPagePath, currentPathname, isSlackEnabled, grant, slackChannels, pageId, revisionIdHackmdSynced, pageTags,
-    saveOrUpdate, mutatePageData, mutateRemoteRevisionId, mutateRevisionIdHackmdSynced, mutateHasDraftOnHackmd, mutateTagsInfo, t]);
+    currentPagePath, currentPathname, isSlackEnabled, grant, slackChannels, pageId, revisionIdHackmdSynced,
+    pageTags, saveOrUpdate, mutatePageData, useUpdateStateAfterSave, mutateTagsInfo, t,
+  ]);
 
   /**
    * onChange event of HackmdEditor handler

+ 3 - 0
packages/app/src/components/PageStatusAlert.tsx

@@ -119,6 +119,9 @@ export const PageStatusAlert = (): JSX.Element => {
     const isRevisionOutdated = revision?._id !== remoteRevisionId;
     const isHackmdDocumentOutdated = revisionIdHackmdSynced !== remoteRevisionId;
 
+    // 'revision?._id' and 'remoteRevisionId' are can not be undefined
+    if (revision?._id == null || remoteRevisionId == null) { return }
+
     // when remote revision is newer than both
     if (isHackmdDocumentOutdated && isRevisionOutdated) {
       return getContentsForUpdatedAlert();

+ 38 - 6
packages/app/src/services/renderer/renderer.tsx

@@ -335,6 +335,10 @@ export const generateViewOptions = (
     remarkPlugins.push(breaks);
   }
 
+  const rehypeSanitizePlugin: Pluggable<any[]> | (() => void) = config.isEnabledXssPrevention
+    ? [sanitize, deepmerge(commonSanitizeOption, lsxGrowiPlugin.sanitizeOption)]
+    : () => {};
+
   // add rehype plugins
   rehypePlugins.push(
     slug,
@@ -344,6 +348,7 @@ export const generateViewOptions = (
       drawioPlugin.sanitizeOption,
       lsxGrowiPlugin.sanitizeOption,
     )],
+    rehypeSanitizePlugin,
     katex,
     [toc.rehypePluginStore, { storeTocNode }],
     // [autoLinkHeadings, {
@@ -373,7 +378,9 @@ export const generateViewOptions = (
   // renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaks });
   // renderer.configure();
 
-  verifySanitizePlugin(options, false);
+  if (config.isEnabledXssPrevention) {
+    verifySanitizePlugin(options, false);
+  }
   return options;
 };
 
@@ -386,16 +393,23 @@ export const generateTocOptions = (config: RendererConfig, tocNode: HtmlElementN
   // add remark plugins
   // remarkPlugins.push();
 
+
+  const rehypeSanitizePlugin: Pluggable<any[]> | (() => void) = config.isEnabledXssPrevention
+    ? [sanitize, deepmerge(commonSanitizeOption, lsxGrowiPlugin.sanitizeOption)]
+    : () => {};
+
   // add rehype plugins
   rehypePlugins.push(
     [toc.rehypePluginRestore, { tocNode }],
-    [sanitize, commonSanitizeOption],
+    rehypeSanitizePlugin,
   );
   // renderer.rehypePlugins.push([autoLinkHeadings, {
   //   behavior: 'append',
   // }]);
 
-  verifySanitizePlugin(options);
+  if (config.isEnabledXssPrevention) {
+    verifySanitizePlugin(options);
+  }
   return options;
 };
 
@@ -417,6 +431,10 @@ export const generateSimpleViewOptions = (config: RendererConfig, pagePath: stri
     remarkPlugins.push(breaks);
   }
 
+  const rehypeSanitizePlugin: Pluggable<any[]> | (() => void) = config.isEnabledXssPrevention
+    ? [sanitize, deepmerge(commonSanitizeOption, lsxGrowiPlugin.sanitizeOption)]
+    : () => {};
+
   // add rehype plugins
   rehypePlugins.push(
     [lsxGrowiPlugin.rehypePlugin, { pagePath }],
@@ -426,6 +444,7 @@ export const generateSimpleViewOptions = (config: RendererConfig, pagePath: stri
       drawioPlugin.sanitizeOption,
       lsxGrowiPlugin.sanitizeOption,
     )],
+    rehypeSanitizePlugin,
     katex,
   );
 
@@ -436,7 +455,9 @@ export const generateSimpleViewOptions = (config: RendererConfig, pagePath: stri
     components.table = Table;
   }
 
-  verifySanitizePlugin(options, false);
+  if (config.isEnabledXssPrevention) {
+    verifySanitizePlugin(options, false);
+  }
   return options;
 };
 
@@ -458,6 +479,10 @@ export const generatePreviewOptions = (config: RendererConfig, pagePath: string)
     remarkPlugins.push(breaks);
   }
 
+  const rehypeSanitizePlugin: Pluggable<any[]> | (() => void) = config.isEnabledXssPrevention
+    ? [sanitize, deepmerge(commonSanitizeOption, lsxGrowiPlugin.sanitizeOption, addLineNumberAttribute.sanitizeOption)]
+    : () => {};
+
   // add rehype plugins
   rehypePlugins.push(
     [lsxGrowiPlugin.rehypePlugin, { pagePath }],
@@ -468,6 +493,7 @@ export const generatePreviewOptions = (config: RendererConfig, pagePath: string)
       drawioPlugin.sanitizeOption,
       addLineNumberAttribute.sanitizeOption,
     )],
+    rehypeSanitizePlugin,
     katex,
   );
 
@@ -493,12 +519,18 @@ export const generateOthersOptions = (config: RendererConfig): RendererOptions =
   // renderer.setMarkdownSettings({ breaks: rendererSettings.isEnabledLinebreaks });
   // renderer.configure();
 
+  const rehypeSanitizePlugin: Pluggable<any[]> | (() => void) = config.isEnabledXssPrevention
+    ? [sanitize, deepmerge(commonSanitizeOption)]
+    : () => {};
+
   // add rehype plugins
   rehypePlugins.push(
-    [sanitize, commonSanitizeOption],
+    rehypeSanitizePlugin,
   );
 
-  verifySanitizePlugin(options);
+  if (config.isEnabledXssPrevention) {
+    verifySanitizePlugin(options);
+  }
   return options;
 };