@@ -240,10 +240,16 @@ import { show_modal_dialog, apply_modal_dialog } from "cockpit-components-dialog
240
240
import { ListingTable } from "cockpit-components-table.jsx" ;
241
241
import { FormHelper } from "cockpit-components-form-helper" ;
242
242
243
- import { fmt_size , block_name , format_size_and_text , format_delay , for_each_async } from "./utils.js" ;
243
+ import {
244
+ decode_filename , fmt_size , block_name , format_size_and_text , format_delay , for_each_async ,
245
+ is_available_block
246
+ } from "./utils.js" ;
244
247
import { fmt_to_fragments } from "utils.jsx" ;
248
+
245
249
import client from "./client.js" ;
246
250
251
+ import fsys_is_empty_sh from "./fsys-is-empty.sh" ;
252
+
247
253
const _ = cockpit . gettext ;
248
254
249
255
function make_rows ( fields , values , errors , onChange ) {
@@ -328,6 +334,23 @@ const Body = ({ body, teardown, fields, values, errors, isFormHorizontal, onChan
328
334
) ;
329
335
} ;
330
336
337
+ const ExtraConfirmation = ( { title, text, confirm_text, onChange } ) => {
338
+ const [ confirmed , setConfirmed ] = useState ( false ) ;
339
+
340
+ return (
341
+ < Alert variant = "danger" isInline
342
+ title = { title } >
343
+ { text }
344
+ < Checkbox isChecked = { confirmed }
345
+ id = "dialog-confirm"
346
+ label = { confirm_text }
347
+ onChange = { ( _ , val ) => {
348
+ setConfirmed ( val ) ;
349
+ onChange ( val ) ;
350
+ } } />
351
+ </ Alert > ) ;
352
+ } ;
353
+
331
354
function flatten_fields ( fields ) {
332
355
return fields . reduce (
333
356
( acc , val ) => acc . concat ( [ val ] ) . concat ( val . options && val . options . nested_fields
@@ -340,6 +363,8 @@ export const dialog_open = (def) => {
340
363
const nested_fields = def . Fields || [ ] ;
341
364
const fields = flatten_fields ( nested_fields ) ;
342
365
const values = { } ;
366
+ let confirmation = null ;
367
+ let confirmed = false ;
343
368
let errors = null ;
344
369
345
370
fields . forEach ( f => { values [ f . tag ] = f . initial_value } ) ;
@@ -415,8 +440,10 @@ export const dialog_open = (def) => {
415
440
caption : variant . Title ,
416
441
style : actions . length == 0 ? "primary" : "secondary" ,
417
442
danger : def . Action . Danger || def . Action . DangerButton ,
418
- disabled : running_promise != null || ( def . Action . disable_on_error &&
419
- errors && errors . toString ( ) != "[object Object]" ) ,
443
+ disabled : ( running_promise != null ||
444
+ ( def . Action . disable_on_error &&
445
+ errors && errors . toString ( ) != "[object Object]" ) ||
446
+ ( confirmation && ! confirmed ) ) ,
420
447
clicked : progress_callback => run_action ( progress_callback , variant . tag ) ,
421
448
} ) ;
422
449
}
@@ -436,13 +463,21 @@ export const dialog_open = (def) => {
436
463
}
437
464
}
438
465
439
- const extra = (
440
- < div >
441
- { def . Action && def . Action . Danger
442
- ? < HelperText > < HelperTextItem variant = "error" > { def . Action . Danger } </ HelperTextItem > </ HelperText >
443
- : null
444
- }
445
- </ div > ) ;
466
+ let extra = null ;
467
+ if ( confirmation ) {
468
+ extra = < ExtraConfirmation title = { confirmation . Title }
469
+ text = { confirmation . Text }
470
+ confirm_text = { confirmation . ConfirmText }
471
+ onChange = { val => {
472
+ confirmed = val ;
473
+ update_footer ( ) ;
474
+ } } /> ;
475
+ } else if ( def . Action && def . Action . Danger ) {
476
+ extra = (
477
+ < div >
478
+ < HelperText > < HelperTextItem variant = "error" > { def . Action . Danger } </ HelperTextItem > </ HelperText >
479
+ </ div > ) ;
480
+ }
446
481
447
482
return {
448
483
idle_message : ( running_promise
@@ -537,6 +572,12 @@ export const dialog_open = (def) => {
537
572
update ( ) ;
538
573
} ,
539
574
575
+ need_confirmation : ( conf ) => {
576
+ confirmation = conf ;
577
+ confirmed = false ;
578
+ update_footer ( ) ;
579
+ } ,
580
+
540
581
close : ( ) => {
541
582
dlg . footerProps . dialog_done ( ) ;
542
583
}
@@ -1207,13 +1248,14 @@ const teardown_block_name = use => {
1207
1248
} ;
1208
1249
1209
1250
export const TeardownMessage = ( usage , expect_single_unmount ) => {
1210
- if ( usage . length == 0 )
1251
+ if ( ! usage . Teardown )
1211
1252
return null ;
1212
1253
1213
1254
if ( is_expected_unmount ( usage , expect_single_unmount ) )
1214
1255
return < StopProcessesMessage mount_point = { expect_single_unmount } users = { usage [ 0 ] . users } /> ;
1215
1256
1216
1257
const rows = [ ] ;
1258
+ let have_data = false ;
1217
1259
usage . forEach ( ( use , index ) => {
1218
1260
if ( use . block ) {
1219
1261
const name = teardown_block_name ( use ) ;
@@ -1223,12 +1265,22 @@ export const TeardownMessage = (usage, expect_single_unmount) => {
1223
1265
if ( location === false )
1224
1266
location = _ ( "(Not part of target)" ) ;
1225
1267
}
1268
+ if ( use . data_warning )
1269
+ have_data = true ;
1226
1270
rows . push ( {
1227
1271
columns : [ name ,
1228
1272
location || "-" ,
1229
1273
use . actions . length ? use . actions . join ( ", " ) : "-" ,
1230
1274
{
1231
- title : < UsersPopover users = { use . users || [ ] } /> ,
1275
+ title : < >
1276
+ < UsersPopover users = { use . users || [ ] } />
1277
+ { use . data_warning &&
1278
+ < span >
1279
+ < ExclamationTriangleIcon className = "ct-icon-exclamation-triangle" /> { "\n" }
1280
+ { use . data_warning }
1281
+ </ span >
1282
+ }
1283
+ </ > ,
1232
1284
props : { className : "pf-v5-u-text-align-right" }
1233
1285
}
1234
1286
]
@@ -1247,6 +1299,11 @@ export const TeardownMessage = (usage, expect_single_unmount) => {
1247
1299
{ title : "" }
1248
1300
] }
1249
1301
rows = { rows } />
1302
+ { have_data &&
1303
+ < >
1304
+ < br />
1305
+ </ >
1306
+ }
1250
1307
</ div > ) ;
1251
1308
} ;
1252
1309
@@ -1268,24 +1325,48 @@ export function teardown_danger_message(usage, expect_single_unmount) {
1268
1325
}
1269
1326
}
1270
1327
1271
- export function init_active_usage_processes ( client , usage , expect_single_unmount ) {
1328
+ // XXX - rename to init_teardown_usage
1329
+
1330
+ export function init_teardown_usage ( client , usage , expect_single_unmount ) {
1272
1331
return {
1273
- title : _ ( "Checking related processes" ) ,
1274
- func : dlg => {
1275
- return for_each_async ( usage , u => {
1332
+ title : _ ( "Checking filesystem usage" ) ,
1333
+ func : async function ( dlg ) {
1334
+ let have_data = false ;
1335
+ for ( const u of usage ) {
1276
1336
if ( u . usage == "mounted" ) {
1277
- return client . find_mount_users ( u . location )
1278
- . then ( users => {
1279
- u . users = users ;
1280
- } ) ;
1281
- } else
1282
- return Promise . resolve ( ) ;
1283
- } ) . then ( ( ) => {
1284
- dlg . set_attribute ( "Teardown" , TeardownMessage ( usage , expect_single_unmount ) ) ;
1285
- const msg = teardown_danger_message ( usage , expect_single_unmount ) ;
1286
- if ( msg )
1287
- dlg . add_danger ( msg ) ;
1288
- } ) ;
1337
+ u . users = await client . find_mount_users ( u . location ) ;
1338
+ }
1339
+ if ( client . in_anaconda_mode ( ) && ! expect_single_unmount && u . block ) {
1340
+ if ( u . block . IdUsage == "filesystem" ) {
1341
+ const empty = await cockpit . script ( fsys_is_empty_sh ,
1342
+ [ decode_filename ( u . block . PreferredDevice ) ] ,
1343
+ { superuser : true , err : "message" } ) ;
1344
+ if ( empty . trim ( ) != "yes" )
1345
+ u . data_warning = _ ( "Filesystem is not empty" ) ;
1346
+ } else if ( u . block . IdUsage == "crypto" && ! client . blocks_cleartext [ u . block . path ] ) {
1347
+ u . data_warning = _ ( "Locked encrypted device might contain data" ) ;
1348
+ } else if ( ! client . blocks_ptable [ u . block . path ] &&
1349
+ u . block . IdUsage != "raid" &&
1350
+ ! is_available_block ( client , u . block ) ) {
1351
+ u . data_warning = _ ( "Device contains unrecognized data" ) ;
1352
+ }
1353
+ if ( u . data_warning )
1354
+ have_data = true ;
1355
+ }
1356
+ }
1357
+
1358
+ if ( have_data ) {
1359
+ usage . Teardown = true ;
1360
+ dlg . need_confirmation ( {
1361
+ Title : _ ( "Important data might be deleted." ) ,
1362
+ Text : _ ( "The device that you are about to erase or delete might contain important data. Please confirm that you really want to delete or erase this device." ) ,
1363
+ ConfirmText : _ ( "Yes, delete my data." ) ,
1364
+ } ) ;
1365
+ }
1366
+ dlg . set_attribute ( "Teardown" , TeardownMessage ( usage , expect_single_unmount ) ) ;
1367
+ const msg = teardown_danger_message ( usage , expect_single_unmount ) ;
1368
+ if ( msg )
1369
+ dlg . add_danger ( msg ) ;
1289
1370
}
1290
1371
} ;
1291
1372
}
0 commit comments