Przeglądaj źródła

Implemented updateGrant api

Taichi Masuyama 3 lat temu
rodzic
commit
676ee019ff

+ 3 - 3
packages/app/src/components/Page/FixPageGrantAlert.tsx

@@ -86,13 +86,13 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
               <div className="custom-control custom-radio mb-3">
               <div className="custom-control custom-radio mb-3">
                 <input
                 <input
                   className="custom-control-input"
                   className="custom-control-input"
-                  name="grantUser"
-                  id="grantUser"
+                  name="grantRestricted"
+                  id="grantRestricted"
                   type="radio"
                   type="radio"
                   checked={selectedGrant === PageGrant.GRANT_RESTRICTED}
                   checked={selectedGrant === PageGrant.GRANT_RESTRICTED}
                   onChange={() => setSelectedGrant(PageGrant.GRANT_RESTRICTED)}
                   onChange={() => setSelectedGrant(PageGrant.GRANT_RESTRICTED)}
                 />
                 />
-                <label className="custom-control-label" htmlFor="grantUser">
+                <label className="custom-control-label" htmlFor="grantRestricted">
                   { t('fix_page_grant.modal.radio_btn.restrected') }
                   { t('fix_page_grant.modal.radio_btn.restrected') }
                 </label>
                 </label>
               </div>
               </div>

+ 44 - 12
packages/app/src/server/models/page.ts

@@ -13,7 +13,7 @@ import uniqueValidator from 'mongoose-unique-validator';
 import { IUserHasId } from '~/interfaces/user';
 import { IUserHasId } from '~/interfaces/user';
 import { ObjectIdLike } from '~/server/interfaces/mongoose-utils';
 import { ObjectIdLike } from '~/server/interfaces/mongoose-utils';
 
 
-import { IPage, IPageHasId } from '../../interfaces/page';
+import { IPage, IPageHasId, PageGrant } from '../../interfaces/page';
 import loggerFactory from '../../utils/logger';
 import loggerFactory from '../../utils/logger';
 import Crowi from '../crowi';
 import Crowi from '../crowi';
 
 
@@ -1191,7 +1191,32 @@ export default (crowi: Crowi): any => {
     pageEvent.emit('update', page, user);
     pageEvent.emit('update', page, user);
   };
   };
 
 
