diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml index 7e66a42d39..8265159ffa 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml @@ -113,6 +113,7 @@ diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index 83f49121cb..71141106bf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -156,7 +156,9 @@ angular.module('umbraco.services') lastServerTimeoutSet = null; currentUser = null; - openLoginDialog(isLogout === undefined ? true : !isLogout); + if (!isLogout) { + openLoginDialog(true); + } } // Register a handler for when an item is added to the retry queue @@ -231,15 +233,11 @@ angular.module('umbraco.services') return authResource.performLogout() .then(function (data) { - userAuthExpired(); + userAuthExpired(true); - if (data && data.signOutRedirectUrl) { - $window.location.replace(data.signOutRedirectUrl); - } - else { - //done! - return null; - } + const signOutRedirectUrl = data && data.signOutRedirectUrl ? data.signOutRedirectUrl : 'login?logout=true'; + + $window.location.replace(signOutRedirectUrl); }); }, diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js index 582f462814..08d4322432 100644 --- a/src/Umbraco.Web.UI.Client/src/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/main.controller.js @@ -83,7 +83,7 @@ function MainController($scope, $location, appState, treeService, notificationsS evts.push(eventsService.on("app.notAuthenticated", function (evt, data) { $scope.authenticated = null; $scope.user = null; - const isTimedOut = data && data.isTimedOut ? true : false; + const isTimedOut = !!(data && data.isTimedOut); $scope.showLoginScreen(isTimedOut); diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 7e65346d1b..c86c4ababc 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -1,5 +1,5 @@ window.app.config(function ($routeProvider) { - + /** * This determines if the route can continue depending on authentication and initialization requirements * @param {boolean} authRequired If true, it checks if the user is authenticated and will resolve successfully @@ -82,9 +82,6 @@ window.app.config(function ($routeProvider) { return { isLoggedOut: function ($q, $location, userService) { return userService.logout().then(function () { - // we have to redirect here instead of the routes redirectTo - // https://github.com/angular/angular.js/commit/7f4b356c2bebb87f0c26b57a20415b004b20bcd1 - $location.path("/login/false"); //success so continue return $q.when(true); }, function() { @@ -117,9 +114,9 @@ window.app.config(function ($routeProvider) { template: "
", //This controller will execute for this route, then we can execute some code in order to set the template Url controller: function ($scope, $route, $routeParams, $location, sectionService) { - + //We are going to check the currently loaded sections for the user and if the section we are navigating - //to has a custom route path we'll use that + //to has a custom route path we'll use that sectionService.getSectionsForUser().then(function(sections) { //find the one we're requesting var found = _.find(sections, function(s) { @@ -199,7 +196,7 @@ window.app.config(function ($routeProvider) { }) .otherwise({ redirectTo: '/login' }); }).config(function ($locationProvider) { - + $locationProvider.html5Mode(false); //turn html5 mode off $locationProvider.hashPrefix(''); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html index 4a364d374c..3e0cb02e4f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html @@ -36,7 +36,17 @@
- + +
-

- Log in below. -

