V13: Allow external login custom views to see where they are being shown (#15251)
* fix: make sure isTimedOut state is forwarded to the login screen * fix: make umb-custom-view work without attributeChangedCallback * style subheadline * add support for userViewState on external login custom views * fix: subheadline should be a span * check for isTimedOut on internal login screen * reformat code * add args to external login provider custom view * send args to custom view * set state to "logout" when clicking the logout button * force user to new login page /umbraco/login?logout=true if logged out
This commit is contained in:
@@ -113,6 +113,7 @@
|
||||
<umb-editors ng-show="infiniteMode"></umb-editors>
|
||||
|
||||
<umb-login ng-if="login.show"
|
||||
is-timed-out="login.isTimedOut"
|
||||
on-login="hideLoginScreen()">
|
||||
</umb-login>
|
||||
</umb-backoffice-icon-registry>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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: "<div ng-include='templateUrl'></div>",
|
||||
//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('');
|
||||
});
|
||||
|
||||
@@ -36,7 +36,17 @@
|
||||
<div class="flex flex-column flx-gap-sm">
|
||||
<div ng-repeat="login in vm.externalLoginProviders">
|
||||
|
||||
<umb-custom-view ng-if="login.customView" custom-view="{{ ::login.customView }}"></umb-custom-view>
|
||||
<umb-custom-view
|
||||
ng-if="login.customView"
|
||||
ng-attr-custom-view="{{ ::login.customView }}"
|
||||
ng-attr-args='{{"{" +
|
||||
"\"providerName\": \"" + login.authType + "\"," +
|
||||
"\"displayName\": \"" + login.caption + "\"," +
|
||||
"\"externalLoginUrl\": \"" + vm.externalLinkLoginFormAction + "\"," +
|
||||
"\"userViewState\": \"loggedIn\"" +
|
||||
"}"
|
||||
| safe_html }}'>
|
||||
</umb-custom-view>
|
||||
|
||||
<div ng-if="!login.customView">
|
||||
<form action="{{ ::vm.externalLinkLoginFormAction }}" id="oauthloginform-{{ ::login.authType }}" method="POST"
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
return-url=""
|
||||
ng-on-umb-login-success="vm.onLoginSuccess($event)"
|
||||
>
|
||||
<p ng-if="vm.isTimedOut" slot="subheadline">
|
||||
<localize key="login_timeout">Log in below</localize>.
|
||||
</p>
|
||||
<span ng-if="vm.isTimedOut" slot="subheadline">
|
||||
<umb-localize key="login_timeout">Log in below</umb-localize>.
|
||||
</span>
|
||||
<umb-external-login-provider
|
||||
ng-repeat="login in vm.externalLoginProviders"
|
||||
slot="external"
|
||||
@@ -23,6 +23,7 @@
|
||||
external-login-url="{{ ::vm.externalLoginFormAction }}"
|
||||
ng-attr-button-look="{{ ::login.options.buttonLook }}"
|
||||
ng-attr-button-color="{{ ::login.options.buttonColor }}"
|
||||
ng-attr-user-view-state="{{ ::vm.isTimedOut ? 'timedOut' : 'loggedOut' }}"
|
||||
></umb-external-login-provider>
|
||||
</umb-auth>
|
||||
</div>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -95,10 +95,12 @@ export default class UmbLoginPageElement extends LitElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h1 id="greeting">
|
||||
<umb-localize key="general_welcome">Welcome</umb-localize>
|
||||
</h1>
|
||||
<slot name="subheadline"></slot>
|
||||
<header id="header">
|
||||
<h1 id="greeting">
|
||||
<umb-localize key="general_welcome">Welcome</umb-localize>
|
||||
</h1>
|
||||
<slot name="subheadline"></slot>
|
||||
</header>
|
||||
${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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ template.innerHTML = `
|
||||
<div>
|
||||
<uui-button id="button" look="primary" label="My custom button">
|
||||
<uui-icon name="favorite"></uui-icon>
|
||||
My Custom button (<span id="providerName"></span>)
|
||||
My Custom button (<span id="providerName"></span> / <span id="userViewState"></span>)
|
||||
</uui-button>
|
||||
</div>
|
||||
`;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user