Browse Source

Merge branch 'Ultimaker:master' into master

x40-Community 3 years ago
parent
commit
0c762dcd81

+ 1 - 1
com.ultimaker.cura.appdata.xml

@@ -28,6 +28,6 @@
       <image>https://raw.githubusercontent.com/Ultimaker/Cura/master/screenshot.png</image>
     </screenshot>
   </screenshots>
-  <url type="homepage">https://ultimaker.com/en/products/cura-software?utm_source=cura&amp;utm_medium=software&amp;utm_campaign=resources</url>
+  <url type="homepage">https://ultimaker.com/software/ultimaker-cura?utm_source=cura&amp;utm_medium=software&amp;utm_campaign=cura-update-linux</url>
   <translation type="gettext">Cura</translation>
 </component>

+ 1 - 1
cura/CuraActions.py

@@ -35,7 +35,7 @@ class CuraActions(QObject):
         # Starting a web browser from a signal handler connected to a menu will crash on windows.
         # So instead, defer the call to the next run of the event loop, since that does work.
         # Note that weirdly enough, only signal handlers that open a web browser fail like that.
-        event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/resources/manuals/software")], {})
+        event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/resources/manuals/software?utm_source=cura&utm_medium=software&utm_campaign=dropdown-documentation")], {})
         cura.CuraApplication.CuraApplication.getInstance().functionEvent(event)
 
     @pyqtSlot()

+ 2 - 1
cura/CuraApplication.py

@@ -161,7 +161,7 @@ class CuraApplication(QtApplication):
 
         self.default_theme = "cura-light"
 
-        self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
+        self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features?utm_source=cura&utm_medium=software&utm_campaign=cura-update-features"
 
         self._boot_loading_time = time.time()
 
@@ -471,6 +471,7 @@ class CuraApplication(QtApplication):
                 ("definition_changes", InstanceContainer.Version * 1000000 + self.SettingVersion):              (self.ResourceTypes.DefinitionChangesContainer, "application/x-uranium-instancecontainer"),
                 ("variant", InstanceContainer.Version * 1000000 + self.SettingVersion):                         (self.ResourceTypes.VariantInstanceContainer, "application/x-uranium-instancecontainer"),
                 ("setting_visibility", SettingVisibilityPresetsModel.Version * 1000000 + self.SettingVersion):  (self.ResourceTypes.SettingVisibilityPreset, "application/x-uranium-preferences"),
+                ("machine", 2):                                                                                 (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer")
             }
         )
 

+ 11 - 6
cura/OAuth2/AuthorizationService.py

@@ -24,7 +24,7 @@ if TYPE_CHECKING:
     from cura.OAuth2.Models import UserProfile, OAuth2Settings
     from UM.Preferences import Preferences
 
-MYCLOUD_LOGOFF_URL = "https://mycloud.ultimaker.com/logoff"
+MYCLOUD_LOGOFF_URL = "https://account.ultimaker.com/logoff?utm_source=cura&utm_medium=software&utm_campaign=change-account-before-adding-printers"
 
 class AuthorizationService:
     """The authorization service is responsible for handling the login flow, storing user credentials and providing
@@ -209,25 +209,27 @@ class AuthorizationService:
                                      link to force the a browser logout from mycloud.ultimaker.com
         :return: The authentication URL, properly formatted and encoded
         """
-        auth_url = "{}?{}".format(self._auth_url, urlencode(query_parameters_dict))
+        auth_url = f"{self._auth_url}?{urlencode(query_parameters_dict)}"
         if force_browser_logout:
-            # The url after '?next=' should be urlencoded
-            auth_url = "{}?next={}".format(MYCLOUD_LOGOFF_URL, quote_plus(auth_url))
+            connecting_char = "&" if "?" in MYCLOUD_LOGOFF_URL else "?"
+            # The url after 'next=' should be urlencoded
+            auth_url = f"{MYCLOUD_LOGOFF_URL}{connecting_char}next={quote_plus(auth_url)}"
         return auth_url
 
     def _onAuthStateChanged(self, auth_response: AuthenticationResponse) -> None:
         """Callback method for the authentication flow."""
