feat: menu contextuel Déplacer/Dupliquer sidebar + réparation drag & drop interne #201 #BUG-117
This commit is contained in:
@@ -1534,6 +1534,80 @@ def move_path(vault_name: str, source_path: str, destination_dir: str = "") -> d
|
||||
"item_type": item_type,
|
||||
}
|
||||
|
||||
def duplicate_path(vault_name: str, source_path: str, destination_dir: str = "") -> dict[str, Any]:
|
||||
"""Duplicate a file or directory (copy) into *destination_dir* (same vault).
|
||||
|
||||
The copy is named ``<stem> copie<ext>`` (then ``copie 2``, ``copie 3``… on
|
||||
collision). Directories are copied recursively.
|
||||
|
||||
Raises:
|
||||
ServiceError: ``not_found`` (404), ``read_only`` (403),
|
||||
``permission_denied`` (403), ``invalid_arguments`` (400) when the
|
||||
destination is inside the source directory.
|
||||
"""
|
||||
root = get_vault_root(vault_name)
|
||||
_ensure_writable(root)
|
||||
|
||||
source = resolve_safe_path(root, source_path)
|
||||
if not source.exists():
|
||||
raise ServiceError(
|
||||
f"Source not found: {source_path}",
|
||||
code="not_found",
|
||||
status=404,
|
||||
details={"vault": vault_name, "path": source_path},
|
||||
)
|
||||
|
||||
is_directory = source.is_dir()
|
||||
dest_clean = (destination_dir or "").strip("/")
|
||||
if dest_clean:
|
||||
dest_parent = resolve_safe_path(root, dest_clean)
|
||||
if not dest_parent.exists() or not dest_parent.is_dir():
|
||||
raise ServiceError(
|
||||
f"Destination directory not found: {destination_dir}",
|
||||
code="not_found",
|
||||
status=404,
|
||||
details={"vault": vault_name, "path": destination_dir},
|
||||
)
|
||||
else:
|
||||
dest_parent = root
|
||||
|
||||
if is_directory and str(dest_parent.resolve()) in (str(source.resolve()), str(source.resolve()) + os.sep, str(source.resolve()) + "/"):
|
||||
raise ServiceError(
|
||||
"Cannot duplicate a directory inside itself",
|
||||
code="invalid_arguments",
|
||||
status=400,
|
||||
details={"vault": vault_name, "path": source_path},
|
||||
)
|
||||
|
||||
ext = "" if is_directory else source.suffix
|
||||
base = dest_parent / (source.stem + " copie" + ext)
|
||||
target = base
|
||||
n = 2
|
||||
while target.exists():
|
||||
target = base.parent / (source.stem + " copie " + str(n) + ext)
|
||||
n += 1
|
||||
|
||||
try:
|
||||
if is_directory:
|
||||
shutil.copytree(source, target)
|
||||
else:
|
||||
shutil.copy2(source, target)
|
||||
except PermissionError as e:
|
||||
raise ServiceError("Permission denied: cannot duplicate item", code="permission_denied", status=403) from e
|
||||
|
||||
new_rel = _rel(root, target)
|
||||
item_type = "directory" if is_directory else "file"
|
||||
logger.info(f"Item duplicated: {vault_name}/{source_path} -> {new_rel}")
|
||||
return {
|
||||
"success": True,
|
||||
"vault": vault_name,
|
||||
"source_path": _rel(root, source),
|
||||
"new_path": new_rel,
|
||||
"item_type": item_type,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# ── D3. Find & replace ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user