From a11657c1455bffc03e865d1c410e5406d03c11c8 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 3 Feb 2024 14:54:01 +0100 Subject: [PATCH] Allow creating new project from zip archive --- novelwriter/core/coretools.py | 32 +++++++++++++++++++++----------- novelwriter/shared.py | 18 ++++++++++-------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py index 0eb4197b..b493d1b0 100644 --- a/novelwriter/core/coretools.py +++ b/novelwriter/core/coretools.py @@ -32,6 +32,7 @@ import logging from typing import Iterable from pathlib import Path from functools import partial +from zipfile import ZipFile, is_zipfile from PyQt5.QtCore import QCoreApplication @@ -459,7 +460,7 @@ class ProjectBuilder: """ source = data.get("template") if not (isinstance(source, Path) and source.is_file() - and source.name == nwFiles.PROJ_FILE): + and (source.name == nwFiles.PROJ_FILE or is_zipfile(source))): logger.error("Could not access source project: %s", source) return False @@ -478,10 +479,22 @@ class ProjectBuilder: dstCont = dstPath / "content" dstPath.mkdir(exist_ok=True) dstCont.mkdir(exist_ok=True) - shutil.copy2(srcPath / nwFiles.PROJ_FILE, dstPath) - for contFile in srcCont.iterdir(): - if contFile.is_file() and contFile.suffix == ".nwd" and isHandle(contFile.stem): - shutil.copy2(contFile, dstCont) + try: + if is_zipfile(source): + with ZipFile(source) as zipObj: + for member in zipObj.namelist(): + if member == nwFiles.PROJ_FILE: + zipObj.extract(member, dstPath) + elif member.startswith("content") and member.endswith(".nwd"): + zipObj.extract(member, dstPath) + else: + shutil.copy2(srcPath / nwFiles.PROJ_FILE, dstPath) + for item in srcCont.iterdir(): + if item.is_file() and item.suffix == ".nwd" and isHandle(item.stem): + shutil.copy2(item, dstCont) + except Exception as exc: + SHARED.error(self.tr("Could not copy project files."), exc=exc) + return False # Open the copied project and update settings project = NWProject() @@ -505,14 +518,11 @@ class ProjectBuilder: """Make a copy of the sample project by extracting the sample.zip file to the new path. """ - pkgSample = CONFIG.assetPath("sample.zip") - if pkgSample.is_file(): + if (sample := CONFIG.assetPath("sample.zip")).is_file(): try: - shutil.unpack_archive(pkgSample, path) + shutil.unpack_archive(sample, path) except Exception as exc: - SHARED.error(self.tr( - "Failed to create a new example project." - ), exc=exc) + SHARED.error(self.tr("Failed to create a new example project."), exc=exc) return False else: SHARED.error(self.tr( diff --git a/novelwriter/shared.py b/novelwriter/shared.py index c2c342f8..28d1cf74 100644 --- a/novelwriter/shared.py +++ b/novelwriter/shared.py @@ -221,16 +221,18 @@ class SharedData(QObject): def getProjectPath(self, parent: QWidget, path: str | Path | None = None, allowZip: bool = False) -> Path | None: """Open the file dialog and select a novelWriter project file.""" - ext = [ - self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE), - self.tr("All files ({0})").format("*"), - ] if allowZip: - ext.insert(1, self.tr("Zip Archives ({0})").format("*.zip")) - projFile, _ = QFileDialog.getOpenFileName( - parent, self.tr("Open Project"), str(path or ""), filter=";;".join(ext) + label = self.tr("novelWriter Project File or Zip") + ext = f"{nwFiles.PROJ_FILE} *.zip" + else: + label = self.tr("novelWriter Project File") + ext = nwFiles.PROJ_FILE + selected, _ = QFileDialog.getOpenFileName( + parent, self.tr("Open Project"), str(path or ""), filter=";;".join( + [f"{label} ({ext})", "{0} (*)".format(self.tr("All Files"))] + ) ) - return Path(projFile) if projFile else None + return Path(selected) if selected else None def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None: """Find a top level widget."""