-  schema.statics.updatePage = async function(pageData, body, previousBody, user, options = {}) {
+  /**
+   * A wrapper method of schema.statics.updatePage for updating grant only.
+   * @param {PageDocument} page
+   * @param {UserDocument} user
+   * @param options
+   */
+  schema.statics.updateGrant = async function(page, user, grantData: {grant: PageGrant, grantedGroup: ObjectIdLike}, shouldUseV4Process: boolean) {
+    const { grant, grantedGroup } = grantData;
+
+    const options = {
+      grant,
+      grantUserGroupId: grantedGroup,
+      isSyncRevisionToHackmd: false,
+      shouldUseV4Process,
+    };
+
+    return this.updatePage(page, null, null, user, options);
+  };
+
+  schema.statics.updatePage = async function(
+      pageData,
+      body: string | null,
+      previousBody: string | null,
+      user,
+      options: {grant?: PageGrant, grantUserGroupId?: ObjectIdLike, isSyncRevisionToHackmd?: boolean, shouldUseV4Process?: boolean} = {},
+  ) {
     if (crowi.configManager == null || crowi.pageGrantService == null || crowi.pageService == null) {
     if (crowi.configManager == null || crowi.pageGrantService == null || crowi.pageService == null) {
       throw Error('Crowi is not set up');
       throw Error('Crowi is not set up');
     }
     }
@@ -1200,16 +1225,15 @@ export default (crowi: Crowi): any => {
     const exParent = pageData.parent;
     const exParent = pageData.parent;
     const isV5Compatible = crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
     const isV5Compatible = crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
 
 
-    const shouldUseV4Process = shouldUseUpdatePageV4(pageData.grant, isV5Compatible, wasOnTree);
+    const shouldUseV4Process = options.shouldUseV4Process ?? shouldUseUpdatePageV4(pageData.grant, isV5Compatible, wasOnTree);
     if (shouldUseV4Process) {
     if (shouldUseV4Process) {
       // v4 compatible process
       // v4 compatible process
       return this.updatePageV4(pageData, body, previousBody, user, options);
       return this.updatePageV4(pageData, body, previousBody, user, options);
     }
     }
 
 
-    const Revision = mongoose.model('Revision') as any; // TODO: Typescriptize model
-    const grant = options.grant || pageData.grant; // use the previous data if absence
+    const grant = options.grant ?? pageData.grant; // use the previous data if absence
     const grantUserGroupId: undefined | ObjectIdLike = options.grantUserGroupId ?? pageData.grantedGroup?._id.toString();
     const grantUserGroupId: undefined | ObjectIdLike = options.grantUserGroupId ?? pageData.grantedGroup?._id.toString();
-    const isSyncRevisionToHackmd = options.isSyncRevisionToHackmd;
+
     const grantedUserIds = pageData.grantedUserIds || [user._id];
     const grantedUserIds = pageData.grantedUserIds || [user._id];
     const shouldBeOnTree = grant !== GRANT_RESTRICTED;
     const shouldBeOnTree = grant !== GRANT_RESTRICTED;
     const isChildrenExist = await this.count({ path: new RegExp(`^${escapeStringRegexp(addTrailingSlash(pageData.path))}`), parent: { $ne: null } });
     const isChildrenExist = await this.count({ path: new RegExp(`^${escapeStringRegexp(addTrailingSlash(pageData.path))}`), parent: { $ne: null } });
@@ -1252,14 +1276,23 @@ export default (crowi: Crowi): any => {
 
 
     // update existing page
     // update existing page
     let savedPage = await newPageData.save();
     let savedPage = await newPageData.save();
-    const newRevision = await Revision.prepareRevision(newPageData, body, previousBody, user);
-    savedPage = await pushRevision(savedPage, newRevision, user);
-    await savedPage.populateDataToShowRevision();
 
 
-    if (isSyncRevisionToHackmd) {
-      savedPage = await this.syncRevisionToHackmd(savedPage);
+    // Update body
+    const Revision = mongoose.model('Revision') as any; // TODO: Typescriptize model
+    const isSyncRevisionToHackmd = options.isSyncRevisionToHackmd;
+    const isBodyPresent = body != null && previousBody != null;
+    const shouldUpdateBody = isBodyPresent;
+    if (shouldUpdateBody) {
+      const newRevision = await Revision.prepareRevision(newPageData, body, previousBody, user);
+      savedPage = await pushRevision(savedPage, newRevision, user);
+      await savedPage.populateDataToShowRevision();
+
+      if (isSyncRevisionToHackmd) {
+        savedPage = await this.syncRevisionToHackmd(savedPage);
+      }
     }
     }
 
 
+
     this.emitPageEventUpdate(savedPage, user);
     this.emitPageEventUpdate(savedPage, user);
 
 
     // Update ex children's parent
     // Update ex children's parent
@@ -1296,7 +1329,6 @@ export default (crowi: Crowi): any => {
     }
     }
 
 
     // 2. Delete unnecessary empty pages
     // 2. Delete unnecessary empty pages
-
     const shouldRemoveLeafEmpPages = wasOnTree && !isChildrenExist;
     const shouldRemoveLeafEmpPages = wasOnTree && !isChildrenExist;
     if (shouldRemoveLeafEmpPages) {
     if (shouldRemoveLeafEmpPages) {
       await this.removeLeafEmptyPagesRecursively(exParent);
       await this.removeLeafEmptyPagesRecursively(exParent);

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

@@ -10,7 +10,7 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 const logger = loggerFactory('growi:routes:apiv3:page'); // eslint-disable-line no-unused-vars
 const logger = loggerFactory('growi:routes:apiv3:page'); // eslint-disable-line no-unused-vars
 
 
 const express = require('express');
 const express = require('express');
-const { body, query } = require('express-validator');
+const { body, query, param } = require('express-validator');
 
 
 const router = express.Router();
 const router = express.Router();
 const { convertToNewAffiliationPath, isTopPage } = pagePathUtils;
 const { convertToNewAffiliationPath, isTopPage } = pagePathUtils;
@@ -186,6 +186,11 @@ module.exports = (crowi) => {
     applicableGrant: [
     applicableGrant: [
       query('pageId').isMongoId().withMessage('pageId is required'),
       query('pageId').isMongoId().withMessage('pageId is required'),
     ],
     ],
+    updateGrant: [
+      param('pageId').isMongoId().withMessage('pageId is required'),
+      body('grant').isInt().withMessage('grant is required'),
+      body('grantedGroup').optional().isMongoId().withMessage('grantedGroup must be a mongo id'),
+    ],
     export: [
     export: [
       query('format').isString().isIn(['md', 'pdf']),
       query('format').isString().isIn(['md', 'pdf']),
       query('revisionId').isString(),
       query('revisionId').isString(),
@@ -464,10 +469,31 @@ module.exports = (crowi) => {
     return res.apiv3(data);
     return res.apiv3(data);
   });
   });
 
 
-  // TODO: implement
-  // router.put('/:pageId/grant', loginRequiredStrictly, validator.isGrantNormalized, apiV3FormValidator, async(req, res) => {
-  //   return;
-  // });
+  router.put('/:pageId/grant', loginRequiredStrictly, validator.updateGrant, apiV3FormValidator, async(req, res) => {
+    const { pageId } = req.params;
+    const { grant, grantedGroup } = req.body;
+
+    const Page = crowi.model('Page');
+
+    const page = await Page.findByIdAndViewer(pageId, req.user, false);
+
+    if (page == null) {
+      return res.apiv3Err(new ErrorV3('Page is unreachable or empty.', 'page_unreachable_or_empty'), 400);
+    }
+
+    let data;
+    try {
+      const shouldUseV4Process = false;
+      const grantData = { grant, grantedGroup };
+      data = await Page.updateGrant(page, req.user, grantData, shouldUseV4Process);
+    }
+    catch (err) {
+      logger.error('Error occurred while processing calcApplicableGrantData.', err);
+      return res.apiv3Err(err, 500);
+    }
+
+    return res.apiv3(data);
+  });
 
 
   /**
   /**
   * @swagger
   * @swagger