Merge PR #1111: Respect workbook structure protection in sheet rename, move, copy, hide and unhide (#1049)

This commit is contained in:
aiongg
2026-10-22 02:37:22 +00:00
2 changed files with 38 additions and 0 deletions
+26
View File
@@ -426,6 +426,32 @@ mod tests {
s.execute("review.unprotectSheet", json!({"password": "old"})).unwrap();
}
#[test]
fn protected_structure_blocks_rename_move_copy_hide_and_unhide_sheet() {
let mut s = book();
let names = |s: &Session| s.doc().unwrap().wb.sheets.iter().map(|x| x.name.clone()).collect::<Vec<_>>();
let before = names(&s);
s.execute("review.protectWorkbook", json!({})).unwrap();
assert!(s.execute("sheet.rename", json!({"sheet": 0, "name": "Renamed"})).is_err());
assert!(s.execute("sheet.move", json!({"sheet": 0, "to": 1})).is_err());
assert!(s.execute("sheet.move", json!({"sheet": 0, "to": 1, "copy": true})).is_err());
assert!(s.execute("sheet.hide", json!({"sheet": 1})).is_err());
assert_eq!(names(&s), before);
assert!(s.doc().unwrap().wb.sheets.iter().all(|x| x.visibility == Visibility::Visible));
s.execute("review.protectWorkbook", json!({})).unwrap();
s.execute("sheet.rename", json!({"sheet": 0, "name": "Renamed"})).unwrap();
s.execute("sheet.move", json!({"sheet": 0, "to": 1})).unwrap();
s.execute("sheet.move", json!({"sheet": 0, "to": 1, "copy": true})).unwrap();
assert_eq!(s.doc().unwrap().wb.sheets.len(), 3);
s.execute("sheet.hide", json!({"sheet": 1})).unwrap();
s.execute("review.protectWorkbook", json!({})).unwrap();
assert!(s.execute("sheet.unhide", json!({"sheet": 1})).is_err());
assert!(s.execute("sheet.unhide", json!({})).is_err());
assert_eq!(s.doc().unwrap().wb.sheets[1].visibility, Visibility::Hidden);
s.execute("review.protectWorkbook", json!({})).unwrap();
s.execute("sheet.unhide", json!({"sheet": 1})).unwrap();
}
#[test]
fn protect_workbook_with_a_password_really_verifies() {
let mut s = book();
+12
View File
@@ -767,6 +767,9 @@ fn delete_sheet(s: &mut Session, p: &Json) -> Result<Json> {
}
fn rename_sheet(s: &mut Session, p: &Json) -> Result<Json> {
if s.doc()?.wb.protected_structure {
return Err(EngineError::Other("The workbook is protected and cannot be changed.".into()));
}
let i = target_sheet(s, p)?;
let Some(name) = str_param(p, "name").map(|n| n.trim().to_string()) else { return Err(bad("sheet.rename", "missing `name`")) };
s.doc()?.wb.check_sheet_name(&name, Some(i)).map_err(EngineError::Other)?;
@@ -818,6 +821,9 @@ fn rename_sheet(s: &mut Session, p: &Json) -> Result<Json> {
}
fn move_sheet(s: &mut Session, p: &Json) -> Result<Json> {
if s.doc()?.wb.protected_structure {
return Err(EngineError::Other("The workbook is protected and cannot be changed.".into()));
}
let i = target_sheet(s, p)?;
let copy = bool_param(p, "copy").unwrap_or(false);
let to = p.get("to").and_then(Json::as_u64).map(|v| v as usize).ok_or_else(|| bad("sheet.move", "missing `to`"))?;
@@ -895,6 +901,9 @@ fn tab_color(s: &mut Session, p: &Json) -> Result<Json> {
}
fn hide_sheet(s: &mut Session, p: &Json) -> Result<Json> {
if s.doc()?.wb.protected_structure {
return Err(EngineError::Other("The workbook is protected and cannot be changed.".into()));
}
let i = target_sheet(s, p)?;
if s.doc()?.wb.sheets.iter().filter(|s| s.visibility == Visibility::Visible).count() <= 1 {
return Err(EngineError::Other("A workbook must contain at least one visible worksheet.".into()));
@@ -909,6 +918,9 @@ fn hide_sheet(s: &mut Session, p: &Json) -> Result<Json> {
}
fn unhide_sheet(s: &mut Session, p: &Json) -> Result<Json> {
if s.doc()?.wb.protected_structure {
return Err(EngineError::Other("The workbook is protected and cannot be changed.".into()));
}
if p.get("sheet").is_none() {
let hidden: Vec<String> = s.doc()?.wb.sheets.iter().filter(|s| s.visibility != Visibility::Visible).map(|s| s.name.clone()).collect();
if hidden.is_empty() {