Explorar el Código

Merge pull request #5718 from weseek/fix/share-link-list-occures-error-when-related-page-is-not-found

fix: Share link list occures error when related page is not found
Yuki Takei hace 4 años
padre
commit
dec68f6c46

+ 0 - 89
packages/app/src/components/ShareLink/ShareLinkList.jsx

@@ -1,89 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-
-import { withTranslation } from 'react-i18next';
-import dateFnsFormat from 'date-fns/format';
-
-import { withUnstatedContainers } from '../UnstatedUtils';
-
-import AppContainer from '~/client/services/AppContainer';
-import CopyDropdown from '../Page/CopyDropdown';
-
-const ShareLinkList = (props) => {
-
-  const { t } = props;
-  function deleteLinkHandler(shareLinkId) {
-    if (props.onClickDeleteButton == null) {
-      return;
-    }
-    props.onClickDeleteButton(shareLinkId);
-  }
-
-  function renderShareLinks() {
-    return (
-      <>
-        {props.shareLinks.map(shareLink => (
-          <tr key={shareLink._id}>
-            <td>
-              <div className="d-flex">
-                <span className="mr-auto my-auto">{shareLink._id}</span>
-                <CopyDropdown
-                  pagePath={shareLink.relatedPage.path}
-                  dropdownToggleId={`copydropdown-${shareLink._id}`}
-                  pageId={shareLink._id}
-                  isShareLinkMode
-                >
-                  Copy Link
-                </CopyDropdown>
-              </div>
-            </td>
-            {props.isAdmin && <td><a href={shareLink.relatedPage.path}>{shareLink.relatedPage.path}</a></td>}
-            <td>{shareLink.expiredAt && <span>{dateFnsFormat(new Date(shareLink.expiredAt), 'yyyy-MM-dd HH:mm')}</span>}</td>
-            <td>{shareLink.description}</td>
-            <td>
-              <button className="btn btn-outline-warning" type="button" onClick={() => deleteLinkHandler(shareLink._id)}>
-                <i className="icon-trash"></i>{t('Delete')}
-              </button>
-            </td>
-          </tr>
-        ))}
-      </>
-    );
-  }
-
-  return (
-    <div className="table-responsive">
-      <table className="table table-bordered">
-        <thead>
-          <tr>
-            <th>{t('share_links.Share Link')}</th>
-            {props.isAdmin && <th>{t('share_links.Page Path')}</th>}
-            <th>{t('share_links.expire')}</th>
-            <th>{t('share_links.description')}</th>
-            <th></th>
-          </tr>
-        </thead>
-        <tbody>
-          {renderShareLinks()}
-        </tbody>
-      </table>
-    </div>
-  );
-};
-
-/**
- * Wrapper component for using unstated
- */
-const ShareLinkListWrapper = withUnstatedContainers(ShareLinkList, [AppContainer]);
-
-ShareLinkList.propTypes = {
-  t: PropTypes.func.isRequired, //  i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-
-  shareLinks: PropTypes.array.isRequired,
-  onClickDeleteButton: PropTypes.func,
-  isAdmin: PropTypes.bool,
-};
-
-export default withTranslation()(ShareLinkListWrapper);

+ 113 - 0
packages/app/src/components/ShareLink/ShareLinkList.tsx

