github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteObject.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 import React, { Fragment, useState } from "react"; 18 import { ErrorResponseHandler } from "../../../../../../common/types"; 19 import { decodeURLString } from "../../../../../../common/utils"; 20 import ConfirmDialog from "../../../../Common/ModalWrapper/ConfirmDialog"; 21 import useApi from "../../../../Common/Hooks/useApi"; 22 import { ConfirmDeleteIcon, Switch } from "mds"; 23 import { setErrorSnackMessage } from "../../../../../../systemSlice"; 24 import { AppState, useAppDispatch } from "../../../../../../store"; 25 import { hasPermission } from "../../../../../../common/SecureComponent"; 26 import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; 27 import { useSelector } from "react-redux"; 28 import { isVersionedMode } from "../../../../../../utils/validationFunctions"; 29 import { BucketVersioningResponse } from "api/consoleApi"; 30 31 interface IDeleteObjectProps { 32 closeDeleteModalAndRefresh: (refresh: boolean) => void; 33 deleteOpen: boolean; 34 selectedObject: string; 35 selectedBucket: string; 36 37 versioningInfo: BucketVersioningResponse | undefined; 38 selectedVersion?: string; 39 } 40 41 const DeleteObject = ({ 42 closeDeleteModalAndRefresh, 43 deleteOpen, 44 selectedBucket, 45 selectedObject, 46 versioningInfo, 47 selectedVersion = "", 48 }: IDeleteObjectProps) => { 49 const dispatch = useAppDispatch(); 50 const onDelSuccess = () => closeDeleteModalAndRefresh(true); 51 const onDelError = (err: ErrorResponseHandler) => { 52 dispatch(setErrorSnackMessage(err)); 53 54 // We close the modal box on access denied. 55 if (err.detailedError === "Access Denied.") { 56 closeDeleteModalAndRefresh(true); 57 } 58 }; 59 const onClose = () => closeDeleteModalAndRefresh(false); 60 61 const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError); 62 const [deleteVersions, setDeleteVersions] = useState<boolean>(false); 63 const [bypassGovernance, setBypassGovernance] = useState<boolean>(false); 64 65 const retentionConfig = useSelector( 66 (state: AppState) => state.objectBrowser.retentionConfig, 67 ); 68 69 const canBypass = 70 hasPermission( 71 [selectedBucket], 72 [IAM_SCOPES.S3_BYPASS_GOVERNANCE_RETENTION], 73 ) && retentionConfig?.mode === "governance"; 74 75 if (!selectedObject) { 76 return null; 77 } 78 const onConfirmDelete = () => { 79 const decodedSelectedObject = decodeURLString(selectedObject); 80 const recursive = decodedSelectedObject.endsWith("/"); 81 invokeDeleteApi( 82 "DELETE", 83 `/api/v1/buckets/${selectedBucket}/objects?prefix=${selectedObject}${ 84 selectedVersion !== "" 85 ? `&version_id=${selectedVersion}` 86 : `&recursive=${recursive}&all_versions=${deleteVersions}` 87 }${bypassGovernance ? "&bypass=true" : ""}`, 88 ); 89 }; 90 91 return ( 92 <ConfirmDialog 93 title={`Delete Object`} 94 confirmText={"Delete"} 95 isOpen={deleteOpen} 96 titleIcon={<ConfirmDeleteIcon />} 97 isLoading={deleteLoading} 98 onConfirm={onConfirmDelete} 99 onClose={onClose} 100 confirmationContent={ 101 <Fragment> 102 Are you sure you want to delete: <br /> 103 <b>{decodeURLString(selectedObject)}</b>{" "} 104 {selectedVersion !== "" ? ( 105 <Fragment> 106 <br /> 107 <br /> 108 Version ID: 109 <br /> 110 <strong>{selectedVersion}</strong> 111 </Fragment> 112 ) : ( 113 "" 114 )} 115 ? <br /> 116 <br /> 117 {isVersionedMode(versioningInfo?.status) && 118 selectedVersion === "" && ( 119 <Fragment> 120 <Switch 121 label={"Delete All Versions"} 122 indicatorLabels={["Yes", "No"]} 123 checked={deleteVersions} 124 value={"delete_versions"} 125 id="delete-versions" 126 name="delete-versions" 127 onChange={(e) => { 128 setDeleteVersions(!deleteVersions); 129 }} 130 description="" 131 /> 132 </Fragment> 133 )} 134 {canBypass && (deleteVersions || selectedVersion !== "") && ( 135 <Fragment> 136 <div 137 style={{ 138 marginTop: 10, 139 }} 140 > 141 <Switch 142 label={"Bypass Governance Mode"} 143 indicatorLabels={["Yes", "No"]} 144 checked={bypassGovernance} 145 value={"bypass_governance"} 146 id="bypass_governance" 147 name="bypass_governance" 148 onChange={(e) => { 149 setBypassGovernance(!bypassGovernance); 150 }} 151 description="" 152 /> 153 </div> 154 </Fragment> 155 )} 156 {deleteVersions && ( 157 <Fragment> 158 <div 159 style={{ 160 marginTop: 10, 161 border: "#c83b51 1px solid", 162 borderRadius: 3, 163 padding: 5, 164 backgroundColor: "#c83b5120", 165 color: "#c83b51", 166 }} 167 > 168 This will remove the object as well as all of its versions,{" "} 169 <br /> 170 This action is irreversible. 171 </div> 172 <br /> 173 Are you sure you want to continue? 174 </Fragment> 175 )} 176 </Fragment> 177 } 178 /> 179 ); 180 }; 181 182 export default DeleteObject;