Просмотр исходного кода

Merge pull request #5833 from weseek/feat/fix-grant-alert-update-grant-api

feat: Fix grant alert update grant api
Yuki Takei 3 лет назад
Родитель
Сommit
cae677eef3

+ 13 - 12
packages/app/src/components/Page/FixPageGrantAlert.tsx

@@ -27,7 +27,7 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
   } = props;
 
   const [selectedGrant, setSelectedGrant] = useState<PageGrant>(PageGrant.GRANT_OWNER);
-  const [selectedGroupName, setSelectedGroupName] = useState<string | undefined>(undefined);
+  const [selectedGroup, setSelectedGroup] = useState<{_id: string, name: string} | undefined>(undefined); // TODO: Typescriptize model
 
   // Alert message state
   const [shouldShowModalAlert, setShowModalAlert] = useState<boolean>(false);
@@ -38,14 +38,14 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
   useEffect(() => {
     if (isOpen) {
       setSelectedGrant(PageGrant.GRANT_OWNER);
-      setSelectedGroupName(undefined);
+      setSelectedGroup(undefined);
       setShowModalAlert(false);
     }
   }, [isOpen]);
 
   const submit = async() => {
     // Validate input values
-    if (selectedGrant === PageGrant.GRANT_USER_GROUP && selectedGroupName == null) {
+    if (selectedGrant === PageGrant.GRANT_USER_GROUP && selectedGroup == null) {
       setShowModalAlert(true);
       return;
     }
@@ -55,13 +55,14 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
     try {
       await apiv3Put(`/page/${pageId}/grant`, {
         grant: selectedGrant,
-        grantedGroupName: selectedGroupName,
+        grantedGroup: selectedGroup?._id,
       });
+
+      toastSuccess(t('Successfully updated'));
     }
     catch (err) {
-      toastError();
+      toastError(t('Failed to update'));
     }
-    toastSuccess();
   };
 
   const renderModalBodyAndFooter = () => {
@@ -86,13 +87,13 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
               <div className="custom-control custom-radio mb-3">
                 <input
                   className="custom-control-input"
-                  name="grantUser"
-                  id="grantUser"
+                  name="grantRestricted"
+                  id="grantRestricted"
                   type="radio"
                   checked={selectedGrant === 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') }
                 </label>
               </div>
@@ -131,9 +132,9 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
                     >
                       <span className="float-left">
                         {
-                          selectedGroupName == null
+                          selectedGroup == null
                             ? t('fix_page_grant.modal.select_group_default_text')
-                            : selectedGroupName
+                            : selectedGroup.name
                         }
                       </span>
                     </button>
@@ -143,7 +144,7 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
                           <button
                             className="dropdown-item"
                             type="button"
-                            onClick={() => setSelectedGroupName(g.name)}
+                            onClick={() => setSelectedGroup(g)}
                           >
                             {g.name}
                           </button>

+ 12 - 5
packages/app/src/server/models/obsolete-page.js

@@ -742,14 +742,21 @@ export const getPageSchema = (crowi) => {
 
     // update existing page
     let savedPage = await pageData.save();
-    const newRevision = await Revision.prepareRevision(pageData, body, previousBody, user);
-    savedPage = await pushRevision(savedPage, newRevision, user);
-    await savedPage.populateDataToShowRevision();
 
-    if (isSyncRevisionToHackmd) {
-      savedPage = await this.syncRevisionToHackmd(savedPage);
+    // Update revision
+    const isBodyPresent = body != null && previousBody != null;
+    const shouldUpdateBody = isBodyPresent;
+    if (shouldUpdateBody) {
+      const newRevision = await Revision.prepareRevision(pageData, body, previousBody, user);
+      savedPage = await pushRevision(savedPage, newRevision, user);
+      await savedPage.populateDataToShowRevision();
+
+      if (isSyncRevisionToHackmd) {
+        savedPage = await this.syncRevisionToHackmd(savedPage);
+      }
     }
 
+
     pageEvent.emit('update', savedPage, user);
 
     return savedPage;

+ 42 - 11
packages/app/src/server/models/page.ts

@@ -13,7 +13,7 @@ import uniqueValidator from 'mongoose-unique-validator';
 import { IUserHasId } from '~/interfaces/user';
 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 Crowi from '../crowi';
 
@@ -1191,7 +1191,31 @@ export default (crowi: Crowi): any => {
     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}) {
+    const { grant, grantedGroup } = grantData;
+
+    const options = {
+      grant,
+      grantUserGroupId: grantedGroup,
+      isSyncRevisionToHackmd: false,
+    };
+
+    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} = {},
+  ) {
     if (crowi.configManager == null || crowi.pageGrantService == null || crowi.pageService == null) {
       throw Error('Crowi is not set up');
     }
@@ -1206,10 +1230,9 @@ export default (crowi: Crowi): any => {
       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 isSyncRevisionToHackmd = options.isSyncRevisionToHackmd;
+
     const grantedUserIds = pageData.grantedUserIds || [user._id];
     const shouldBeOnTree = grant !== GRANT_RESTRICTED;
     const isChildrenExist = await this.count({ path: new RegExp(`^${escapeStringRegexp(addTrailingSlash(pageData.path))}`), parent: { $ne: null } });
@@ -1252,14 +1275,23 @@ export default (crowi: Crowi): any => {
 
     // update existing page
     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);
 
     // Update ex children's parent
@@ -1296,7 +1328,6 @@ export default (crowi: Crowi): any => {
     }
 
     // 2. Delete unnecessary empty pages
-
     const shouldRemoveLeafEmpPages = wasOnTree && !isChildrenExist;
     if (shouldRemoveLeafEmpPages) {
       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 express = require('express');
-const { body, query } = require('express-validator');
+const { body, query, param } = require('express-validator');
 
 const router = express.Router();
 const { convertToNewAffiliationPath, isTopPage } = pagePathUtils;
@@ -186,6 +186,11 @@ module.exports = (crowi) => {
     applicableGrant: [
       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: [
       query('format').isString().isIn(['md', 'pdf']),
       query('revisionId').isString(),
@@ -464,10 +469,31 @@ module.exports = (crowi) => {
     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, null, 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