diff --git a/cura/Backups/Backup.py b/cura/Backups/Backup.py index 2bf945ac64..866a6269bb 100644 --- a/cura/Backups/Backup.py +++ b/cura/Backups/Backup.py @@ -2,6 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. import io import os +import shutil + from typing import Optional from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile @@ -17,8 +19,7 @@ class Backup: """ # These files should be ignored when making a backup. - # Cura.cfg might contain secret data, so we don't back it up for now. - IGNORED_FILES = {"cura.log", "cura.cfg"} + IGNORED_FILES = {"cura.log"} def __init__(self, zip_file: bytes = None, meta_data: dict = None): self.zip_file = zip_file # type: Optional[bytes] @@ -33,6 +34,9 @@ class Backup: Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir) + # Ensure all current settings are saved. + CuraApplication.getInstance().saveSettings() + # We're using an easy to parse filename for when we're restoring edge cases: # TIMESTAMP.backup.VERSION.cura.zip archive = self._makeArchive(version_data_dir) @@ -76,14 +80,31 @@ class Backup: Logger.log("e", "Could not create archive from user data directory: %s", error) return None - def restore(self) -> None: + def restore(self) -> bool: """ Restore this backup. """ if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None): # We can restore without the minimum required information. Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.") - return + return False # global_data_dir = os.path.dirname(version_data_dir) - # TODO: restore logic. + # TODO: handle restoring older data version. + + version_data_dir = Resources.getDataStoragePath() + archive = ZipFile(io.BytesIO(self.zip_file), "r") + extracted = self._extractArchive(archive, version_data_dir) + if not extracted: + return False + return True + + @staticmethod + def _extractArchive(archive: "ZipFile", target_path: str) -> bool: + Logger.log("d", "Removing current data in location: %s", target_path) + shutil.rmtree(target_path) + + Logger.log("d", "Extracting backup to location: %s", target_path) + archive.extractall(target_path) + + return True diff --git a/cura/Backups/BackupsManager.py b/cura/Backups/BackupsManager.py index bb52ad57ba..1f8c706eee 100644 --- a/cura/Backups/BackupsManager.py +++ b/cura/Backups/BackupsManager.py @@ -4,6 +4,7 @@ from typing import Optional from UM.Logger import Logger from cura.Backups.Backup import Backup +from cura.CuraApplication import CuraApplication class BackupsManager: @@ -40,7 +41,11 @@ class BackupsManager: self._disableAutoSave() backup = Backup(zip_file = zip_file, meta_data = meta_data) - backup.restore() # At this point, Cura will need to restart for the changes to take effect + restored = backup.restore() + if restored: + # At this point, Cura will need to restart for the changes to take effect. + # We don't want to store the data at this point as that would override the just-restored backup. + CuraApplication.getInstance().windowClosed(safe_data=False) def _disableAutoSave(self): """Here we try to disable the auto-save plugin as it might interfere with restoring a backup.""" diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c8adfbc93b..d26d2ae3f3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -497,10 +497,10 @@ class CuraApplication(QtApplication): ## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently. # # Note that the AutoSave plugin also calls this method. - def saveSettings(self): - if not self.started: # Do not do saving during application start + def saveSettings(self, safe_data: bool = True): + if not self.started or not safe_data: + # Do not do saving during application start or when data should not be safed on quit. return - ContainerRegistry.getInstance().saveDirtyContainers() def saveStack(self, stack): diff --git a/resources/packages.json b/resources/packages.json index c299102498..8d58f226b0 100644 --- a/resources/packages.json +++ b/resources/packages.json @@ -1025,6 +1025,25 @@ } } }, + "UltimakerPC": { + "package_info": { + "package_id": "UltimakerPC", + "package_type": "material", + "display_name": "Ultimaker PC", + "description": "Example package for material and quality profiles for Ultimaker materials.", + "package_version": "1.0.0", + "cura_version": 4, + "website": "https://ultimaker.com/products/materials/pc", + "author": { + "author_id": "Ultimaker", + "display_name": "Ultimaker B.V.", + "email": "materials@ultimaker.com", + "website": "https://ultimaker.com", + "description": "Professional 3D printing made accessible.", + "support_website": "https://ultimaker.com/en/resources/troubleshooting/materials" + } + } + }, "UltimakerPLA": { "package_info": { "package_id": "UltimakerPLA",