-
         if auth_response.success:
+            Logger.log("d", "Got callback from Authorization state. The user should now be logged in!")
             self._storeAuthData(auth_response)
             self.onAuthStateChanged.emit(logged_in = True)
         else:
+            Logger.log("d", "Got callback from Authorization state. Something went wrong: [%s]", auth_response.err_message)
             self.onAuthenticationError.emit(logged_in = False, error_message = auth_response.err_message)
         self._server.stop()  # Stop the web server at all times.
 
     def loadAuthDataFromPreferences(self) -> None:
         """Load authentication data from preferences."""
-
+        Logger.log("d", "Attempting to load the auth data from preferences.")
         if self._preferences is None:
             Logger.log("e", "Unable to load authentication data, since no preference has been set!")
             return
@@ -239,6 +241,7 @@ class AuthorizationService:
                 user_profile = self.getUserProfile()
                 if user_profile is not None:
                     self.onAuthStateChanged.emit(logged_in = True)
+                    Logger.log("d", "Auth data was successfully loaded")
                 else:
                     if self._unable_to_get_data_message is not None:
                         self._unable_to_get_data_message.hide()
@@ -247,6 +250,7 @@ class AuthorizationService:
                                                                                   "Unable to reach the Ultimaker account server."),
                                                                title = i18n_catalog.i18nc("@info:title", "Warning"),
                                                                message_type = Message.MessageType.ERROR)
+                    Logger.log("w", "Unable to load auth data from preferences")
                     self._unable_to_get_data_message.show()
         except (ValueError, TypeError):
             Logger.logException("w", "Could not load auth data from preferences")
@@ -264,6 +268,7 @@ class AuthorizationService:
             self._user_profile = self.getUserProfile()
             self._preferences.setValue(self._settings.AUTH_DATA_PREFERENCE_KEY, json.dumps(auth_data.dump()))
         else:
+            Logger.log("d", "Clearing the user profile")
             self._user_profile = None
             self._preferences.resetPreference(self._settings.AUTH_DATA_PREFERENCE_KEY)
 

+ 4 - 0
cura/OAuth2/KeyringAttribute.py

@@ -43,6 +43,10 @@ class KeyringAttribute:
                 self._store_secure = False
                 Logger.log("i", "Access to the keyring was denied.")
                 return getattr(instance, self._name)
+            except UnicodeDecodeError:
+                self._store_secure = False
+                Logger.log("w", "The password retrieved from the keyring cannot be used because it contains characters that cannot be decoded.")
+                return getattr(instance, self._name)
         else:
             return getattr(instance, self._name)
 

+ 2 - 1
plugins/CuraEngineBackend/CuraEngineBackend.py

@@ -159,7 +159,8 @@ class CuraEngineBackend(QObject, Backend):
 
         self._slicing_error_message = Message(
             text = catalog.i18nc("@message", "Slicing failed with an unexpected error. Please consider reporting a bug on our issue tracker."),
-            title = catalog.i18nc("@message:title", "Slicing failed")
+            title = catalog.i18nc("@message:title", "Slicing failed"),
+            message_type = Message.MessageType.ERROR
         )
         self._slicing_error_message.addAction(
             action_id = "report_bug",

+ 21 - 6
plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml

@@ -49,11 +49,27 @@ Item
             id: searchBar
             Layout.fillWidth: true
             implicitHeight: createNewProjectButton.height
+            leftPadding: searchIcon.width + UM.Theme.getSize("default_margin").width * 2
 
             onTextEdited: manager.projectFilter = text //Update the search filter when editing this text field.
 
-            leftIcon: UM.Theme.getIcon("Magnifier")
             placeholderText: "Search"
+
+            UM.RecolorImage
+            {
+                id: searchIcon
+
+                anchors
+                {
+                    verticalCenter: parent.verticalCenter
+                    left: parent.left
+                    leftMargin: UM.Theme.getSize("default_margin").width
+                }
+                source: UM.Theme.getIcon("search")
+                height: UM.Theme.getSize("small_button_icon").height
+                width: height
+                color: UM.Theme.getColor("text")
+            }
         }
 
         Cura.SecondaryButton