+ + Log in below. +
diff --git a/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts b/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts index 6f0570a91b..a577bbbb0d 100644 --- a/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/external-login-provider.element.ts @@ -6,10 +6,13 @@ import { until } from 'lit/directives/until.js'; import { loadCustomView, renderCustomView } from '../utils/load-custom-view.function.js'; import { umbLocalizationContext } from '../external/localization/localization-context.js'; +type UserViewState = 'loggingIn' | 'loggedIn' | 'loggedOut' | 'timedOut'; + type ExternalLoginCustomViewElement = HTMLElement & { - displayName: string; - providerName: string; - externalLoginUrl: string; + displayName?: string; + providerName?: string; + externalLoginUrl?: string; + userViewState?: UserViewState; }; /** @@ -50,6 +53,17 @@ export class UmbExternalLoginProviderElement extends LitElement { @property({attribute: 'provider-name'}) providerName = ''; + /** + * Gets or sets the view state of the user. This indicates in which state the user is in the login process, + * which can be used to determine where the external-login-provider is being shown. + * + * @attr user-view-state + * @example loggingIn + * @default loggingIn + */ + @property({attribute: 'user-view-state'}) + userViewState: UserViewState = 'loggingIn'; + /** * Gets or sets the url to the external login provider. * @@ -68,7 +82,6 @@ export class UmbExternalLoginProviderElement extends LitElement { return this.#externalLoginUrl; } - /** * Gets or sets the icon to display next to the provider name. * This should be the name of an icon in the Umbraco Backoffice icon set. @@ -104,6 +117,17 @@ export class UmbExternalLoginProviderElement extends LitElement { #externalLoginUrl = ''; + constructor() { + super(); + + const searchParams = new URLSearchParams(window.location.search); + const isLogout = searchParams.get('logout') === 'true'; + + if (isLogout) { + this.userViewState = 'loggedOut'; + } + } + protected render() { return this.customView ? until(this.renderCustomView(), html` @@ -146,6 +170,7 @@ export class UmbExternalLoginProviderElement extends LitElement { customView.displayName = this.displayName; customView.providerName = this.providerName; customView.externalLoginUrl = this.externalLoginUrl; + customView.userViewState = this.userViewState; } return renderCustomView(customView); diff --git a/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts b/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts index 460b867896..565ea08a98 100644 --- a/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts +++ b/src/Umbraco.Web.UI.Login/src/components/pages/login.page.element.ts @@ -95,10 +95,12 @@ export default class UmbLoginPageElement extends LitElement { render() { return html` -

- Welcome -

- + ${this.disableLocalLogin ? nothing : html` @@ -158,6 +160,18 @@ export default class UmbLoginPageElement extends LitElement { flex-direction: column; } + #header { + text-align: center; + display: flex; + flex-direction: column; + gap: var(--uui-size-space-5); + } + + #header span { + color: var(--uui-color-text-alt); /* TODO Change to uui color when uui gets a muted text variable */ + font-size: 14px; + } + #greeting { color: var(--uui-color-interactive); text-align: center; diff --git a/src/Umbraco.Web.UI.Login/src/external/custom-view.element.ts b/src/Umbraco.Web.UI.Login/src/external/custom-view.element.ts index 0bc3ba8719..693b024321 100644 --- a/src/Umbraco.Web.UI.Login/src/external/custom-view.element.ts +++ b/src/Umbraco.Web.UI.Login/src/external/custom-view.element.ts @@ -6,30 +6,32 @@ import {loadCustomView, renderCustomView} from "../utils/load-custom-view.functi @customElement('umb-custom-view') export default class UmbCustomViewElement extends LitElement { @property({ attribute: 'custom-view' }) - customView?: string; + set customView (value: string) { + this.#customView = value; + this.#loadView(); + } - @property({ attribute: 'args' }) - args?: any; + @property({ type: Object, attribute: 'args'}) + set args (value: any) { + this.#args = value; + this.#loadView(); + } @state() protected component: any = null; - attributeChangedCallback(name: string, _old: string | null, value: string | null) { - super.attributeChangedCallback(name, _old, value); - if (name === 'custom-view') { - this.#loadView(); - } - } + #args?: any; + #customView?: string; async #loadView() { - if (!this.customView || !this.customView.endsWith('.js') && !this.customView.endsWith('.html')) { + if (!this.#customView || !this.#customView.endsWith('.js') && !this.#customView.endsWith('.html')) { return; } - const customView = await loadCustomView(this.customView); + const customView = await loadCustomView(this.#customView); - if (this.args) { - Object.entries(this.args).forEach(([key, value]) => { + if (this.#args) { + Object.entries(this.#args).forEach(([key, value]) => { (customView as any)[key] = value; }); } diff --git a/src/Umbraco.Web.UI.Login/src/mocks/customViews/my-custom-view.js b/src/Umbraco.Web.UI.Login/src/mocks/customViews/my-custom-view.js index f60df70033..c53603f126 100644 --- a/src/Umbraco.Web.UI.Login/src/mocks/customViews/my-custom-view.js +++ b/src/Umbraco.Web.UI.Login/src/mocks/customViews/my-custom-view.js @@ -8,7 +8,7 @@ template.innerHTML = `
- My Custom button () + My Custom button ( / )
`; @@ -16,6 +16,7 @@ template.innerHTML = ` export class MyCustomView extends HTMLElement { providerName = ''; displayName = ''; + userViewState = ''; constructor() { super(); this.attachShadow({ mode: 'open' }); @@ -29,6 +30,7 @@ export class MyCustomView extends HTMLElement { connectedCallback() { console.log('My custom view connected'); this.shadowRoot.getElementById('providerName').innerText = this.providerName; + this.shadowRoot.getElementById('userViewState').innerText = this.userViewState; } }