diff --git a/nw/core/document.py b/nw/core/document.py index 4cfd09a3..a28804a9 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -93,8 +93,9 @@ class NWDoc(): if self.theItem.parHandle == self.theProject.projTree.trashRoot(): self.docEditable = False - docDir, docFile = self._assemblePath(self.docHandle, self.FILE_MN) - self.fileLoc = path.join(docDir,docFile) + docDir = "content" + docFile = self.docHandle+".nwd" + self.fileLoc = path.join(docDir, docFile) logger.debug("Opening document %s" % self.fileLoc) dataDir = path.join(self.theProject.projPath, docDir) docPath = path.join(dataDir, docFile) @@ -139,8 +140,9 @@ class NWDoc(): if self.docHandle is None or not self.docEditable: return False - docDir, docFile = self._assemblePath(self.docHandle, self.FILE_MN) - logger.debug("Saving document %s" % path.join(docDir,docFile)) + docDir = "content" + docFile = self.docHandle+".nwd" + logger.debug("Saving document %s" % path.join(docDir, docFile)) dataPath = path.join(self.theProject.projPath, docDir) docPath = path.join(dataPath, docFile) if not path.isdir(dataPath): @@ -159,12 +161,6 @@ class NWDoc(): self.makeAlert(["Could not save document.",str(e)], nwAlert.ERROR) return False - # Remove bak files from old file save method, if one exists - # This part can eventually be removed - docBack = path.join(dataPath, docFile[:-3]+"bak") - if path.isfile(docBack): - unlink(docBack) - # If we're here, the file was successfully saved, so we can # replace the temp file with the actual file if path.isfile(docPath): @@ -179,7 +175,8 @@ class NWDoc(): """Permanently delete a document source file and its backups from the project data folder. """ - docDir, docFile = self._assemblePath(tHandle, self.FILE_MN) + docDir = "content" + docFile = self.docHandle+".nwd" dataPath = path.join(self.theProject.projPath, docDir) chkList = [] chkList.append(path.join(dataPath, docFile)) @@ -226,18 +223,4 @@ class NWDoc(): return theMeta, thePath - ## - # Internal Functions - ## - - @staticmethod - def _assemblePath(tHandle, docExt): - """Assemble the file path for a given handle. - """ - if tHandle is None: - return None, None - docDir = "data_"+tHandle[0] - docFile = tHandle[1:13]+"_"+docExt - return docDir, docFile - # END Class NWDoc diff --git a/nw/core/project.py b/nw/core/project.py index 0c303f74..fbf04177 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -33,7 +33,7 @@ import logging import nw -from os import path, mkdir, listdir, unlink, rename +from os import path, mkdir, listdir, unlink, rename, rmdir from lxml import etree from hashlib import sha256 from time import time @@ -74,6 +74,7 @@ class NWProject(): # Class Settings self.projPath = None # The full path to where the currently open project is saved self.projMeta = None # The full path to the project's meta data folder + self.projData = None # The full path to the project's data folder self.projDict = None # The spell check dictionary self.projFile = None # The file name of the project main XML file @@ -195,6 +196,7 @@ class NWProject(): # Project Settings self.projPath = None self.projMeta = None + self.projData = None self.projDict = None self.projFile = nwFiles.PROJ_FILE self.projName = "" @@ -247,10 +249,13 @@ class NWProject(): logger.debug("Opening project: %s" % self.projPath) self.projMeta = path.join(self.projPath,"meta") + self.projData = path.join(self.projPath,"content") self.projDict = path.join(self.projMeta, nwFiles.PROJ_DICT) if not self._checkFolder(self.projMeta): return False + if not self._checkFolder(self.projData): + return False if overrideLock: self._clearLockFile() @@ -291,7 +296,7 @@ class NWProject(): self.clearProject() return False - xRoot = nwXML.getroot() + xRoot = nwXML.getroot() nwxRoot = xRoot.tag appVersion = "Unknown" @@ -314,13 +319,40 @@ class NWProject(): logger.verbose("XML root is %s" % nwxRoot) logger.verbose("File version is %s" % fileVersion) - if not nwxRoot == "novelWriterXML" or not fileVersion == "1.0": + # Check File Type + # =============== + if not nwxRoot == "novelWriterXML": self.makeAlert( - "Project file does not appear to be a novelWriterXML file version 1.0", + "Project file does not appear to be a novelWriterXML file.", nwAlert.ERROR ) return False + # Check Project Storage Version + # ============================= + if fileVersion == "1.0": + msgBox = QMessageBox() + msgRes = msgBox.question(self.theParent, "Old Project Version", ( + "The project file and data is created by a %s version lower than 0.7. " + "Do you want to upgrade the project to the most recent format?

