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

Merge branch 'support/share-link-for-outside-for-merge' into feat/security-settings-share-link-for-sub-merge

# Conflicts:
#	src/client/js/components/Admin/Security/ShareLinkSetting.jsx
ryuichi-e 5 лет назад
Родитель
Сommit
4511623d65

+ 2 - 0
resource/locales/en_US/translation.json

@@ -59,6 +59,8 @@
   "Last_Login": "Last login",
   "Share": "Share",
   "Share Link": "Share Link",
+  "share_link_notice":"remove {{count}} share links",
+  "delete_all_share_links":"Delete all share links",
   "Markdown Link": "Markdown Link",
   "Create/Edit Template": "Create/Edit template page",
   "Go to this version": "View this version",

+ 2 - 0
resource/locales/ja_JP/translation.json

@@ -59,6 +59,8 @@
   "Last_Login": "最終ログイン",
   "Share": "共有",
   "Share Link": "共有用リンク",
+  "share_link_notice":"{{count}} 件の共有リンクを削除します",
+  "delete_all_share_links":"全ての共有リンクを削除します",
   "Markdown Link": "Markdown形式のリンク",
   "Create/Edit Template": "テンプレートページの作成/編集",
   "Go to this version": "このバージョンを見る",

+ 4 - 2
resource/locales/zh_CN/translation.json

@@ -53,7 +53,9 @@
 	"Last updated": "上次更新",
 	"Last_Login": "上次登录",
 	"Share": "分享",
-	"Share Link": "分享链接",
+  "Share Link": "分享链接",
+  "share_link_notice":"remove {{count}} share links",
+  "delete_all_share_links":"Delete all share links",
 	"Markdown Link": "Markdown链接",
 	"Create/Edit Template": "创建/编辑 模板页面",
 	"Unportalize": "未启动",
@@ -697,4 +699,4 @@
 		"Registration successful": "注册成功",
 		"Setup": "安装程序"
 	}
-}
+}

+ 67 - 0
src/client/js/components/Admin/Security/DeleteAllShareLinksModal.jsx

@@ -0,0 +1,67 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { withTranslation } from 'react-i18next';
+
+import {
+  Button, Modal, ModalHeader, ModalBody, ModalFooter,
+} from 'reactstrap';
+
+const DeleteAllShareLinksModal = React.memo((props) => {
+  const { t } = props;
+
+  function closeModal() {
+    if (props.onClose == null) {
+      return;
+    }
+
+    props.onClose();
+  }
+
+  function deleteAllLinkHandler() {
+    if (props.onClickDeleteButton == null) {
+      return;
+    }
+
+    props.onClickDeleteButton();
+
+    closeModal();
+  }
+
+  function closeButtonHandler() {
+    closeModal();
+  }
+
+  return (
+    <Modal isOpen={props.isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">
+      <ModalHeader tag="h4" toggle={closeButtonHandler} className="bg-danger text-light">
+        <span>
+          <i className="icon-fw icon-fire"></i>
+          {t('delete_all_share_links')}
+        </span>
+      </ModalHeader>
+      <ModalBody>
+        { t('share_link_notice', { count: props.count })}
+      </ModalBody>
+      <ModalFooter>
+        <Button onClick={closeButtonHandler}>Cancel</Button>
+        <Button color="danger" onClick={deleteAllLinkHandler}>
+          <i className="icon icon-fire"></i>
+            Delete
+        </Button>
+      </ModalFooter>
+    </Modal>
+  );
+
+});
+
+DeleteAllShareLinksModal.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+
+  isOpen: PropTypes.bool.isRequired,
+  onClose: PropTypes.func,
+  count: PropTypes.number.isRequired,
+  onClickDeleteButton: PropTypes.func,
+};
+
+export default withTranslation()(DeleteAllShareLinksModal);

+ 47 - 4
src/client/js/components/Admin/Security/ShareLinkSetting.jsx

@@ -3,21 +3,28 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
+import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import PaginationWrapper from '../../PaginationWrapper';
 
 import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 
