Merge pull request #7215 from dotCooCoo/fix/new-profile-errors-hide-path

New Document profiles: errors name the profile file by its file name outside the read roots
This commit is contained in:
BFlat
2026-11-14 01:53:47 +07:00
committed by GitHub
4 changed files with 52 additions and 8 deletions
+13 -6
View File
@@ -63,7 +63,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Edit Profile",
[],
None,
"{name: a category of file.newPresets} open the category's New Document profile as a document to edit (File › Save keeps the changes for the next file.new); a category without a profile file gets the shipped profile written first → document.open's result",
"{name: a category of file.newPresets} open the category's New Document profile as a document to edit (File › Save keeps the changes for the next file.new); a category without a profile file gets the shipped profile written first; under automation roots it needs write and read access to the file, and an error names the file by its file name outside the read roots → document.open's result",
always,
edit_profile
),
@@ -72,7 +72,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Reset Profile",
[],
None,
"{name: a category of file.newPresets} delete the category's profile file, so the shipped profile (today's defaults) applies again; an open copy of it stays open → {name, reset: whether a file was deleted}",
"{name: a category of file.newPresets} delete the category's profile file, so the shipped profile (today's defaults) applies again; an open copy of it stays open; under automation roots it needs write access to the file, and an error names the file by its file name outside the read roots → {name, reset: whether a file was deleted}",
always,
reset_profile
),
@@ -999,10 +999,17 @@ fn list_profiles(s: &mut Session, _: &Value) -> Result<Value> {
Ok(json!({ "profiles": profiles, "folder": s.new_doc_profile_dir().and_then(shown) }))
}
/// `msg`, an error about the profile file at `path`, as the request being served may see it: the
/// file named whole inside the read roots, else by its file name.
fn profile_error(msg: String, path: &str) -> super::EngineError {
super::fileio::naming_in_error(super::EngineError::Other(msg), path)
}
/// The file of profile `name` for `cmd` to change, checked against the request's write access.
fn writable_profile(s: &Session, name: &str, cmd: &str) -> Result<std::path::PathBuf> {
let path = s.profile_path(name).ok_or_else(|| bad(cmd, "New Document profiles need the preferences folder, which this run doesn't have"))?;
crate::file_access::check_write(&path.to_string_lossy()).map_err(super::EngineError::Other)?;
let file = path.to_string_lossy();
crate::file_access::check_write(&file).map_err(|e| profile_error(e, &file))?;
Ok(path)
}
@@ -1012,13 +1019,13 @@ fn edit_profile(s: &mut Session, p: &Value) -> Result<Value> {
let file = writable_profile(s, name, C)?;
let path = file.to_string_lossy().to_string();
// document.open reads it next: a profile it can't open is not written either.
crate::file_access::check_read(&path).map_err(super::EngineError::Other)?;
crate::file_access::check_read(&path).map_err(|e| profile_error(e, &path))?;
if !file.exists() {
let bytes = vectorcraft_format::save(&shipped_profile(s, name), false);
file.parent()
.map_or(Ok(()), std::fs::create_dir_all)
.and_then(|()| std::fs::write(&file, bytes))
.map_err(|e| super::EngineError::Other(format!("{path}: {e}")))?;
.map_err(|e| profile_error(format!("{path}: {e}"), &path))?;
}
s.execute("document.open", &json!({ "path": path }))
}
@@ -1031,7 +1038,7 @@ fn reset_profile(s: &mut Session, p: &Value) -> Result<Value> {
let reset = match std::fs::remove_file(&file) {
Ok(()) => true,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => false,
Err(e) => return Err(super::EngineError::Other(format!("{path}: {e}"))),
Err(e) => return Err(profile_error(format!("{path}: {e}"), &path)),
};
Ok(json!({ "name": name, "reset": reset }))
}
+33
View File
@@ -631,6 +631,39 @@ fn the_profile_follows_the_preset_access_and_reset() {
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn profile_errors_name_the_file_by_its_name_outside_the_read_roots() {
let (mut s, dir) = with_profiles("errors");
let elsewhere = dir.with_extension("elsewhere");
std::fs::create_dir_all(&dir).unwrap();
std::fs::create_dir_all(&elsewhere).unwrap();
let whole = dir.join("Print.vectorcraft").to_string_lossy().to_string();
let run = |s: &mut Session, roots: &std::sync::Arc<crate::file_access::AutomationRoots>, cmd: &str| {
crate::file_access::confine(Some(roots), || s.execute(cmd, &json!({"name": "Print"}))).unwrap_err().to_string()
};
// No write access: edit and reset are refused before anything is written or deleted.
let read_only = crate::file_access::AutomationRoots::new(&[elsewhere.as_path()], &[]).unwrap().unwrap();
for cmd in ["file.newProfiles.edit", "file.newProfiles.reset"] {
let e = run(&mut s, &read_only, cmd);
assert!(e.ends_with(": Print.vectorcraft") && !e.contains(&whole), "{cmd}: {e}");
}
// Write access without read access: edit is refused, and nothing is written.
let write_only = crate::file_access::AutomationRoots::new(&[elsewhere.as_path()], &[dir.as_path()]).unwrap().unwrap();
let e = run(&mut s, &write_only, "file.newProfiles.edit");
assert!(e.contains("outside the read root") && e.ends_with(": Print.vectorcraft") && !e.contains(&whole), "{e}");
assert!(!dir.join("Print.vectorcraft").exists());
// A file reset cannot delete (a folder in its place) is named by its file name too.
std::fs::create_dir_all(dir.join("Print.vectorcraft")).unwrap();
let e = run(&mut s, &write_only, "file.newProfiles.reset");
assert!(e.starts_with("Print.vectorcraft: ") && !e.contains(&whole), "{e}");
// Inside the read roots the error names the whole path.
let inside = crate::file_access::AutomationRoots::new(&[dir.as_path()], &[dir.as_path()]).unwrap().unwrap();
let e = run(&mut s, &inside, "file.newProfiles.reset");
assert!(e.starts_with(&format!("{whole}: ")), "{e}");
let _ = std::fs::remove_dir_all(&elsewhere);
let _ = std::fs::remove_dir_all(&dir);
}
/// The new document's links find their files from the profile's file, as a document opened from
/// that file does; a document without a profile has no such file.
#[test]
+5 -1
View File
@@ -267,7 +267,8 @@ the suite) keep the agent to the folders you give it:
Links in an opened document that point outside the read roots show as missing.
- An error names a path the client gave as given. A path the client did not give (the document's own file
in a save without `path` and in `file.revert`, also inside `action.batch`, an export preset's folder, the
Scripts folder, the document's folder in `links.relink {copy}`) is named
Scripts folder, the document's folder in `links.relink {copy}`, a New Document profile's file in
`file.newProfiles.edit` and `file.newProfiles.reset`) is named
whole only inside the read roots; outside them the error names it by its file name, or as
`a file or folder outside the read roots` when it has none, and keeps the roots and the reason, for example
`automation path rejected: outside the write root /work/project/out: poster.vectorcraft`.
@@ -6248,6 +6249,9 @@ with the defaults.
them.
- `file.newProfiles.reset {name}` deletes the category's file, so the shipped profile applies again, and returns
`{name, reset}`. `reset` is false when there was no file. A copy of the profile that is open stays open.
- Under automation roots, `file.newProfiles.edit` needs write and read access to the profile's file and
`file.newProfiles.reset` needs write access. An error names the file whole only inside the read roots, and
otherwise by its file name.
The profile is read on every `file.new`, so a replayed journal or a recorded action uses the profile as it is when it
runs, as `document.open` reads its file when it runs. A file larger than 64 MiB (VectorCraft's own limit) is not read.
+1 -1
View File
@@ -712,7 +712,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Document Setup › Color:** a Color tab shows the document color mode (RGB Color or CMYK Color), a Convert to CMYK or Convert to RGB box for a mode change (cleared: the colors keep their numbers), and the RGB and CMYK profiles assigned to the document, with a working-space entry for none. OK runs `edit.assignProfile` and then `file.documentColorMode` with the dialog's other changes in one `command.batch`, and the document's changes are one undo step. An unchanged Color tab adds no color commands, and Cancel, or OK with nothing changed, records no undo step. A Color tab field with a value of the wrong kind is an error, and the dialog stays open. The tab is VectorCraft's own layout; File › Document Color Mode and Edit › Assign Profile… stay as they are. Tests in `dialogs/document_setup.rs`.
- **Assign Profile and the working spaces:** Edit › Assign Profile… tags the document and leaves the working spaces of Edit › Color Settings… as they are, in Color Settings, the Separations panel and the settings saved for the next start. Colors show and convert through the profiles assigned; a change in Color Settings keeps the profiles the document in front is assigned in use, and removing an assigned profile in use puts the working space back in its place. The profiles in use follow the document in front: a new, opened or reverted document, a switch to another tab, a close, Undo and Redo of Assign Profile, a failed batch, each step of a batch, Export All Open and the update of placed documents put that document's profiles in use, and the working spaces where it is assigned none or no document is open. View › Proof Setup › Working CMYK simulates the working CMYK space of Color Settings in Proof Colors and Separations Preview, also in a document assigned another CMYK profile. Public references: [Changing the color profile for a document and About color working spaces](https://help.adobe.com/archive/en_US/illustrator/cs3/illustrator_cs3_help.pdf#page=150) and [Soft-proof presets](https://help.adobe.com/archive/en_US/illustrator/cs3/illustrator_cs3_help.pdf#page=134). Tests in `engine/tests/color_settings.rs`.
- **New Document's More Settings and Save Preset:** More Settings opens a pop-up over New Document with only Number of Artboards, the artboard layout, Change to Right-to-Left Layout, Spacing and Columns; Done, Escape or a click outside closes it and keeps the values. Save Preset turns the top of Preset Details into a Save Document Preset heading with the preset's name, Cancel and Save, and hides the document's name, Close and Create until the preset is saved or canceled; nothing in that mode creates a document. Save Preset's default name is the first "Preset N" no saved preset has. VectorCraft's own layout. Harness tests in `dialogs/new_document.rs` cover both.
- **New Document profiles:** each built-in category's presets start documents from that category's profile, a native document holding the starting swatches, graphic styles, brushes, symbols and other assets, the ruler and Document Setup options and the print settings ([Create a custom new document profile](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/start-a-new-file/create-a-custom-new-document-profile.html)). New Document's Manage Profiles pop-up opens a profile to edit and resets one to the shipped profile (`file.newProfiles`, `file.newProfiles.edit`, `file.newProfiles.reset`; `file.new {profile}`). The profiles live in VectorCraft's own `New Document Profiles` folder next to the preferences; a profile that cannot be read or is larger than 64 MiB leaves the defaults and is reported. Tests: `tests::newdoc::an_edited_profile_starts_the_documents_of_its_category`, `the_profile_follows_the_preset_access_and_reset`, `a_damaged_or_oversized_profile_leaves_the_defaults_and_is_reported`, `profile_names_are_checked_and_editing_needs_the_folder`, `dialogs::new_document::tests::manage_profiles_edits_and_resets_the_profiles`.
- **New Document profiles:** each built-in category's presets start documents from that category's profile, a native document holding the starting swatches, graphic styles, brushes, symbols and other assets, the ruler and Document Setup options and the print settings ([Create a custom new document profile](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/start-a-new-file/create-a-custom-new-document-profile.html)). New Document's Manage Profiles pop-up opens a profile to edit and resets one to the shipped profile (`file.newProfiles`, `file.newProfiles.edit`, `file.newProfiles.reset`; `file.new {profile}`). The profiles live in VectorCraft's own `New Document Profiles` folder next to the preferences; a profile that cannot be read or is larger than 64 MiB leaves the defaults and is reported. Tests: `tests::newdoc::an_edited_profile_starts_the_documents_of_its_category`, `the_profile_follows_the_preset_access_and_reset`, `profile_errors_name_the_file_by_its_name_outside_the_read_roots`, `a_damaged_or_oversized_profile_leaves_the_defaults_and_is_reported`, `profile_names_are_checked_and_editing_needs_the_folder`, `dialogs::new_document::tests::manage_profiles_edits_and_resets_the_profiles`.
- **Control bar with the Artboard tool:** while the Artboard tool is in use, the Control bar shows the active artboard's controls in place of the object controls: Preset, Portrait and Landscape, New Artboard, Delete Artboard, the Name field, Artboard Options, Rearrange All Artboards, the Transform link, Move and Scale Artwork with Artboard and, while the bar has room, the reference point with X and Y, and W and H with the Constrain Width and Height Proportions link. Each runs the command its Properties or Transform panel counterpart runs (`artboard.setProps`, `artboard.new`, `artboard.delete`, `tool.options {tool: "artboard"}`), and the reference point is the one the Transform panel uses. In an 800-point window the controls up to the Transform link fit, Move and Scale Artwork with Artboard show as their boxes alone with their names in tooltips, and the link opens the Transform panel with the artboard's position and size. Pointer and key tests in `panels/artboards.rs` cover the controls, W about the reference point, the orientation and the name. Public reference: [Using multiple artboards](https://web.archive.org/web/20210925094818/https://helpx.adobe.com/illustrator/using/using-multiple-artboards.html) (archived copy).
- **Names of new and renamed artboards, brushes and patterns:** `artboard.new` and `artboard.setProps` trim the white space around the name they are given, as the Artboards panel's rename does, and a blank name gives the artboard its default name, Artboard N for the Nth artboard. `brush.new`, `brush.duplicate`, `brush.options`, `object.pattern.make`, `object.pattern.saveCopy` and `pattern.options` trim the name as the Brush Options dialog and the Pattern Options panel do, and compare the trimmed name with the names there are; a blank name gives the name each command gives when there is none, and Brush Options and Pattern Options keep the name. Tests in `tests/panelcmds.rs`, `tests/brushsym.rs` and `tests/pattern.rs` cover each command. Public references: [Using multiple artboards](https://web.archive.org/web/20210925094818/https://helpx.adobe.com/illustrator/using/using-multiple-artboards.html), [Brushes](https://web.archive.org/web/20251018101754/https://helpx.adobe.com/illustrator/using/brushes.html) and [Create and edit patterns](https://web.archive.org/web/20211031180358/https://helpx.adobe.com/illustrator/using/create-edit-patterns.html) (archived copies).
- **Pattern names and swatch names:** `pattern.options` refuses a new name that another pattern, a swatch or a color group has. It takes a name that only the pattern's own swatches have. The reference page does not say what happens when the new name is taken; the refusal follows VectorCraft's rule that swatches and color groups share one namespace (`Document::swatch_name_taken`), which `swatch.new` and `swatch.edit` also follow. A rename gives the new name only to the pattern's swatches that had the old one; any other swatch with the old name keeps it, and art linked to that swatch stays linked. `object.pattern.make` and `object.pattern.saveCopy` number a color group's name as they number a swatch's. Tests in `tests/pattern.rs` cover each command. Public reference: [Create and edit patterns](https://web.archive.org/web/20211031180358/https://helpx.adobe.com/illustrator/using/create-edit-patterns.html) (archived copy).