diff --git a/cura/API/Account.py b/cura/API/Account.py index 7273479de4..acf507e23f 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -96,6 +96,18 @@ class Account(QObject): return self._authorization_service.startAuthorizationFlow() + @pyqtSlot() + def loginWithForcedLogout(self) -> None: + """ + Forces a logout from Cura and then initiates the authorization flow with the force_logout_from_mycloud variable + as true, to sync the accounts in Cura and in the browser. + + :return: None + """ + if self._logged_in: + self.logout() + self._authorization_service.startAuthorizationFlow(True) + @pyqtProperty(str, notify=loginStateChanged) def userName(self): user_profile = self._authorization_service.getUserProfile() diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 2f865456b6..ee1cdff19d 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -4,7 +4,7 @@ import json from datetime import datetime, timedelta from typing import Optional, TYPE_CHECKING -from urllib.parse import urlencode +from urllib.parse import urlencode, quote_plus import requests.exceptions from PyQt5.QtCore import QUrl @@ -142,7 +142,7 @@ class AuthorizationService: self.onAuthStateChanged.emit(logged_in = False) ## Start the flow to become authenticated. This will start a new webbrowser tap, prompting the user to login. - def startAuthorizationFlow(self) -> None: + def startAuthorizationFlow(self, force_logout_from_mycloud = False) -> None: Logger.log("d", "Starting new OAuth2 flow...") # Create the tokens needed for the code challenge (PKCE) extension for OAuth2. @@ -173,8 +173,17 @@ class AuthorizationService: title=i18n_catalog.i18nc("@info:title", "Warning")).show() return - # Open the authorization page in a new browser window. - QDesktopServices.openUrl(QUrl("{}?{}".format(self._auth_url, query_string))) + # Open the authorization page in a new browser window. If a force logout is requested during the authorization + # flow, the "mycloud logoff" link will be prepended to the authorization url to make sure that the user will be + # logged off from the browser before being redirected to login again. This case is used to sync the accounts + # between Cura and the browser. + auth_url = "{}?{}".format(self._auth_url, query_string) + if force_logout_from_mycloud: + mycloud_logoff_link = "https://mycloud.ultimaker.com/logoff" + logoff_auth_url = "{}?next={}".format(mycloud_logoff_link, quote_plus(auth_url)) + QDesktopServices.openUrl(QUrl(logoff_auth_url)) + else: + QDesktopServices.openUrl(QUrl(auth_url)) ## Callback method for the authentication flow. diff --git a/resources/qml/WelcomePages/AddCloudPrintersView.qml b/resources/qml/WelcomePages/AddCloudPrintersView.qml index f97d68f776..511328b0c8 100644 --- a/resources/qml/WelcomePages/AddCloudPrintersView.qml +++ b/resources/qml/WelcomePages/AddCloudPrintersView.qml @@ -51,11 +51,11 @@ Item } // Component that contains a busy indicator and a message, while it waits for Cura to discover a cloud printer - Rectangle + Item { id: waitingContent width: parent.width - height: waitingIndicator.height + waitingLabel.height + height: childrenRect.height anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter BusyIndicator @@ -74,6 +74,37 @@ Item font: UM.Theme.getFont("large") renderType: Text.NativeRendering } + Label + { + id: noPrintersFoundLabel + anchors.top: waitingLabel.bottom + anchors.topMargin: 2 * UM.Theme.getSize("wide_margin").height + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + text: catalog.i18nc("@label", "No printers found in your account?") + font: UM.Theme.getFont("medium") + } + Label + { + text: "Sign in with a different account" + anchors.top: noPrintersFoundLabel.bottom + anchors.horizontalCenter: parent.horizontalCenter + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_link") + MouseArea { + anchors.fill: parent; + onClicked: Cura.API.account.loginWithForcedLogout() + hoverEnabled: true + onEntered: + { + parent.font.underline = true + } + onExited: + { + parent.font.underline = false + } + } + } visible: discoveredCloudPrintersModel.count == 0 }