Преглед изворни кода

Merge branch 'support/apply-nextjs-2' of https://github.com/weseek/growi into support/apply-nextjs-2

Shun Miyazawa пре 3 година
родитељ
комит
ee4c10efc8

+ 0 - 1
packages/app/src/components/PageEditor/Editor.tsx

@@ -20,7 +20,6 @@ import CodeMirrorEditor from './CodeMirrorEditor';
 import pasteHelper from './PasteHelper';
 import TextAreaEditor from './TextAreaEditor';
 
-
 type EditorPropsType = {
   value?: string,
   isGfmMode?: boolean,

+ 142 - 214
packages/app/src/components/PageEditorByHackmd.jsx

@@ -1,7 +1,10 @@
-import React from 'react';
+import React, {
+  useCallback, useEffect, useRef, useState,
+} from 'react';
 
-import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
+import { useTranslation } from 'react-i18next';
+
 
 import AppContainer from '~/client/services/AppContainer';
 import EditorContainer from '~/client/services/EditorContainer';
@@ -9,9 +12,7 @@ import PageContainer from '~/client/services/PageContainer';
 import { apiPost } from '~/client/util/apiv1-client';
 import { getOptionsToSave } from '~/client/util/editor';
 import { useCurrentPagePath, useCurrentPageId } from '~/stores/context';
-import {
-  useSWRxSlackChannels, useIsSlackEnabled, usePageTagsForEditors, useIsEnabledUnsavedWarning,
-} from '~/stores/editor';
+import { useSWRxSlackChannels, useIsSlackEnabled, usePageTagsForEditors } from '~/stores/editor';
 import {
   useEditorMode, useSelectedGrant,
 } from '~/stores/ui';
@@ -22,86 +23,71 @@ import { withUnstatedContainers } from './UnstatedUtils';
 
 const logger = loggerFactory('growi:PageEditorByHackmd');
 
-class PageEditorByHackmd extends React.Component {
-
-  constructor(props) {
-    super(props);
+const PageEditorByHackmd = (props) => {
+  const { appContainer, pageContainer, editorContainer } = props; // wip
 
-    this.state = {
-      isInitialized: false,
-      isInitializing: false,
-      // for error
-      hasError: false,
-      errorMessage: '',
-      errorReason: '',
+  const { t } = useTranslation();
+  const { data: editorMode } = useEditorMode();
+  const { data: currentPagePath } = useCurrentPagePath();
+  const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
+  const { data: isSlackEnabled } = useIsSlackEnabled();
+  const { data: pageId } = useCurrentPageId();
+  const { data: pageTags } = usePageTagsForEditors(pageId);
+  const { data: grant } = useSelectedGrant();
+
+  const slackChannels = slackChannelsData.toString();
+
+  const [isInitialized, setIsInitialized] = useState(false);
+  const [isInitializing, setIsInitializing] = useState(false);
+  // for error
+  const [hasError, setHasError] = useState(false);
+  const [errorMessage, setErrorMessage] = useState('');
+  const [errorReason, setErrorReason] = useState('');
+
+  const hackmdEditorRef = useRef(null);
+
+  useEffect(() => {
+    const pageEditorByHackmdInstance = {
+      getMarkdown: () => {
+        if (!isInitialized) {
+          return Promise.reject(new Error(t('hackmd.not_initialized')));
+        }
+
+        return hackmdEditorRef.current.getValue();
+      },
+      reset: () => {
+        setIsInitialized(false);
+      },
     };
+    appContainer.registerComponentInstance('PageEditorByHackmd', pageEditorByHackmdInstance);
+  }, [appContainer, isInitialized, t]);
 
-    this.getHackmdUri = this.getHackmdUri.bind(this);
-    this.startToEdit = this.startToEdit.bind(this);
-    this.resumeToEdit = this.resumeToEdit.bind(this);
-    this.onSaveWithShortcut = this.onSaveWithShortcut.bind(this);
-    this.hackmdEditorChangeHandler = this.hackmdEditorChangeHandler.bind(this);
-    this.penpalErrorOccuredHandler = this.penpalErrorOccuredHandler.bind(this);
-  }
-
-  UNSAFE_componentWillMount() {
-    this.props.appContainer.registerComponentInstance('PageEditorByHackmd', this);
-  }
-
-  /**
-   * return markdown document of HackMD
-   * @return {Promise<string>}
-   */
-  getMarkdown() {
-    const { t } = this.props;
-    if (!this.state.isInitialized) {
-      return Promise.reject(new Error(t('hackmd.not_initialized')));
-    }
-
-    return this.hackmdEditor.getValue();
-  }
-
-  /**
-   * reset initialized status
-   */
-  reset() {
-    this.setState({ isInitialized: false });
-  }
-
-  getHackmdUri() {
-    const envVars = this.props.appContainer.getConfig().env;
+  const getHackmdUri = useCallback(() => {
+    const envVars = appContainer.getConfig().env;
     return envVars.HACKMD_URI;
-  }
+  }, [appContainer]);
 
-  get isResume() {
-    const { pageContainer } = this.props;
+  const isResume = useCallback(() => {
     const {
       pageIdOnHackmd, hasDraftOnHackmd, isHackmdDraftUpdatingInRealtime,
     } = pageContainer.state;
-
     const isPageExistsOnHackmd = (pageIdOnHackmd != null);
     return (isPageExistsOnHackmd && hasDraftOnHackmd) || isHackmdDraftUpdatingInRealtime;
-  }
+  }, [pageContainer.state]);
 
-  /**
-   * Start integration with HackMD
-   */
-  async startToEdit() {
-    const { pageContainer } = this.props;
-    const hackmdUri = this.getHackmdUri();
+  const startToEdit = useCallback(async() => {
+    const hackmdUri = getHackmdUri();
 
     if (hackmdUri == null) {
       // do nothing
       return;
     }
 
-    this.setState({
-      isInitialized: false,
-      isInitializing: true,
-    });
+    setIsInitialized(false);
+    setIsInitializing(true);
 
     const params = {
-      pageId: pageContainer.state.pageId,
+      pageId,
     };
 
     try {
@@ -119,31 +105,23 @@ class PageEditorByHackmd extends React.Component {
     catch (err) {
       pageContainer.showErrorToastr(err);
 
-      this.setState({
-        hasError: true,
-        errorMessage: 'GROWI server failed to connect to HackMD.',
-        errorReason: err.toString(),
-      });
+      setHasError(true);
+      setErrorMessage('GROWI server failed to connect to HackMD.');
+      setErrorReason(err.toString());
     }
 
-    this.setState({
-      isInitialized: true,
-      isInitializing: false,
-    });
-  }
+    setIsInitialized(true);
+    setIsInitializing(false);
+  }, [getHackmdUri, pageContainer, pageId]);
 
   /**
    * Start to edit w/o any api request
    */
-  resumeToEdit() {
-    this.setState({ isInitialized: true });
-  }
+  const resumeToEdit = useCallback(() => {
+    setIsInitialized(true);
+  }, []);
 
-  /**
-   * Reset draft
-   */
-  async discardChanges() {
-    const { pageContainer } = this.props;
+  const discardChanges = useCallback(async() => {
     const { pageId } = pageContainer.state;
 
     try {
@@ -153,7 +131,7 @@ class PageEditorByHackmd extends React.Component {
         throw new Error(res.error);
       }
 
-      this.props.pageContainer.setState({
+      pageContainer.setState({
         isHackmdDraftUpdatingInRealtime: false,
         hasDraftOnHackmd: false,
         pageIdOnHackmd: res.pageIdOnHackmd,
@@ -165,24 +143,21 @@ class PageEditorByHackmd extends React.Component {
       logger.error(err);
       pageContainer.showErrorToastr(err);
     }
-  }
+  }, [pageContainer]);
 
   /**
    * save and update state of containers
    * @param {string} markdown
    */
-  async onSaveWithShortcut(markdown) {
-    const {
-      isSlackEnabled, slackChannels, pageContainer, editorContainer, grant, grantGroupId, grantGroupName, pageTags, mutateIsEnabledUnsavedWarning,
-    } = this.props;
-    const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, grant, grantGroupId, grantGroupName, pageTags);
+  const onSaveWithShortcut = useCallback(async(markdown) => {
+    const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, grant, grant.grantedGroup.id, grant.grantedGroup.name, pageTags);
 
     try {
       // disable unsaved warning
-      mutateIsEnabledUnsavedWarning(false);
+      editorContainer.disableUnsavedWarning();
 
       // eslint-disable-next-line no-unused-vars
-      const { page, tags } = await pageContainer.save(markdown, this.props.editorMode, optionsToSave);
+      const { page, tags } = await pageContainer.save(markdown, editorMode, optionsToSave);
       logger.debug('success to save');
 
       pageContainer.showSuccessToastr();
@@ -194,14 +169,13 @@ class PageEditorByHackmd extends React.Component {
       logger.error('failed to save', error);
       pageContainer.showErrorToastr(error);
     }
-  }
+  }, [editorContainer, editorMode, grant, isSlackEnabled, pageContainer, pageTags, slackChannels]);
 
   /**
    * onChange event of HackmdEditor handler
    */
-  async hackmdEditorChangeHandler(body) {
-    const hackmdUri = this.getHackmdUri();
-    const { pageContainer, mutateIsEnabledUnsavedWarning } = this.props;
+  const hackmdEditorChangeHandler = useCallback(async(body) => {
+    const hackmdUri = getHackmdUri();
 
     if (hackmdUri == null) {
       // do nothing
@@ -214,7 +188,7 @@ class PageEditorByHackmd extends React.Component {
     }
 
     // enable unsaved warning
-    mutateIsEnabledUnsavedWarning(true);
+    editorContainer.enableUnsavedWarning();
 
     const params = {
       pageId: pageContainer.state.pageId,
@@ -225,23 +199,18 @@ class PageEditorByHackmd extends React.Component {
     catch (err) {
       logger.error(err);
     }
-  }
-
-  penpalErrorOccuredHandler(error) {
-    const { pageContainer, t } = this.props;
+  }, [editorContainer, getHackmdUri, pageContainer.state.markdown, pageContainer.state.pageId]);
 
+  const penpalErrorOccuredHandler = useCallback((error) => {
     pageContainer.showErrorToastr(error);
 
-    this.setState({
-      hasError: true,
-      errorMessage: t('hackmd.fail_to_connect'),
-      errorReason: error.toString(),
-    });
-  }
+    setHasError(true);
+    setErrorMessage(t('hackmd.fail_to_connect'));
+    setErrorReason(error.toString());
+  }, [pageContainer, t]);
 
-  renderPreInitContent() {
-    const hackmdUri = this.getHackmdUri();
-    const { pageContainer, t } = this.props;
+  const renderPreInitContent = useCallback(() => {
+    const hackmdUri = getHackmdUri();
     const {
       revisionId, revisionIdHackmdSynced, remoteRevisionId, pageId,
     } = pageContainer.state;
@@ -280,7 +249,7 @@ class PageEditorByHackmd extends React.Component {
     /*
      * Resume to edit or discard changes
      */
-    else if (this.isResume) {
+    else if (isResume()) {
       const isHackmdDocumentOutdated = revisionIdHackmdSynced !== remoteRevisionId;
 
       content = (
@@ -299,8 +268,8 @@ class PageEditorByHackmd extends React.Component {
                   <button
                     className="btn btn-link btn-view-outdated-draft p-0"
                     type="button"
-                    disabled={this.state.isInitializing}
-                    onClick={() => { return this.resumeToEdit() }}
+                    disabled={isInitializing}
+                    onClick={resumeToEdit}
                   >
                     {t('hackmd.view_outdated_draft')}
                   </button>
@@ -312,10 +281,10 @@ class PageEditorByHackmd extends React.Component {
           { !isHackmdDocumentOutdated && (
             <div className="text-center hackmd-resume-button-container mb-3">
               <button
-                className="btn btn-success btn-lg"
+                className="btn btn-success btn-lg waves-effect waves-light"
                 type="button"
-                disabled={this.state.isInitializing}
-                onClick={() => { return this.resumeToEdit() }}
+                disabled={isInitializing}
+                onClick={resumeToEdit}
               >
                 <span className="btn-label"><i className="icon-fw icon-control-end"></i></span>
                 <span className="btn-text">{t('hackmd.resume_to_edit')}</span>
@@ -325,9 +294,9 @@ class PageEditorByHackmd extends React.Component {
 
           <div className="text-center hackmd-discard-button-container mb-3">
             <button
-              className="btn btn-outline-secondary btn-lg"
+              className="btn btn-outline-secondary btn-lg waves-effect waves-light"
               type="button"
-              onClick={() => { return this.discardChanges() }}
+              onClick={discardChanges}
             >
               <span className="btn-label"><i className="icon-fw icon-control-start"></i></span>
               <span className="btn-text">{t('hackmd.discard_changes')}</span>
@@ -348,10 +317,10 @@ class PageEditorByHackmd extends React.Component {
           <p className="text-muted text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <div className="text-center hackmd-start-button-container mb-3">
             <button
-              className="btn btn-info btn-lg"
+              className="btn btn-info btn-lg waves-effect waves-light"
               type="button"
-              disabled={isRevisionOutdated || this.state.isInitializing}
-              onClick={() => { return this.startToEdit() }}
+              disabled={isRevisionOutdated || isInitializing}
+              onClick={startToEdit}
             >
               <span className="btn-label"><i className="icon-fw icon-paper-plane"></i></span>
               {t('hackmd.start_to_edit')}
@@ -367,116 +336,75 @@ class PageEditorByHackmd extends React.Component {
         {content}
       </div>
     );
-  }
-
-  render() {
-    const hackmdUri = this.getHackmdUri();
-    const { pageContainer, t } = this.props;
-    const {
-      markdown, pageIdOnHackmd,
-    } = pageContainer.state;
-
-
-    let content;
-
-    if (this.state.isInitialized) {
-      content = (
-        <HackmdEditor
-          ref={(c) => { this.hackmdEditor = c }}
-          hackmdUri={hackmdUri}
-          pageIdOnHackmd={pageIdOnHackmd}
-          initializationMarkdown={this.isResume ? null : markdown}
-          onChange={this.hackmdEditorChangeHandler}
-          onSaveWithShortcut={(document) => {
-            this.onSaveWithShortcut(document);
-          }}
-          onPenpalErrorOccured={this.penpalErrorOccuredHandler}
-        >
-        </HackmdEditor>
-      );
-    }
-    else {
-      content = this.renderPreInitContent();
-    }
+  }, [discardChanges, getHackmdUri, isInitializing, isResume, pageContainer.state, resumeToEdit, startToEdit, t]);
 
+  if (editorMode == null) {
+    return null;
+  }
 
-    return (
-      <div className="position-relative">
+  const hackmdUri = getHackmdUri();
+  const {
+    markdown, pageIdOnHackmd,
+  } = pageContainer.state;
+
+  let content;
+
+  if (isInitialized) {
+    content = (
+      <HackmdEditor
+        ref={hackmdEditorRef}
+        hackmdUri={hackmdUri}
+        pageIdOnHackmd={pageIdOnHackmd}
+        initializationMarkdown={isResume() ? null : markdown}
+        onChange={hackmdEditorChangeHandler}
+        onSaveWithShortcut={(document) => {
+          onSaveWithShortcut(document);
+        }}
+        onPenpalErrorOccured={penpalErrorOccuredHandler}
+      >
+      </HackmdEditor>
+    );
+  }
+  else {
+    content = renderPreInitContent();
+  }
 
-        {content}
 
-        { this.state.hasError && (
-          <div className="hackmd-error position-absolute d-flex flex-column justify-content-center align-items-center">
-            <div className="bg-box p-5 text-center">
-              <h2 className="text-warning"><i className="icon-fw icon-exclamation"></i> {t('hackmd.integration_failed')}</h2>
-              <h4>{this.state.errorMessage}</h4>
-              <p className="card well text-danger">
-                {this.state.errorReason}
-              </p>
-              {/* eslint-disable-next-line react/no-danger */}
-              <p dangerouslySetInnerHTML={{ __html: t('hackmd.check_configuration') }} />
-            </div>
+  return (
+    <div className="position-relative">
+
+      {content}
+
+      { hasError && (
+        <div className="hackmd-error position-absolute d-flex flex-column justify-content-center align-items-center">
+          <div className="bg-box p-5 text-center">
+            <h2 className="text-warning"><i className="icon-fw icon-exclamation"></i> {t('hackmd.integration_failed')}</h2>
+            <h4>{errorMessage}</h4>
+            <p className="card well text-danger">
+              {errorReason}
+            </p>
+            {/* eslint-disable-next-line react/no-danger */}
+            <p dangerouslySetInnerHTML={{ __html: t('hackmd.check_configuration') }} />
           </div>
-        ) }
+        </div>
+      ) }
 
-      </div>
-    );
-  }
+    </div>
+  );
 
-}
+};
 
 PageEditorByHackmd.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
 
   // TODO: remove this when omitting unstated is completed
-  editorMode: PropTypes.string.isRequired,
-  isSlackEnabled: PropTypes.bool.isRequired,
   pageTags: PropTypes.arrayOf(PropTypes.string),
-  slackChannels: PropTypes.string.isRequired,
-  grant: PropTypes.number.isRequired,
   grantGroupId: PropTypes.string,
   grantGroupName: PropTypes.string,
-  mutateIsEnabledUnsavedWarning: PropTypes.func,
 };
 
-/**
- * Wrapper component for using unstated
- */
-const PageEditorByHackmdHOCWrapper = withUnstatedContainers(PageEditorByHackmd, [AppContainer, PageContainer, EditorContainer]);
-
-const PageEditorByHackmdWrapper = (props) => {
-  const { t } = useTranslation();
-  const { data: editorMode } = useEditorMode();
-  const { data: currentPagePath } = useCurrentPagePath();
-  const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
-  const { data: isSlackEnabled } = useIsSlackEnabled();
-  const { data: pageId } = useCurrentPageId();
-  const { data: pageTags } = usePageTagsForEditors(pageId);
-  const { data: grantData } = useSelectedGrant();
-  const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
-
-  if (editorMode == null) {
-    return null;
-  }
-
-  return (
-    <PageEditorByHackmdHOCWrapper
-      {...props}
-      t={t}
-      editorMode={editorMode}
-      isSlackEnabled={isSlackEnabled}
-      slackChannels={slackChannelsData.toString()}
-      pageTags={pageTags}
-      grant={grantData.grant}
-      grantGroupId={grantData.grantGroup?.id}
-      grantGroupName={grantData.grantedGroup?.name}
-      mutateIsEnabledUnsavedWarning={mutateIsEnabledUnsavedWarning}
-    />
-  );
-};
+const PageEditorByHackmdWrapper = withUnstatedContainers(PageEditorByHackmd, [AppContainer, PageContainer, EditorContainer]);
 
 export default PageEditorByHackmdWrapper;

+ 7 - 3
packages/app/src/components/Sidebar/PageTree/PrivateLegacyPagesLink.tsx

@@ -1,14 +1,18 @@
 import React, { FC, memo } from 'react';
 
+import Link from 'next/link';
+
 import { useTranslation } from 'next-i18next';
 
 export const PrivateLegacyPagesLink: FC = memo(() => {
   const { t } = useTranslation();
 
   return (
-    <a href="/_private-legacy-pages" className="h5 grw-private-legacy-pages-anchor text-decoration-none">
-      <i className="icon-drawer mr-2"></i> {t('pagetree.private_legacy_pages')}
-    </a>
+    <Link href="/_private-legacy-pages" prefetch={false}>
+      <a className="h5 grw-private-legacy-pages-anchor text-decoration-none">
+        <i className="icon-drawer mr-2"></i> {t('pagetree.private_legacy_pages')}
+      </a>
+    </Link>
   );
 });
 

+ 176 - 0
packages/app/src/pages/_private-legacy-pages.page.tsx

@@ -0,0 +1,176 @@
+import {
+  NextPage, GetServerSideProps, GetServerSidePropsContext,
+} from 'next';
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
+import dynamic from 'next/dynamic';
+import Head from 'next/head';
+
+import { BasicLayout } from '~/components/Layout/BasicLayout';
+import { CrowiRequest } from '~/interfaces/crowi-request';
+import { RendererConfig } from '~/interfaces/services/renderer';
+import { ISidebarConfig } from '~/interfaces/sidebar-config';
+import { IUser, IUserHasId } from '~/interfaces/user';
+import { IUserUISettings } from '~/interfaces/user-ui-settings';
+import UserUISettings from '~/server/models/user-ui-settings';
+import {
+  useCsrfToken, useCurrentUser, useIsSearchPage, useIsSearchScopeChildrenAsDefault,
+  useIsSearchServiceConfigured, useIsSearchServiceReachable, useRendererConfig,
+} from '~/stores/context';
+import {
+  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed,
+  useCurrentSidebarContents, useCurrentProductNavWidth,
+} from '~/stores/ui';
+
+
+import {
+  CommonProps, getNextI18NextConfig, getServerSideCommonProps, useCustomTitle,
+} from './utils/commons';
+
+type Props = CommonProps & {
+  currentUser: IUser,
+
+  isSearchServiceConfigured: boolean,
+  isSearchServiceReachable: boolean,
+  isSearchScopeChildrenAsDefault: boolean,
+
+  // UI
+  userUISettings?: IUserUISettings
+  // Sidebar
+  sidebarConfig: ISidebarConfig,
+
+  // Render config
+  rendererConfig: RendererConfig,
+
+};
+
+const PrivateLegacyPage: NextPage<Props> = (props: Props) => {
+  const { userUISettings } = props;
+
+  const PrivateLegacyPages = dynamic(() => import('~/components/PrivateLegacyPages'), { ssr: false });
+
+  // commons
+  useCsrfToken(props.csrfToken);
+
+  useCurrentUser(props.currentUser ?? null);
+
+  // Search
+  useIsSearchPage(true);
+  useIsSearchServiceConfigured(props.isSearchServiceConfigured);
+  useIsSearchServiceReachable(props.isSearchServiceReachable);
+  useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
+
+  // UserUISettings
+  usePreferDrawerModeByUser(userUISettings?.preferDrawerModeByUser ?? props.sidebarConfig.isSidebarDrawerMode);
+  usePreferDrawerModeOnEditByUser(userUISettings?.preferDrawerModeOnEditByUser);
+  useSidebarCollapsed(userUISettings?.isSidebarCollapsed ?? props.sidebarConfig.isSidebarClosedAtDockMode);
+  useCurrentSidebarContents(userUISettings?.currentSidebarContents);
+  useCurrentProductNavWidth(userUISettings?.currentProductNavWidth);
+
+  // render config
+  useRendererConfig(props.rendererConfig);
+
+  return (
+    <>
+      <Head>
+        {/*
+        {renderScriptTagByName('drawio-viewer')}
+        {renderScriptTagByName('highlight-addons')}
+        */}
+      </Head>
+      <div className="on-search">
+        <BasicLayout title={useCustomTitle(props, 'GROWI')}>
+
+          <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
+          <div id="main" className="main search-page mt-0">
+
+            <div id="private-regacy-pages">
+              <PrivateLegacyPages />
+            </div>
+
+          </div>
+        </BasicLayout>
+      </div>
+    </>
+  );
+};
+
+async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
+  const req = context.req as CrowiRequest<IUserHasId & any>;
+  const { user } = req;
+
+  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
+  if (userUISettings != null) {
+    props.userUISettings = userUISettings.toObject();
+  }
+}
+
+async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
+  const req: CrowiRequest = context.req as CrowiRequest;
+  const { crowi } = req;
+  const { configManager, searchService } = crowi;
+
+  props.isSearchServiceConfigured = searchService.isConfigured;
+  props.isSearchServiceReachable = searchService.isReachable;
+  props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault');
+
+  props.sidebarConfig = {
+    isSidebarDrawerMode: configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
+    isSidebarClosedAtDockMode: configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
+  };
+
+  props.rendererConfig = {
+    isEnabledLinebreaks: configManager.getConfig('markdown', 'markdown:isEnabledLinebreaks'),
+    isEnabledLinebreaksInComments: configManager.getConfig('markdown', 'markdown:isEnabledLinebreaksInComments'),
+    adminPreferredIndentSize: configManager.getConfig('markdown', 'markdown:adminPreferredIndentSize'),
+    isIndentSizeForced: configManager.getConfig('markdown', 'markdown:isIndentSizeForced'),
+
+    plantumlUri: process.env.PLANTUML_URI ?? null,
+    blockdiagUri: process.env.BLOCKDIAG_URI ?? null,
+
+    // XSS Options
+    isEnabledXssPrevention: configManager.getConfig('markdown', 'markdown:xss:isEnabledPrevention'),
+    attrWhiteList: crowi.xssService.getAttrWhiteList(),
+    tagWhiteList: crowi.xssService.getTagWhiteList(),
+    highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
+  };
+}
+
+/**
+ * for Server Side Translations
+ * @param context
+ * @param props
+ * @param namespacesRequired
+ */
+async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise<void> {
+  const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired);
+  props._nextI18Next = nextI18NextConfig._nextI18Next;
+}
+
+export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
+  const req = context.req as CrowiRequest<IUserHasId & any>;
+  const { user } = req;
+
+  const result = await getServerSideCommonProps(context);
+
+  // check for presence
+  // see: https://github.com/vercel/next.js/issues/19271#issuecomment-730006862
+  if (!('props' in result)) {
+    throw new Error('invalid getSSP result');
+  }
+
+  const props: Props = result.props as Props;
+
+  if (user != null) {
+    props.currentUser = user.toObject();
+  }
+
+  await injectUserUISettings(context, props);
+  await injectServerConfigurations(context, props);
+  await injectNextI18NextConfigurations(context, props, ['translation']);
+
+  return {
+    props,
+  };
+};
+
+export default PrivateLegacyPage;

+ 10 - 0
packages/app/src/pages/admin/[[...path]].page.tsx

@@ -28,11 +28,13 @@ import AdminSamlSecurityContainer from '~/client/services/AdminSamlSecurityConta
 import AdminSlackIntegrationLegacyContainer from '~/client/services/AdminSlackIntegrationLegacyContainer';
 import AdminTwitterSecurityContainer from '~/client/services/AdminTwitterSecurityContainer';
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { SupportedActionType } from '~/interfaces/activity';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import PluginUtils from '~/server/plugins/plugin-utils';
 import ConfigLoader from '~/server/service/config-loader';
 import {
   useCurrentUser, /* useSearchServiceConfigured, */ useIsAclEnabled, useIsMailerSetup, useIsSearchServiceReachable, useSiteUrl,
+  useAuditLogEnabled, useAuditLogAvailableActions,
 } from '~/stores/context';
 
 import {
@@ -75,6 +77,8 @@ type Props = CommonProps & {
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isMailerSetup: boolean,
+  auditLogEnabled: boolean,
+  auditLogAvailableActions: SupportedActionType[],
 
   siteUrl: string,
 };
@@ -194,6 +198,9 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
 
   // useEnvVars(props.envVars);
 
+  useAuditLogEnabled(props.auditLogEnabled);
+  useAuditLogAvailableActions(props.auditLogAvailableActions);
+
   const injectableContainers: Container<any>[] = [];
 
   if (isClient()) {
@@ -318,6 +325,9 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceReachable = searchService.isReachable;
 
+  props.auditLogEnabled = crowi.configManager.getConfig('crowi', 'app:auditLogEnabled');
+  props.auditLogAvailableActions = crowi.activityService.getAvailableActions(false);
+
   return {
     props,
   };

+ 1 - 1
packages/app/src/server/crowi/express-init.js

@@ -150,7 +150,7 @@ module.exports = function(crowi, app) {
 
   // csurf should be initialized after express-session
   // default methods + PUT. See: https://expressjs.com/en/resources/middleware/csurf.html#ignoremethods
-  app.use(csrf({ ignoreMethods: ['GET', 'HEAD', 'OPTIONS', 'PUT', 'POST'], cookie: false }));
+  app.use(csrf({ ignoreMethods: ['GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'DELETE'], cookie: false }));
 
   // passport
   debug('initialize Passport');

+ 2 - 3
packages/app/src/server/routes/index.js

@@ -144,7 +144,7 @@ module.exports = function(crowi, app) {
   // app.get('/admin/user-group-detail/:id'                , loginRequiredStrictly, adminRequired, admin.userGroup.detail);
 
   // auditLog admin
-  app.get('/admin/audit-log'                            , loginRequiredStrictly, adminRequired, admin.auditLog.index);
+  // app.get('/admin/audit-log'                            , loginRequiredStrictly, adminRequired, admin.auditLog.index);
 
   // importer management for admin
   // app.get('/admin/importer'                     , loginRequiredStrictly , adminRequired , admin.importer.index);
@@ -239,8 +239,7 @@ module.exports = function(crowi, app) {
     .get('/:token', injectResetOrderByTokenMiddleware, forgotPassword.resetPassword)
     .use(forgotPassword.handleErrosMiddleware));
 
-  app.use('/_private-legacy-pages', express.Router()
-    .get('/', injectUserUISettings, privateLegacyPages.renderPrivateLegacyPages));
+  app.get('/_private-legacy-pages', next.delegateToNext);
   app.use('/user-activation', express.Router()
     .get('/:token', applicationInstalled, injectUserRegistrationOrderByTokenMiddleware, userActivation.form)
     .use(userActivation.tokenErrorHandlerMiddeware));