@@ -76,12 +92,11 @@ Item
             id: upgradePlanButton
 
             text: "Upgrade plan"
-            iconSource: UM.Theme.getIcon("LinkExternal")
+            iconSource: UM.Theme.getIcon("external_link")
             visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
-            tooltip: "You have reached the maximum number of projects allowed by your subscription. Please upgrade to the Professional subscription to create more projects."
-            tooltipWidth: parent.width * 0.5
+            tooltip: "Maximum number of projects reached. Please upgrade your subscription to create more projects."
 
-            onClicked: Qt.openUrlExternally("https://ultimaker.com/software/ultimaker-essentials/sign-up-cura?utm_source=cura&utm_medium=software&utm_campaign=lib-max")
+            onClicked: Qt.openUrlExternally("https://ultimaker.com/software/enterprise-software?utm_source=cura&utm_medium=software&utm_campaign=MaxProjLink")
         }
     }
 
@@ -124,7 +139,7 @@ Item
                 id: visitDigitalLibraryButton
                 anchors.horizontalCenter: parent.horizontalCenter
                 text: "Visit Digital Library"
-                onClicked:  Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
+                onClicked:  Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library?utm_source=cura&utm_medium=software&utm_campaign=empty-library")
                 visible: searchBar.text === "" //Show the link to Digital Library when there are no projects in the user's Library.
             }
         }

+ 15 - 0
plugins/DigitalLibrary/src/BackwardsCompatibleMessage.py

@@ -0,0 +1,15 @@
+# Copyright (c) 2021 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from cura.CuraApplication import CuraApplication
+from UM.Message import Message
+from UM.Version import Version
+
+def getBackwardsCompatibleMessage(text: str, title: str, lifetime: int, message_type_str: str) -> Message:
+    if CuraApplication.getInstance().getAPIVersion() < Version("7.7.0"):
+        return Message(text=text, title=title, lifetime=lifetime)
+    else:
+        message_type = Message.MessageType.NEUTRAL
+        if ("MessageType." + message_type_str) in [str(item) for item in Message.MessageType]:
+            message_type = Message.MessageType[message_type_str]
+        return Message(text=text, title=title, lifetime=lifetime, message_type=message_type)

+ 20 - 19
plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py

@@ -14,6 +14,7 @@ from UM.Logger import Logger
 from UM.Message import Message
 from UM.Scene.SceneNode import SceneNode
 from cura.CuraApplication import CuraApplication
+from .BackwardsCompatibleMessage import getBackwardsCompatibleMessage
 from .DFLibraryFileUploadRequest import DFLibraryFileUploadRequest
 from .DFLibraryFileUploadResponse import DFLibraryFileUploadResponse
 from .DFPrintJobUploadRequest import DFPrintJobUploadRequest
@@ -69,11 +70,11 @@ class DFFileExportAndUploadManager:
                 use_inactivity_timer = False
         )
 