-import { toastError } from '../../../util/apiNotification';
+import DeleteAllShareLinksModal from './DeleteAllShareLinksModal';
 
 class ShareLinkSetting extends React.Component {
 
-  constructor(props) {
+  constructor() {
     super();
+
     this.state = {
+      shareLinks: [],
+      isDeleteConfirmModalShown: false,
     };
     this.getShareLinkList = this.getShareLinkList.bind(this);
+    this.showDeleteConfirmModal = this.showDeleteConfirmModal.bind(this);
+    this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
+    this.deleteAllLinksButtonHandler = this.deleteAllLinksButtonHandler.bind(this);
   }
 
   componentWillMount() {
@@ -31,8 +38,32 @@ class ShareLinkSetting extends React.Component {
     catch (err) {
       toastError(err);
     }
+
+  }
+
+  showDeleteConfirmModal() {
+    this.setState({ isDeleteConfirmModalShown: true });
+  }
+
+  closeDeleteConfirmModal() {
+    this.setState({ isDeleteConfirmModalShown: false });
+  }
+
+  async deleteAllLinksButtonHandler() {
+    const { t, appContainer } = this.props;
+
+    try {
+      const res = await appContainer.apiv3Delete('/share-links/all');
+      const { deletedCount } = res.data;
+      toastSuccess(t('toaster.remove_share_link', { count: deletedCount }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+
   }
 
+
   render() {
     const { adminGeneralSecurityContainer } = this.props;
 
@@ -48,11 +79,12 @@ class ShareLinkSetting extends React.Component {
     );
 
     const deleteAllButton = (
-      adminGeneralSecurityContainer.state.shareLinks.length > 0
+      adminGeneralSecurityContainer.state.shareLinks.length === 0
         ? (
           <button
             className="pull-right btn btn-danger"
             type="button"
+            onClick={this.showDeleteConfirmModal}
           >
             Delete all links
           </button>
@@ -90,7 +122,11 @@ class ShareLinkSetting extends React.Component {
                     <td>{sharelink.expiredAt}</td>
                     <td>{sharelink.description}</td>
                     <td>
-                      <button className="btn btn-outline-warning" type="button">
+                      <button
+                        className="btn btn-outline-warning"
+                        type="button"
+                        onClickDeleteButton={this.deleteLinkById}
+                      >
                         <i className="icon-trash mr-2"></i>Delete
                       </button>
                     </td>
@@ -101,6 +137,13 @@ class ShareLinkSetting extends React.Component {
           </table>
         </div>
 
+        <DeleteAllShareLinksModal
+          isOpen={this.state.isDeleteConfirmModalShown}
+          onClose={this.closeDeleteConfirmModal}
+          count={this.state.shareLinks.length}
+          onClickDeleteButton={this.deleteAllLinksButtonHandler}
+        />
+
       </Fragment>
     );
   }

+ 26 - 1
src/server/routes/apiv3/share-links.js

@@ -24,6 +24,7 @@ const today = new Date();
 
 module.exports = (crowi) => {
   const loginRequired = require('../../middlewares/login-required')(crowi);
+  const adminRequired = require('../../middlewares/admin-required')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
   const ShareLink = crowi.model('ShareLink');
@@ -136,7 +137,6 @@ module.exports = (crowi) => {
   */
   router.delete('/', loginRequired, csrf, async(req, res) => {
     const { relatedPage } = req.query;
-    const ShareLink = crowi.model('ShareLink');
 
     try {
       const deletedShareLink = await ShareLink.remove({ relatedPage });
@@ -149,6 +149,31 @@ module.exports = (crowi) => {
     }
   });
 
+  /**
+  * @swagger
+  *
+  *    /share-links/all:
+  *      delete:
+  *        tags: [ShareLinks]
+  *        description: delete all share links
+  *        responses:
+  *          200:
+  *            description: Succeeded to remove all share links
+  */
+  router.delete('/all', loginRequired, adminRequired, csrf, async(req, res) => {
+
+    try {
+      const deletedShareLink = await ShareLink.deleteMany({});
+      const { deletedCount } = deletedShareLink;
+      return res.apiv3({ deletedCount });
+    }
+    catch (err) {
+      const msg = 'Error occurred in delete all share link';
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg, 'delete-all-shareLink-failed'));
+    }
+  });
+
   /**
   * @swagger
   *