فهرست منبع

get nonEmptyClosestAncestor in non-user-related-groups-granted

Futa Arai 1 سال پیش
والد
کامیت
4cacb600e5

+ 2 - 2
apps/app/src/components/Navbar/PageEditorModeManager.tsx

@@ -76,10 +76,10 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
     }
     }
 
 
     try {
     try {
-      const res = await apiv3Get('/page/non-empty-closest-ancestor', { path });
+      const parentPath = path?.split('/').slice(0, -1).join('/'); // does not have to exist
       await createAndTransit(
       await createAndTransit(
         {
         {
-          path, parentPath: res.data.nonEmptyClosestAncestor?.path, wip: shouldCreateWipPage(path), origin: Origin.View,
+          path, parentPath, wip: shouldCreateWipPage(path), origin: Origin.View,
         },
         },
         { shouldCheckPageExists: true },
         { shouldCheckPageExists: true },
       );
       );

+ 1 - 3
apps/app/src/components/PageCreateModal.tsx

@@ -95,11 +95,9 @@ const PageCreateModal: React.FC = () => {
    */
    */
   const createTodayPage = useCallback(async() => {
   const createTodayPage = useCallback(async() => {
     const joinedPath = [todaysParentPath, todayInput].join('/');
     const joinedPath = [todaysParentPath, todayInput].join('/');
-    const res = await apiv3Get('/page/non-empty-closest-ancestor', { path: joinedPath });
-    const parentPath = res.data.nonEmptyClosestAncestor?.path;
     return createAndTransit(
     return createAndTransit(
       {
       {
-        path: joinedPath, parentPath, wip: true, origin: Origin.View,
+        path: joinedPath, parentPath: todaysParentPath, wip: true, origin: Origin.View,
       },
       },
       { shouldCheckPageExists: true, onTerminated: closeCreateModal },
       { shouldCheckPageExists: true, onTerminated: closeCreateModal },
     );
     );

+ 3 - 5
apps/app/src/components/Sidebar/PageCreateButton/hooks/use-create-todays-memo.tsx

@@ -26,23 +26,21 @@ export const useCreateTodaysMemo: UseCreateTodaysMemo = () => {
 
 
   const parentDirName = t('create_page_dropdown.todays.memo');
   const parentDirName = t('create_page_dropdown.todays.memo');
   const now = format(new Date(), 'yyyy/MM/dd');
   const now = format(new Date(), 'yyyy/MM/dd');
+  const parentPath = `${userHomepagePath(currentUser)}/${parentDirName}`;
   const todaysPath = isCreatable
   const todaysPath = isCreatable
-    ? `${userHomepagePath(currentUser)}/${parentDirName}/${now}`
+    ? `${parentPath}/${now}`
     : null;
     : null;
 
 
   const createTodaysMemo = useCallback(async() => {
   const createTodaysMemo = useCallback(async() => {
     if (!isCreatable || todaysPath == null) return;
     if (!isCreatable || todaysPath == null) return;
 
 
-    const res = await apiv3Get('/page/non-empty-closest-ancestor', { path: todaysPath });
-    const parentPath = res.data.nonEmptyClosestAncestor?.path;
-
     return createAndTransit(
     return createAndTransit(
       {
       {
         path: todaysPath, parentPath, wip: true, origin: Origin.View,
         path: todaysPath, parentPath, wip: true, origin: Origin.View,
       },
       },
       { shouldCheckPageExists: true },
       { shouldCheckPageExists: true },
     );
     );
-  }, [createAndTransit, isCreatable, todaysPath]);
+  }, [createAndTransit, isCreatable, todaysPath, parentPath]);
 
 
   return {
   return {
     isCreating,
     isCreating,

+ 5 - 5
apps/app/src/server/models/page.ts

@@ -77,7 +77,7 @@ export interface PageModel extends Model<PageDocument> {
   generateGrantCondition(
   generateGrantCondition(
     user, userGroups: string[] | null, includeAnyoneWithTheLink?: boolean, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
     user, userGroups: string[] | null, includeAnyoneWithTheLink?: boolean, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
   ): { $or: any[] }
   ): { $or: any[] }
-  findNonEmptyClosestAncestor(path: string): Promise<PageDocument | undefined>
+  findNonEmptyClosestAncestor(path: string): Promise<PageDocument | null>
   findNotEmptyParentByPathRecursively(path: string): Promise<PageDocument | undefined>
   findNotEmptyParentByPathRecursively(path: string): Promise<PageDocument | undefined>
   removeLeafEmptyPagesRecursively(pageId: ObjectIdLike): Promise<void>
   removeLeafEmptyPagesRecursively(pageId: ObjectIdLike): Promise<void>
   findTemplate(path: string): Promise<{
   findTemplate(path: string): Promise<{
@@ -1024,10 +1024,10 @@ function generateGrantConditionForSystemDeletion(): { $or: any[] } {
 
 
 schema.statics.generateGrantConditionForSystemDeletion = generateGrantConditionForSystemDeletion;
 schema.statics.generateGrantConditionForSystemDeletion = generateGrantConditionForSystemDeletion;
 
 
-// find ancestor page with isEmpty: false. If parameter path is '/', return undefined
-schema.statics.findNonEmptyClosestAncestor = async function(path: string): Promise<PageDocument | undefined> {
+// find ancestor page with isEmpty: false. If parameter path is '/', return null
+schema.statics.findNonEmptyClosestAncestor = async function(path: string): Promise<PageDocument | null> {
   if (path === '/') {
   if (path === '/') {
-    return;
+    return null;
   }
   }
 
 
   const builderForAncestors = new PageQueryBuilder(this.find(), false); // empty page not included
   const builderForAncestors = new PageQueryBuilder(this.find(), false); // empty page not included
@@ -1038,7 +1038,7 @@ schema.statics.findNonEmptyClosestAncestor = async function(path: string): Promi
     .query
     .query
     .exec();
     .exec();
 
 
-  return ancestors[0];
+  return ancestors[0] ?? null;
 };
 };
 
 
 schema.statics.removeGroupsToDeleteFromPages = async function(pages: PageDocument[], groupsToDelete: UserGroupDocument[] | ExternalUserGroupDocument[]) {
 schema.statics.removeGroupsToDeleteFromPages = async function(pages: PageDocument[], groupsToDelete: UserGroupDocument[] | ExternalUserGroupDocument[]) {

+ 10 - 23
apps/app/src/server/routes/apiv3/page/index.ts

@@ -246,9 +246,6 @@ module.exports = (crowi) => {
     contentWidth: [
     contentWidth: [
       body('expandContentWidth').isBoolean(),
       body('expandContentWidth').isBoolean(),
     ],
     ],
-    nonEmptyClosestAncestor: [
-      query('path').isString(),
-    ],
   };
   };
 
 
   /**
   /**
@@ -642,17 +639,23 @@ module.exports = (crowi) => {
     return res.apiv3({ isGrantNormalized, grantData });
     return res.apiv3({ isGrantNormalized, grantData });
   });
   });
 
 
-  // Check if non user related groups are granted page access
+  // Check if non user related groups are granted page access.
+  // If specified page does not exist, check the closest ancestor.
   router.get('/non-user-related-groups-granted', loginRequiredStrictly, validator.nonUserRelatedGroupsGranted, apiV3FormValidator,
   router.get('/non-user-related-groups-granted', loginRequiredStrictly, validator.nonUserRelatedGroupsGranted, apiV3FormValidator,
     async(req, res: ApiV3Response) => {
     async(req, res: ApiV3Response) => {
       const { user } = req;
       const { user } = req;
       const { path } = req.query;
       const { path } = req.query;
       const pageGrantService = crowi.pageGrantService as IPageGrantService;
       const pageGrantService = crowi.pageGrantService as IPageGrantService;
       try {
       try {
-        const page = await Page.findByPathAndViewer(path, user, null, true);
-
+        const page = await Page.findByPath(path, true) ?? await Page.findNonEmptyClosestAncestor(path);
         if (page == null) {
         if (page == null) {
-          return res.apiv3Err(new ErrorV3('Page is unreachable or empty.', 'page_unreachable_or_empty'), 400);
+          return res.apiv3Err(new ErrorV3('Page and ancestor does not exist.', 'page_does_not_exist'), 400);
+        }
+
+        const userRelatedGroups = await pageGrantService.getUserRelatedGroups(user);
+        const isUserGrantedPageAccess = await pageGrantService.isUserGrantedPageAccess(page, user, userRelatedGroups);
+        if (!isUserGrantedPageAccess) {
+          return res.apiv3Err(new ErrorV3('Cannot access page or ancestor.', 'cannot_access_page'), 403);
         }
         }
 
 
         if (page.grant !== PageGrant.GRANT_USER_GROUP) {
         if (page.grant !== PageGrant.GRANT_USER_GROUP) {
@@ -942,21 +945,5 @@ module.exports = (crowi) => {
 
 
   router.put('/:pageId/unpublish', unpublishPageHandlersFactory(crowi));
   router.put('/:pageId/unpublish', unpublishPageHandlersFactory(crowi));
 
 
-  router.get(
-    '/non-empty-closest-ancestor', accessTokenParser, loginRequiredStrictly, validator.nonEmptyClosestAncestor, apiV3FormValidator, async(req, res) => {
-      const { path } = req.query;
-      console.log('ここ', path);
-
-      try {
-        const nonEmptyClosestAncestor = await Page.findNonEmptyClosestAncestor(path);
-        return res.apiv3({ nonEmptyClosestAncestor });
-      }
-      catch (err) {
-        logger.error('Failed to get non-empty closest ancestor', err);
-        return res.apiv3Err(err, 500);
-      }
-    },
-  );
-
   return router;
   return router;
 };
 };