" + "Note that after the upgrade, you cannot open the project with an older " + "version of novelWriter any more, so make sure you have a recent backup." + ) % nw.__package__) + if msgRes == QMessageBox.Yes: + self._updateStorage() + else: + return False + elif fileVersion != "1.1": + self.makeAlert(( + "Unknown or unsupported %s project format. " + "The project cannot be opened by this version of %s." + ) % ( + nw.__package__, nw.__package__ + ), nwAlert.ERROR) + return False + + # Check novelWriter Version + # ========================= if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI: msgBox = QMessageBox() msgRes = msgBox.question(self.theParent, "Version Conflict", ( @@ -333,6 +365,8 @@ class NWProject(): if msgRes != QMessageBox.Yes: return False + # Start Parsing XML + # ================= for xChild in xRoot: if xChild.tag == "project": logger.debug("Found project meta") @@ -404,16 +438,21 @@ class NWProject(): file. """ if self.projPath is None: - self.makeAlert("Project path not set, cannot save.", nwAlert.ERROR) + self.makeAlert( + "Project path not set, cannot save project.", nwAlert.ERROR + ) return False - self.projMeta = path.join(self.projPath,"meta") + self.projMeta = path.join(self.projPath, "meta") + self.projData = path.join(self.projPath, "content") saveTime = time() if not self._checkFolder(self.projPath): return False if not self._checkFolder(self.projMeta): return False + if not self._checkFolder(self.projData): + return False logger.debug("Saving project: %s" % self.projPath) @@ -427,7 +466,7 @@ class NWProject(): nwXML = etree.Element("novelWriterXML",attrib={ "appVersion" : str(nw.__version__), "hexVersion" : str(nw.__hexversion__), - "fileVersion" : "1.0", + "fileVersion" : "1.1", "saveCount" : str(self.saveCount), "autoCount" : str(self.autoCount), "timeStamp" : formatTimeStamp(saveTime), @@ -1016,6 +1055,55 @@ class NWProject(): return True + def _updateStorage(self): + """Updates the project storage folder from 1.0 to 1.1. + """ + contDir = path.join(self.projPath, "content") + self._checkFolder(contDir) + errList = [] + + for projItem in listdir(self.projPath): + itemPath = path.join(self.projPath, projItem) + if not path.isdir(itemPath) or not projItem.startswith("data_"): + continue + for dataFile in listdir(itemPath): + dataPath = path.join(itemPath, dataFile) + if dataFile.endswith(".bak"): + try: + unlink(dataPath) + logger.info("Deleted file: %s" % dataPath) + except: + errList.append("Failed to delete: %s" % dataPath) + + elif dataFile.endswith(".nwd") and len(dataFile) == 21: + tHandle = projItem[-1]+dataFile[:12] + newPath = path.join(contDir, tHandle+".nwd") + try: + rename(dataPath, newPath) + logger.info("Moved file: %s" % dataPath) + logger.info("New location: %s" % newPath) + except: + errList.append("Failed to move: %s" % dataPath) + + else: + newPath = path.join(self.projPath, "unknown_"+dataFile) + try: + rename(dataPath, newPath) + logger.info("Moved file: %s" % dataPath) + logger.info("New location: %s" % newPath) + except: + errList.append("Failed to move: %s" % dataPath) + try: + rmdir(itemPath) + logger.info("Removed folder: %s" % itemPath) + except: + errList.append("Failed to delete: %s" % itemPath) + + if errList: + self.makeAlert(errList, nwAlert.ERROR) + + return + # END Class NWProject # ================================================================================================ # diff --git a/nw/core/tools.py b/nw/core/tools.py index a6eb244b..8aa81d7b 100644 --- a/nw/core/tools.py +++ b/nw/core/tools.py @@ -89,7 +89,7 @@ def projectMaintenance(theProject): if path.isdir(theProject.projPath): cacheDir = path.join(theProject.projPath, "cache") if path.isdir(cacheDir): - logger.info("Deprecated cache folder found") + logger.info("Deprecated cache folder content found") rmList = [] for i in range(10): rmList.append(path.join(cacheDir, "nwProject.nwx.%d" % i)) @@ -101,11 +101,6 @@ def projectMaintenance(theProject): unlink(rmFile) except Exception as e: logger.error(str(e)) - logger.info("Deleting: %s" % cacheDir) - try: - rmdir(cacheDir) - except Exception as e: - logger.error(str(e)) # Remove no longer used meta files rmList = []