github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects.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 useApi from "../../../../Common/Hooks/useApi"; 20 import ConfirmDialog from "../../../../Common/ModalWrapper/ConfirmDialog"; 21 import { ConfirmDeleteIcon, Switch } from "mds"; 22 import { setErrorSnackMessage } from "../../../../../../systemSlice"; 23 import { AppState, useAppDispatch } from "../../../../../../store"; 24 import { hasPermission } from "../../../../../../common/SecureComponent"; 25 import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; 26 import { useSelector } from "react-redux"; 27 import { BucketVersioningResponse } from "api/consoleApi"; 28 import { api } from "../../../../../../api"; 29 import { encodeURLString } from "../../../../../../common/utils"; 30 31 interface IDeleteObjectProps { 32 closeDeleteModalAndRefresh: (refresh: boolean) => void; 33 deleteOpen: boolean; 34 selectedObjects: string[]; 35 selectedBucket: string; 36 37 versioning: BucketVersioningResponse; 38 } 39 40 const DeleteObject = ({ 41 closeDeleteModalAndRefresh, 42 deleteOpen, 43 selectedBucket, 44 selectedObjects, 45 46 versioning, 47 }: IDeleteObjectProps) => { 48 const dispatch = useAppDispatch(); 49 const onDelSuccess = () => closeDeleteModalAndRefresh(true); 50 const onDelError = (err: ErrorResponseHandler) => 51 dispatch(setErrorSnackMessage(err)); 52 const onClose = () => closeDeleteModalAndRefresh(false); 53 54 const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError); 55 56 const [deleteVersions, setDeleteVersions] = useState<boolean>(false); 57 const [bypassGovernance, setBypassGovernance] = useState<boolean>(false); 58 59 const retentionConfig = useSelector( 60 (state: AppState) => state.objectBrowser.retentionConfig, 61 ); 62 63 const canBypass = 64 hasPermission( 65 [selectedBucket], 66 [IAM_SCOPES.S3_BYPASS_GOVERNANCE_RETENTION], 67 ) && retentionConfig?.mode === "governance"; 68 69 if (!selectedObjects) { 70 return null; 71 } 72 const onConfirmDelete = () => { 73 let toSend = []; 74 for (let i = 0; i < selectedObjects.length; i++) { 75 if (selectedObjects[i].endsWith("/")) { 76 toSend.push({ 77 path: selectedObjects[i], 78 versionID: "", 79 recursive: true, 80 }); 81 } else { 82 toSend.push({ 83 path: selectedObjects[i], 84 versionID: "", 85 recursive: false, 86 }); 87 } 88 } 89 90 if (toSend) { 91 if (selectedObjects.length === 1) { 92 const firstObject = selectedObjects[0]; 93 api.buckets 94 .deleteObject(selectedBucket, { 95 prefix: encodeURLString(firstObject), 96 all_versions: deleteVersions, 97 bypass: bypassGovernance, 98 recursive: firstObject.endsWith("/"), //if it is just a prefix 99 }) 100 .then(onDelSuccess) 101 .catch((err) => { 102 dispatch( 103 setErrorSnackMessage({ 104 errorMessage: `Could not delete object. ${err.statusText}. ${ 105 retentionConfig 106 ? "Please check retention mode and if object is WORM protected." 107 : "" 108 }`, 109 detailedError: "", 110 }), 111 ); 112 }); 113 } else { 114 invokeDeleteApi( 115 "POST", 116 `/api/v1/buckets/${selectedBucket}/delete-objects?all_versions=${deleteVersions}${ 117 bypassGovernance ? "&bypass=true" : "" 118 }`, 119 toSend, 120 ); 121 } 122 } 123 }; 124 125 const isVersionedDelete = 126 versioning?.status === "Enabled" || versioning?.status === "Suspended"; 127 128 return ( 129 <ConfirmDialog 130 title={`Delete Objects`} 131 confirmText={"Delete"} 132 isOpen={deleteOpen} 133 titleIcon={<ConfirmDeleteIcon />} 134 isLoading={deleteLoading} 135 onConfirm={onConfirmDelete} 136 onClose={onClose} 137 confirmationContent={ 138 <Fragment> 139 Are you sure you want to delete the selected {selectedObjects.length}{" "} 140 objects?{" "} 141 {isVersionedDelete && ( 142 <Fragment> 143 <br /> 144 <br /> 145 <Switch 146 label={"Delete All Versions"} 147 indicatorLabels={["Yes", "No"]} 148 checked={deleteVersions} 149 value={"delete_versions"} 150 id="delete-versions" 151 name="delete-versions" 152 onChange={(e) => { 153 setDeleteVersions(!deleteVersions); 154 }} 155 description="" 156 /> 157 {canBypass && deleteVersions && ( 158 <Fragment> 159 <div 160 style={{ 161 marginTop: 10, 162 }} 163 > 164 <Switch 165 label={"Bypass Governance Mode"} 166 indicatorLabels={["Yes", "No"]} 167 checked={bypassGovernance} 168 value={"bypass_governance"} 169 id="bypass_governance" 170 name="bypass_governance" 171 onChange={(e) => { 172 setBypassGovernance(!bypassGovernance); 173 }} 174 description="" 175 /> 176 </div> 177 </Fragment> 178 )} 179 {deleteVersions && ( 180 <Fragment> 181 <div 182 style={{ 183 marginTop: 10, 184 border: "#c83b51 1px solid", 185 borderRadius: 3, 186 padding: 5, 187 backgroundColor: "#c83b5120", 188 color: "#c83b51", 189 }} 190 > 191 This will remove the objects as well as all of their 192 versions, <br /> 193 This action is irreversible. 194 </div> 195 <br /> 196 Are you sure you want to continue? 197 </Fragment> 198 )} 199 </Fragment> 200 )} 201 </Fragment> 202 } 203 /> 204 ); 205 }; 206 207 export default DeleteObject;