yohei0125 4 лет назад
Родитель
Сommit
c9eccdc42a

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

@@ -7,7 +7,7 @@ import {
   useIsDeleted, useIsNotCreatable, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
   useCurrentPageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
   useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
-  useSlackChannels, useNotFoundTargetPathOrId, useIsSearchPage, useIsForbidden, useIsIdenticalPath,
+  useSlackChannels, useNotFoundTargetPathOrId, useEmptyPagePermalink, useIsSearchPage, useIsForbidden, useIsIdenticalPath,
   useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader, useIsNotFoundPermalink,
 } from '../../stores/context';
 import {
@@ -80,6 +80,7 @@ const ContextExtractorOnce: FC = () => {
   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(notFoundContentForPt?.getAttribute('data-not-found-target-path-or-id') || jsonNull);
+  const emptyPagePermalink = JSON.parse(notFoundContentForPt?.getAttribute('data-empty-page-permalink') || 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;
@@ -137,6 +138,7 @@ const ContextExtractorOnce: FC = () => {
   useRevisionAuthor(revisionAuthor);
   useTargetAndAncestors(targetAndAncestors);
   useNotFoundTargetPathOrId(notFoundTargetPathOrId);
+  useEmptyPagePermalink(emptyPagePermalink);
   useIsNotFoundPermalink(isNotFoundPermalink);
   useIsSearchPage(isSearchPage);
 

+ 7 - 5
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -22,7 +22,7 @@ import {
 
 import {
   useCurrentCreatedAt, useCurrentUpdatedAt, useCurrentPageId, useRevisionId, useCurrentPagePath,
-  useCreator, useRevisionAuthor, useCurrentUser, useIsGuestUser, useIsSharedUser, useShareLinkId,
+  useCreator, useRevisionAuthor, useCurrentUser, useIsGuestUser, useIsSharedUser, useShareLinkId, useEmptyPagePermalink,
 } from '~/stores/context';
 import { useSWRTagsInfo } from '~/stores/page';
 
@@ -141,6 +141,7 @@ const GrowiContextualSubNavigation = (props) => {
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isSharedUser } = useIsSharedUser();
   const { data: shareLinkId } = useShareLinkId();
+  const { data: emptyPagePermalink } = useEmptyPagePermalink();
 
   const { data: isAbleToShowPageManagement } = useIsAbleToShowPageManagement();
   const { data: isAbleToShowTagLabel } = useIsAbleToShowTagLabel();
@@ -229,13 +230,14 @@ const GrowiContextualSubNavigation = (props) => {
 
     const className = `d-flex flex-column align-items-end justify-content-center ${isViewMode ? ' h-50' : ''}`;
 
+    const displayedPageId = pageId ?? emptyPagePermalink;
     return (
       <>
         <div className={className}>
-          { pageId != null && isViewMode && (
+          { displayedPageId != null && isViewMode && (
             <SubNavButtons
               isCompactMode={isCompactMode}
-              pageId={pageId}
+              pageId={displayedPageId}
               shareLinkId={shareLinkId}
               revisionId={revisionId}
               path={path}
@@ -244,7 +246,7 @@ const GrowiContextualSubNavigation = (props) => {
               additionalMenuItemRenderer={props => (
                 <AdditionalMenuItems
                   {...props}
-                  pageId={pageId}
+                  pageId={displayedPageId}
                   revisionId={revisionId}
                   isLinkSharingDisabled={isLinkSharingDisabled}
                   onClickTemplateMenuItem={templateMenuItemClickHandler}
@@ -280,7 +282,7 @@ const GrowiContextualSubNavigation = (props) => {
     isLinkSharingDisabled, isDeviceSmallerThanMd, isGuestUser, isSharedUser, currentUser,
     isViewMode, isAbleToShowPageEditorModeManager, isAbleToShowPageManagement,
     duplicateItemClickedHandler, renameItemClickedHandler, deleteItemClickedHandler,
-    path, templateMenuItemClickHandler, isPageTemplateModalShown,
+    path, templateMenuItemClickHandler, isPageTemplateModalShown, emptyPagePermalink,
   ]);
 
 

+ 2 - 1
packages/app/src/components/Navbar/SubNavButtons.tsx

@@ -212,10 +212,11 @@ export const SubNavButtons = (props: SubNavButtonsProps): JSX.Element => {
 
   const { data: pageInfo, error } = useSWRxPageInfo(pageId ?? null, shareLinkId);
 
-  if (revisionId == null || error != null) {
+  if (pageId == null && (revisionId == null || error != null)) {
     return <></>;
   }
 
+  // it becomes null when opening empty page
   if (!isIPageInfoForOperation(pageInfo)) {
     return <></>;
   }

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

@@ -105,7 +105,7 @@ export type IDataWithMeta<D = unknown, M = unknown> = {
 
 export type IPageWithMeta<M = IPageInfoAll> = IDataWithMeta<IPageHasId, M>;
 
-export type IPageToDeleteWithMeta = IDataWithMeta<HasObjectId & (IPage | { path: string, revision: string }), IPageInfoForEntity | unknown>;
+export type IPageToDeleteWithMeta = IDataWithMeta<HasObjectId & (IPage | { path: string, revision?: string }), IPageInfoForEntity | unknown>;
 export type IPageToRenameWithMeta = IPageToDeleteWithMeta;
 
 export type IDeleteSinglePageApiv1Result = {

+ 42 - 10
packages/app/src/server/routes/page.js

@@ -278,6 +278,13 @@ module.exports = function(crowi, app) {
     renderVars.isNotFoundPermalink = !isPath && !await Page.exists({ _id: pathOrId });
   }
 
+  async function addRenderVarsWhenEmptyPage(renderVars, emptyPagePermalink) {
+    if (emptyPagePermalink == null) {
+      return;
+    }
+    renderVars.emptyPagePermalink = emptyPagePermalink;
+  }
+
   function replacePlaceholdersOfTemplate(template, req) {
     if (req.user == null) {
       return '';
@@ -332,9 +339,11 @@ module.exports = function(crowi, app) {
     const offset = parseInt(req.query.offset) || 0;
     await addRenderVarsForDescendants(renderVars, path, req.user, offset, limit, true);
     await addRenderVarsForPageTree(renderVars, pathOrId, req.user);
-
     await addRenderVarsWhenNotFound(renderVars, pathOrId);
-
+    if (req.emptyPagePermalink != null) {
+    // isEmpty かどうかをフロントに伝える
+      await addRenderVarsWhenEmptyPage(renderVars, req.emptyPagePermalink);
+    }
     return res.render(view, renderVars);
   }
 
@@ -598,17 +607,26 @@ module.exports = function(crowi, app) {
   async function redirector(req, res, next, path) {
     const { redirectFrom } = req.query;
 
-    const builder = new PageQueryBuilder(Page.find({ path }));
+    const includeEmpty = true;
+    const builder = new PageQueryBuilder(Page.find({ path }), includeEmpty);
     await Page.addConditionToFilteringByViewerForList(builder, req.user, true);
 
     const pages = await builder.query.lean().clone().exec('find');
 
     if (pages.length >= 2) {
 
+
+      // 空ページと まだマイグレートされていない同名パスが存在する場合、重複ページに飛ぶけどemptyは表示する必要ない
+
+      // identicalPathPages から empty page を 取り除く
+
+      // 取り除いた後でも空でないページの数が2以上の場合は処理を続行してよい
+      // そうでない場合は、通常通りの挙動(通常?)
+
       // populate to list
       builder.populateDataToList(User.USER_FIELDS_EXCEPT_CONFIDENTIAL);
       const identicalPathPages = await builder.query.lean().exec('find');
-
+      console.log(identicalPathPages);
       return res.render('layout-growi/identical-path-page', {
         identicalPathPages,
         redirectFrom,
@@ -616,21 +634,35 @@ module.exports = function(crowi, app) {
       });
     }
 
+
+    let isEmptyPage = false;
     if (pages.length === 1) {
-      const url = new URL('https://dummy.origin');
-      url.pathname = `/${pages[0]._id}`;
-      Object.entries(req.query).forEach(([key, value], i) => {
-        url.searchParams.append(key, value);
-      });
-      return res.safeRedirect(urljoin(url.pathname, url.search));
+      isEmptyPage = pages[0].isEmpty;
+      // ここで空ページかどうかをチェック
+      // 空の場合 notfoundに飛ばす(ここはスルー)
+      // そうでない場合、続行return res.safeRedirect(urljoin(url.pathname, url.search));
+      if (!isEmptyPage) {
+        const url = new URL('https://dummy.origin');
+        url.pathname = `/${pages[0]._id}`;
+        Object.entries(req.query).forEach(([key, value], i) => {
+          url.searchParams.append(key, value);
+        });
+        return res.safeRedirect(urljoin(url.pathname, url.search));
+      }
     }
 
+    // ここは権限が無い場合 or 空ページのどちらか
     // Exclude isEmpty page to handle _notFound or forbidden
+    // !req.isForbidden;
     const isForbidden = await Page.exists({ path, isEmpty: false });
     if (isForbidden) {
       req.isForbidden = true;
       return _notFound(req, res);
     }
+    if (isEmptyPage) {
+      req.emptyPagePermalink = pages[0]._id;
+      return _notFound(req, res);
+    }
 
     // redirect by PageRedirect
     const pageRedirect = await PageRedirect.findOne({ fromPath: path });

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

@@ -6,6 +6,7 @@
   <div
     id="growi-pagetree-not-found-context"
     data-not-found-target-path-or-id="{% if notFoundTargetPathOrId %}{{notFoundTargetPathOrId|json}}{% endif %}"
+    data-empty-page-permalink="{%if emptyPagePermalink %}{{emptyPagePermalink|json}}{% endif %}"
   >
   </div>
   <div

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

@@ -136,6 +136,10 @@ export const useNotFoundTargetPathOrId = (initialData?: string): SWRResponse<str
   return useStaticSWR<string, Error>('notFoundTargetPathOrId', initialData);
 };
 
+export const useEmptyPagePermalink = (initialData?: string): SWRResponse<string, Error> => {
+  return useStaticSWR<string, Error>('emptyPagePermalink', initialData);
+};
+
 export const useIsNotFoundPermalink = (initialData?: Nullable<IsNotFoundPermalink>): SWRResponse<Nullable<IsNotFoundPermalink>, Error> => {
   return useStaticSWR<Nullable<IsNotFoundPermalink>, Error>('isNotFoundPermalink', initialData);
 };