@@ -0,0 +1,113 @@
+import React from 'react';
+
+import dateFnsFormat from 'date-fns/format';
+import { useTranslation } from 'react-i18next';
+
+import CopyDropdown from '../Page/CopyDropdown';
+
+
+type ShareLinkTrProps = {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  shareLink: any,
+  isAdmin?: boolean,
+  onDelete?: () => void,
+}
+
+const ShareLinkTr = (props: ShareLinkTrProps): JSX.Element => {
+  const { t } = useTranslation();
+
+  const { isAdmin, shareLink, onDelete } = props;
+
+  const { _id: shareLinkId, relatedPage } = shareLink;
+
+  const isRelatedPageExists = relatedPage != null;
+
+  return (
+    <tr key={shareLinkId}>
+      <td>
+        <div className="d-flex">
+          <span className="mr-auto my-auto">{shareLinkId}</span>
+
+          { isRelatedPageExists && (
+            <CopyDropdown
+              pagePath={relatedPage.path}
+              dropdownToggleId={`copydropdown-${shareLinkId}`}
+              pageId={shareLinkId}
+              isShareLinkMode
+            >
+              Copy Link
+            </CopyDropdown>
+          ) }
+        </div>
+      </td>
+      { isAdmin && (
+        <td>
+          { isRelatedPageExists
+            ? <a href={relatedPage.path}>{relatedPage.path}</a>
+            : '(Page is not found)'
+          }
+        </td>
+      ) }
+      <td>{shareLink.expiredAt && <span>{dateFnsFormat(new Date(shareLink.expiredAt), 'yyyy-MM-dd HH:mm')}</span>}</td>
+      <td>{shareLink.description}</td>
+      <td>
+        <button className="btn btn-outline-warning" type="button" onClick={onDelete}>
+          <i className="icon-trash"></i>{t('Delete')}
+        </button>
+      </td>
+    </tr>
+  );
+};
+
+
+type Props = {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  shareLinks: any[],
+  onClickDeleteButton?: (shareLinkId: string) => void,
+  isAdmin?: boolean,
+}
+
+const ShareLinkList = (props: Props): JSX.Element => {
+
+  const { t } = useTranslation();
+
+  function renderShareLinks() {
+    return (
+      <>
+        {props.shareLinks.map(shareLink => (
+          <ShareLinkTr
+            isAdmin={props.isAdmin}
+            shareLink={shareLink}
+            onDelete={() => {
+              if (props.onClickDeleteButton == null) {
+                return;
+              }
+              props.onClickDeleteButton(shareLink._id);
+            }}
+          />
+        ))}
+      </>
+    );
+  }
+
+  return (
+    <div className="table-responsive">
+      <table className="table table-bordered">
+        <thead>
+          <tr>
+            <th>{t('share_links.Share Link')}</th>
+            {props.isAdmin && <th>{t('share_links.Page Path')}</th>}
+            <th>{t('share_links.expire')}</th>
+            <th>{t('share_links.description')}</th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          {renderShareLinks()}
+        </tbody>
+      </table>
+    </div>
+  );
+};
+
+export default ShareLinkList;

+ 12 - 8
packages/app/src/server/routes/apiv3/share-links.js

@@ -249,21 +249,25 @@ module.exports = (crowi) => {
   */
   router.delete('/:id', loginRequired, csrf, validator.deleteShareLink, apiV3FormValidator, async(req, res) => {
     const { id } = req.params;
+    const { user } = req;
 
     try {
-      const deletedShareLink = await ShareLink.findOne({ _id: id });
+      const shareLinkToDelete = await ShareLink.findOne({ _id: id });
 
       // check permission
-      const page = await Page.findByIdAndViewer(deletedShareLink.relatedPage, req.user);
-      if (page == null) {
-        const msg = 'Page is not found or forbidden';
-        logger.error('Error', msg);
-        return res.apiv3Err(new ErrorV3(msg, 'delete-shareLink-failed'));
+      if (!user.isAdmin) {
+        const page = await Page.findByIdAndViewer(shareLinkToDelete.relatedPage, user);
+        const isPageExists = (await Page.count({ _id: shareLinkToDelete.relatedPage }) > 0);
+        if (page == null && isPageExists) {
+          const msg = 'Page is not found or forbidden';
+          logger.error('Error', msg);
+          return res.apiv3Err(new ErrorV3(msg, 'delete-shareLink-failed'));
+        }
       }
 
       // remove
-      await deletedShareLink.remove();
-      return res.apiv3({ deletedShareLink });
+      await shareLinkToDelete.remove();
+      return res.apiv3({ deletedShareLink: shareLinkToDelete });
     }
     catch (err) {
       const msg = 'Error occurred in delete share link';