-        self._generic_success_message = Message(
+        self._generic_success_message = getBackwardsCompatibleMessage(
                 text = "Your {} uploaded to '{}'.".format("file was" if len(self._file_upload_job_metadata) <= 1 else "files were", self._library_project_name),
                 title = "Upload successful",
                 lifetime = 0,
-                message_type = Message.MessageType.POSITIVE
+                message_type_str = "POSITIVE"
         )
         self._generic_success_message.addAction(
                 "open_df_project",
@@ -217,11 +218,11 @@ class DFFileExportAndUploadManager:
             # Set the progress to 100% when the upload job fails, to avoid having the progress message stuck
             self._file_upload_job_metadata[filename]["upload_status"] = "failed"
             self._file_upload_job_metadata[filename]["upload_progress"] = 100
-            self._file_upload_job_metadata[filename]["file_upload_failed_message"] = Message(
+            self._file_upload_job_metadata[filename]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
                     text = "Failed to export the file '{}'. The upload process is aborted.".format(filename),
                     title = "Export error",
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR
+                    message_type_str = "ERROR"
             )
         self._on_upload_error()
         self._onFileUploadFinished(filename)
@@ -240,11 +241,11 @@ class DFFileExportAndUploadManager:
             self._file_upload_job_metadata[filename_3mf]["upload_progress"] = 100
 
             human_readable_error = self.extractErrorTitle(reply_string)
-            self._file_upload_job_metadata[filename_3mf]["file_upload_failed_message"] = Message(
+            self._file_upload_job_metadata[filename_3mf]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
                     text = "Failed to upload the file '{}' to '{}'. {}".format(filename_3mf, self._library_project_name, human_readable_error),
                     title = "File upload error",
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR
+                    message_type_str = "ERROR"
             )
         self._on_upload_error()
         self._onFileUploadFinished(filename_3mf)
@@ -263,11 +264,11 @@ class DFFileExportAndUploadManager:
             self._file_upload_job_metadata[filename_ufp]["upload_progress"] = 100
 
             human_readable_error = self.extractErrorTitle(reply_string)
-            self._file_upload_job_metadata[filename_ufp]["file_upload_failed_message"] = Message(
+            self._file_upload_job_metadata[filename_ufp]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
                     title = "File upload error",
                     text = "Failed to upload the file '{}' to '{}'. {}".format(filename_ufp, self._library_project_name, human_readable_error),
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR
+                    message_type_str = "ERROR"
             )
         self._on_upload_error()
         self._onFileUploadFinished(filename_ufp)
@@ -300,11 +301,11 @@ class DFFileExportAndUploadManager:
             self._file_upload_job_metadata[filename]["upload_status"] = "failed"
             self._file_upload_job_metadata[filename]["upload_progress"] = 100
             human_readable_error = self.extractErrorTitle(reply_string)
-            self._file_upload_job_metadata[filename]["file_upload_failed_message"] = Message(
+            self._file_upload_job_metadata[filename]["file_upload_failed_message"] = getBackwardsCompatibleMessage(
                     title = "File upload error",
                     text = "Failed to upload the file '{}' to '{}'. {}".format(self._file_name, self._library_project_name, human_readable_error),
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR
+                    message_type_str = "ERROR"
             )
 
         self._on_upload_error()
@@ -319,7 +320,7 @@ class DFFileExportAndUploadManager:
 
     def _onMessageActionTriggered(self, message, action):
         if action == "open_df_project":
-            project_url = "{}/app/library/project/{}?wait_for_new_files=true".format(CuraApplication.getInstance().ultimakerDigitalFactoryUrl, self._library_project_id)
+            project_url = "{}/app/library/project/{}?wait_for_new_files=true&utm_source=cura&utm_medium=software&utm_campaign=saved-library-file-message".format(CuraApplication.getInstance().ultimakerDigitalFactoryUrl, self._library_project_id)
             QDesktopServices.openUrl(QUrl(project_url))
             message.hide()
 
@@ -337,17 +338,17 @@ class DFFileExportAndUploadManager:
                 "upload_progress"     : -1,
                 "upload_status"       : "",
                 "file_upload_response": None,
-                "file_upload_success_message": Message(
+                "file_upload_success_message": getBackwardsCompatibleMessage(
                     text = "'{}' was uploaded to '{}'.".format(filename_3mf, self._library_project_name),
                     title = "Upload successful",
                     lifetime = 0,
-                    message_type = Message.MessageType.POSITIVE
+                    message_type_str = "POSITIVE"
                 ),
-                "file_upload_failed_message": Message(
+                "file_upload_failed_message": getBackwardsCompatibleMessage(
                     text = "Failed to upload the file '{}' to '{}'.".format(filename_3mf, self._library_project_name),
                     title = "File upload error",
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR
+                    message_type_str = "ERROR"
                 )
             }
             job_3mf = ExportFileJob(self._file_handlers["3mf"], self._nodes, self._file_name, "3mf")
@@ -361,17 +362,17 @@ class DFFileExportAndUploadManager:
                 "upload_progress"     : -1,
                 "upload_status"       : "",
                 "file_upload_response": None,
-                "file_upload_success_message": Message(
+                "file_upload_success_message": getBackwardsCompatibleMessage(
                     text = "'{}' was uploaded to '{}'.".format(filename_ufp, self._library_project_name),
                     title = "Upload successful",
                     lifetime = 0,
-                    message_type = Message.MessageType.POSITIVE
+                    message_type_str = "POSITIVE"
                 ),
-                "file_upload_failed_message": Message(
+                "file_upload_failed_message": getBackwardsCompatibleMessage(
                     text = "Failed to upload the file '{}' to '{}'.".format(filename_ufp, self._library_project_name),
                     title = "File upload error",
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR
+                    message_type_str = "ERROR"
                 )
             }
             job_ufp = ExportFileJob(self._file_handlers["ufp"], self._nodes, self._file_name, "ufp")

+ 7 - 6
plugins/DigitalLibrary/src/DigitalFactoryController.py

@@ -23,6 +23,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager
 from cura.API import Account
 from cura.CuraApplication import CuraApplication
 from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
+from .BackwardsCompatibleMessage import getBackwardsCompatibleMessage
 from .DFFileExportAndUploadManager import DFFileExportAndUploadManager
 from .DigitalFactoryApiClient import DigitalFactoryApiClient
 from .DigitalFactoryFileModel import DigitalFactoryFileModel
@@ -527,11 +528,11 @@ class DigitalFactoryController(QObject):
             except IOError as ex:
                 Logger.logException("e", "Can't write Digital Library file {0}/{1} download to temp-directory {2}.",
                                     ex, project_name, file_name, temp_dir)
-                Message(
+                getBackwardsCompatibleMessage(
                         text = "Failed to write to temporary file for '{}'.".format(file_name),
                         title = "File-system error",
                         lifetime = 10,
-                        message_type=Message.MessageType.ERROR
+                        message_type_str="ERROR"
                 ).show()
                 return
 
@@ -542,11 +543,11 @@ class DigitalFactoryController(QObject):
                           f = file_name) -> None:
             progress_message.hide()
             Logger.error("An error {0} {1} occurred while downloading {2}/{3}".format(str(error), str(reply), p, f))
-            Message(
+            getBackwardsCompatibleMessage(
                     text = "Failed Digital Library download for '{}'.".format(f),
                     title = "Network error {}".format(error),
                     lifetime = 10,
-                    message_type=Message.MessageType.ERROR
+                    message_type_str="ERROR"
             ).show()
 
         download_manager = HttpRequestManager.getInstance()
@@ -591,10 +592,10 @@ class DigitalFactoryController(QObject):
 
         if filename == "":
             Logger.log("w", "The file name cannot be empty.")
-            Message(text = "Cannot upload file with an empty name to the Digital Library",
+            getBackwardsCompatibleMessage(text = "Cannot upload file with an empty name to the Digital Library",
                     title = "Empty file name provided",
                     lifetime = 0,
-                    message_type = Message.MessageType.ERROR).show()
+                    message_type_str = "ERROR").show()
             return
 
         self._saveFileToSelectedProjectHelper(filename, formats)

Some files were not shown because too many files changed in this diff