Procházet zdrojové kódy

Merge pull request #5430 from weseek/fix/permalink-not-found

fix: Permalink not found
Yuki Takei před 4 roky
rodič
revize
cbfa9f7225

+ 6 - 3
packages/app/src/client/services/ContextExtractor.tsx

@@ -7,7 +7,7 @@ import {
   useCurrentPageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
   useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
   useSlackChannels, useNotFoundTargetPathOrId, useIsSearchPage, useIsForbidden, useIsIdenticalPath,
-  useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader,
+  useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader, useIsNotFoundPermalink,
 } from '../../stores/context';
 import {
   useIsDeviceSmallerThanMd, useIsDeviceSmallerThanLg,
@@ -23,7 +23,8 @@ const jsonNull = 'null';
 const ContextExtractorOnce: FC = () => {
 
   const mainContent = document.querySelector('#content-main');
-  const notFoundContent = document.getElementById('growi-pagetree-not-found-context');
+  const notFoundContentForPt = document.getElementById('growi-pagetree-not-found-context');
+  const notFoundContent = document.getElementById('growi-not-found-context');
   const forbiddenContent = document.getElementById('forbidden-page');
 
   /*
@@ -76,7 +77,8 @@ const ContextExtractorOnce: FC = () => {
   const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
   const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
   const targetAndAncestors = JSON.parse(document.getElementById('growi-pagetree-target-and-ancestors')?.textContent || jsonNull);
-  const notFoundTargetPathOrId = JSON.parse(notFoundContent?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
+  const notFoundTargetPathOrId = JSON.parse(notFoundContentForPt?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
+  const isNotFoundPermalink = JSON.parse(notFoundContent?.getAttribute('data-is-not-found-permalink') || jsonNull);
   const slackChannels = mainContent?.getAttribute('data-slack-channels') || '';
   const isSearchPage = document.getElementById('search-page') != null;
 
@@ -132,6 +134,7 @@ const ContextExtractorOnce: FC = () => {
   useRevisionAuthor(revisionAuthor);
   useTargetAndAncestors(targetAndAncestors);
   useNotFoundTargetPathOrId(notFoundTargetPathOrId);
+  useIsNotFoundPermalink(isNotFoundPermalink);
   useIsSearchPage(isSearchPage);
 
   // Navigation

+ 17 - 12
packages/app/src/components/Page/NotFoundAlert.tsx

@@ -1,6 +1,7 @@
 import React, { useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
 import { UncontrolledTooltip } from 'reactstrap';
+import { useIsNotFoundPermalink } from '~/stores/context';
 
 import { EditorMode, useEditorMode } from '~/stores/ui';
 
@@ -14,6 +15,7 @@ const NotFoundAlert = (props: Props): JSX.Element => {
   const { isGuestUserMode } = props;
 
   const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
+  const { data: isNotFoundPermalink } = useIsNotFoundPermalink(); // TODO: Remove this when renaming on editor is implemented
 
   const isEditorMode = editorMode !== EditorMode.View;
 
@@ -41,19 +43,22 @@ const NotFoundAlert = (props: Props): JSX.Element => {
           <i className="icon-info pr-2 font-weight-bold" aria-hidden="true"></i>
           {t('not_found_page.page_not_exist_alert')}
         </h2>
-        <div id="create-page-btn-wrapper-for-tooltip" className="d-inline-block">
-          <button
-            type="button"
-            className={`pl-3 pr-3 btn bg-info text-white ${isGuestUserMode ? 'disabled' : ''}`}
-            onClick={clickHandler}
-          >
-            <i className="icon-note icon-fw" />
-            {t('not_found_page.Create Page')}
-          </button>
-        </div>
+        {
+          !isNotFoundPermalink && (
+            <div id="create-page-btn-wrapper-for-tooltip" className="d-inline-block">
+              <button
+                type="button"
+                className={`pl-3 pr-3 btn bg-info text-white ${isGuestUserMode ? 'disabled' : ''}`}
+                onClick={clickHandler}
+              >
+                <i className="icon-note icon-fw" />
+                {t('not_found_page.Create Page')}
+              </button>
+            </div>
+          )
+        }
 
-
-        {isGuestUserMode && (
+        {!isNotFoundPermalink && isGuestUserMode && (
           <UncontrolledTooltip placement="bottom" target="create-page-btn-wrapper-for-tooltip" fade={false}>
             {t('Not available for guest')}
           </UncontrolledTooltip>

+ 4 - 0
packages/app/src/interfaces/page-listing-results.ts

@@ -27,6 +27,10 @@ export interface NotFoundTargetPathOrId {
   notFoundTargetPathOrId: string
 }
 
+export interface IsNotFoundPermalink {
+  isNotFoundPermalink: boolean
+}
+
 
 export interface V5MigrationStatus {
   isV5Compatible : boolean,

+ 1 - 2
packages/app/src/server/models/page.ts

@@ -325,9 +325,8 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
   if (!hasSlash(pathOrId)) {
     const _id = pathOrId;
     const page = await this.findOne({ _id });
-    if (page == null) throw new Error('Page not found.');
 
-    path = page.path;
+    path = page == null ? '/' : page.path;
   }
   else {
     path = pathOrId;

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

@@ -266,12 +266,15 @@ module.exports = function(crowi, app) {
     renderVars.targetAndAncestors = { targetAndAncestors, rootPage };
   }
 
-  function addRenderVarsWhenNotFound(renderVars, pathOrId) {
+  async function addRenderVarsWhenNotFound(renderVars, pathOrId) {
     if (pathOrId == null) {
       return;
     }
 
     renderVars.notFoundTargetPathOrId = pathOrId;
+
+    const isPath = pathOrId.includes('/');
+    renderVars.isNotFoundPermalink = !isPath && !await Page.exists({ _id: pathOrId });
   }
 
   function replacePlaceholdersOfTemplate(template, req) {
@@ -329,7 +332,7 @@ module.exports = function(crowi, app) {
     await addRenderVarsForDescendants(renderVars, path, req.user, offset, limit, true);
     await addRenderVarsForPageTree(renderVars, pathOrId, req.user);
 
-    addRenderVarsWhenNotFound(renderVars, pathOrId);
+    await addRenderVarsWhenNotFound(renderVars, pathOrId);
 
     return res.render(view, renderVars);
   }

+ 5 - 0
packages/app/src/server/views/layout-growi/not_found.html

@@ -8,6 +8,11 @@
     data-not-found-target-path-or-id="{% if notFoundTargetPathOrId %}{{notFoundTargetPathOrId|json}}{% endif %}"
   >
   </div>
+  <div
+    id="growi-not-found-context"
+    data-is-not-found-permalink="{% if isNotFoundPermalink %}{{isNotFoundPermalink|json}}{% endif %}"
+  >
+  </div>
   <div class="grw-container-convertible">
     {% include '../widget/page_alerts.html' %}
     <div id="not-found-alert"></div>

+ 5 - 1
packages/app/src/stores/context.tsx

@@ -7,7 +7,7 @@ import { IUser } from '../interfaces/user';
 
 import { useStaticSWR } from './use-static-swr';
 
-import { TargetAndAncestors, NotFoundTargetPathOrId } from '../interfaces/page-listing-results';
+import { TargetAndAncestors, NotFoundTargetPathOrId, IsNotFoundPermalink } from '../interfaces/page-listing-results';
 
 type Nullable<T> = T | null;
 
@@ -131,6 +131,10 @@ export const useNotFoundTargetPathOrId = (initialData?: Nullable<NotFoundTargetP
   return useStaticSWR<Nullable<NotFoundTargetPathOrId>, Error>('notFoundTargetPathOrId', initialData);
 };
 
+export const useIsNotFoundPermalink = (initialData?: Nullable<IsNotFoundPermalink>): SWRResponse<Nullable<IsNotFoundPermalink>, Error> => {
+  return useStaticSWR<Nullable<IsNotFoundPermalink>, Error>('isNotFoundPermalink', initialData);
+};
+
 export const useIsAclEnabled = (initialData?: boolean) : SWRResponse<boolean, Error> => {
   return useStaticSWR<boolean, Error>('isAclEnabled', initialData);
 };

+ 4 - 3
packages/app/src/stores/ui.tsx

@@ -13,7 +13,7 @@ import loggerFactory from '~/utils/logger';
 import { useStaticSWR } from './use-static-swr';
 import {
   useCurrentPageId, useCurrentPagePath, useIsEditable, useIsTrashPage, useIsUserPage,
-  useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath,
+  useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath, useIsNotFoundPermalink,
 } from './context';
 import { IFocusable } from '~/client/interfaces/focusable';
 import { Nullable } from '~/interfaces/common';
@@ -311,12 +311,13 @@ export const useIsAbleToShowPageEditorModeManager = (): SWRResponse<boolean, Err
   const { data: isForbidden } = useIsForbidden();
   const { data: isTrashPage } = useIsTrashPage();
   const { data: isSharedUser } = useIsSharedUser();
+  const { data: isNotFoundPermalink } = useIsNotFoundPermalink();
 
-  const includesUndefined = [isNotCreatable, isForbidden, isTrashPage, isSharedUser].some(v => v === undefined);
+  const includesUndefined = [isNotCreatable, isForbidden, isTrashPage, isSharedUser, isNotFoundPermalink].some(v => v === undefined);
 
   return useSWRImmutable(
     includesUndefined ? null : key,
-    () => !isNotCreatable && !isForbidden && !isTrashPage && !isSharedUser,
+    () => !isNotCreatable && !isForbidden && !isTrashPage && !isSharedUser && !isNotFoundPermalink,
   );
 };