Explicit endpoints returning the Login and BackOffice logos and background (#17696)

* Created explicit endpoints returning the login image instead of leaking the configuration. Thereby some hardcoded values have been changed, but the url will now be the same every time.

* Remove magic concatenation for action lookup

* remove unused backoffice asset (login.jpg)

* remove unused umbraco logo assets

* add manifest handlers

* add mock handlers for the `security/back-office/graphics` endpoints to the backoffice

* add mock handlers for the `security/back-office/graphics` endpoints to the login screen

* chore: update msw service worker

* feat: make static assets available for consumption without copying them from the login project

* update consts with new location for static assets

* feat: prefix login assets with `login-`

* remove unused asset `logo.png`

* feat: introduce a `/back-office/graphics/logo` endpoint to serve the logo "mark" used throughout all applications

* feat: use the alternative logo for disabled javascript warning

* feat: use the umbraco logo on the NoNodes.cshtml page

* Do not expose the new readme in the package

* feat: add logo.svg

* feat: add `umb-app-logo` element to display the backoffice logo including server url and appropriate tags

* feat: use the new `umb-app-logo` element relevant places and make sure to add the serverUrl in front of other graphics

* feat: move logic to connectedCallback to prevent error from non-existing element

* revert usage of HideBackOfficeLogo

* feat: add alt text to logos

* feat: add obsolete message and a hint to use BackOfficeLogo insted

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
This commit is contained in:
Bjarke Berg
2024-12-05 12:21:33 +01:00
committed by GitHub
parent db7c78b899
commit 88f97261ad
29 changed files with 366 additions and 82 deletions

View File

@@ -0,0 +1,74 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
namespace Umbraco.Cms.Api.Management.Controllers.Security;
[ApiVersion("1.0")]
[VersionedApiBackOfficeRoute(Common.Security.Paths.BackOfficeApi.EndpointTemplate + "/graphics")]
[ApiExplorerSettings(IgnoreApi = true)]
public class BackOfficeGraphicsController : Controller
{
public const string LogoRouteName = nameof(BackOfficeGraphicsController) + "." + nameof(Logo);
public const string LoginBackGroundRouteName = nameof(BackOfficeGraphicsController) + "." + nameof(LoginBackground);
public const string LoginLogoRouteName = nameof(BackOfficeGraphicsController) + "." + nameof(LoginLogo);
public const string LoginLogoAlternativeRouteName = nameof(BackOfficeGraphicsController) + "." + nameof(LoginLogoAlternative);
private readonly IOptions<ContentSettings> _contentSettings;
private readonly IContentTypeProvider _contentTypeProvider;
private readonly IWebHostEnvironment _webHostEnvironment;
public BackOfficeGraphicsController(IOptions<ContentSettings> contentSettings, IOptions<StaticFileOptions> staticFileOptions, IWebHostEnvironment webHostEnvironment)
{
_contentSettings = contentSettings;
_webHostEnvironment = webHostEnvironment;
_contentTypeProvider = staticFileOptions.Value.ContentTypeProvider ?? new FileExtensionContentTypeProvider();
}
[HttpGet("login-background", Name = LoginBackGroundRouteName)]
[AllowAnonymous]
[MapToApiVersion("1.0")]
public IActionResult LoginBackground() => HandleFileRequest(_contentSettings.Value.LoginBackgroundImage);
[HttpGet("logo", Name = LogoRouteName)]
[AllowAnonymous]
[MapToApiVersion("1.0")]
public IActionResult Logo() => HandleFileRequest(_contentSettings.Value.BackOfficeLogo);
[HttpGet("login-logo", Name = LoginLogoRouteName)]
[AllowAnonymous]
[MapToApiVersion("1.0")]
public IActionResult LoginLogo() => HandleFileRequest(_contentSettings.Value.LoginLogoImage);
[HttpGet("login-logo-alternative", Name = LoginLogoAlternativeRouteName)]
[AllowAnonymous]
[MapToApiVersion("1.0")]
public IActionResult LoginLogoAlternative() => HandleFileRequest(_contentSettings.Value.LoginLogoImageAlternative);
private IActionResult HandleFileRequest(string virtualPath)
{
var filePath = Path.Combine(Constants.SystemDirectories.Umbraco, virtualPath).TrimStart(Constants.CharArrays.Tilde);
var fileInfo = _webHostEnvironment.WebRootFileProvider.GetFileInfo(filePath);
if (fileInfo.PhysicalPath is null)
{
return NotFound();
}
if (_contentTypeProvider.TryGetContentType(fileInfo.PhysicalPath, out var contentType))
{
Stream fileStream = fileInfo.CreateReadStream();
return File(fileStream, contentType);
}
return StatusCode(StatusCodes.Status412PreconditionFailed);
}
}

View File

@@ -72,6 +72,10 @@
<Content Remove="$(LoginAssetsPath)\**" />
</ItemGroup>
<ItemGroup>
<Content Remove="wwwroot\umbraco\assets\README.md" />
</ItemGroup>
<Target Name="RestoreLogin" Inputs="$(LoginProjectDirectory)package-lock.json" Outputs="$(LoginProjectDirectory)node_modules/.package-lock.json">
<Message Importance="high" Text="Restoring Login NPM packages..." />
<Exec Command="npm ci --no-fund --no-audit --prefer-offline" WorkingDirectory="$(LoginProjectDirectory)" />

View File

@@ -1,4 +1,5 @@
@using Microsoft.Extensions.Options
@using Umbraco.Cms.Api.Management.Controllers.Security
@using Umbraco.Cms.Api.Management.Extensions
@using Umbraco.Cms.Core.Configuration.Models
@using Umbraco.Cms.Core.Logging
@@ -11,13 +12,12 @@
@inject IJsonSerializer JsonSerializer
@inject IProfilerHtml ProfilerHtml
@inject IOptions<GlobalSettings> GlobalSettings
@inject IOptions<ContentSettings> ContentSettings
@{
bool.TryParse(Context.Request.Query["umbDebug"], out var isDebug);
var backOfficePath = BackOfficePathGenerator.BackOfficePath;
var backOfficeAssetsPath = BackOfficePathGenerator.BackOfficeAssetsPath;
var loginLogoImage = ContentSettings.Value.LoginLogoImage;
var loginLogoImageAlternative = Url.RouteUrl(BackOfficeGraphicsController.LoginLogoAlternativeRouteName, new {Version= "1"});
}
<!DOCTYPE html>
@@ -54,8 +54,8 @@
}
</style>
<div id="noscript-container">
<h1 class="uui-h3" style="display: inline-flex; align-items: center; gap: 10px">
<img aria-hidden="true" alt="logo" src="@loginLogoImage" style="width: 100%" />
<h1 aria-hidden="true" class="uui-h3" style="display: inline-flex; align-items: center; gap: 10px">
<img alt="logo" src="@loginLogoImageAlternative" style="width: 100%" />
</h1>
<p>For full functionality of Umbraco CMS it is necessary to enable JavaScript.</p>
<p>Here are the <a href="https://www.enable-javascript.com/" target="_blank" rel="noopener" style="text-decoration: underline;">instructions how to enable JavaScript in your web browser</a>.</p>

View File

@@ -1,5 +1,6 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.Extensions.Options;
@using Umbraco.Cms.Api.Management.Controllers.Security
@using Umbraco.Cms.Api.Management.Extensions
@using Umbraco.Cms.Api.Management.Security
@using Umbraco.Cms.Core.Configuration.Models
@@ -10,7 +11,6 @@
@using Umbraco.Cms.Core.Serialization
@using Umbraco.Cms.Web.Common.Hosting
@using Umbraco.Extensions
@inject IOptions<ContentSettings> ContentSettings
@inject IOptions<SecuritySettings> SecuritySettings
@inject IEmailSender EmailSender
@inject IHostingEnvironment HostingEnvironment
@@ -23,9 +23,9 @@
@{
bool.TryParse(Context.Request.Query["umbDebug"], out var isDebug);
var backOfficePath = GlobalSettings.Value.GetBackOfficePath(HostingEnvironment);
var loginLogoImage = ContentSettings.Value.LoginLogoImage;
var loginLogoImageAlternative = ContentSettings.Value.LoginLogoImageAlternative;
var loginBackgroundImage = ContentSettings.Value.LoginBackgroundImage;
var loginLogoImage = Url.RouteUrl(BackOfficeGraphicsController.LoginLogoRouteName, new {Version= "1"});
var loginLogoImageAlternative = Url.RouteUrl(BackOfficeGraphicsController.LoginLogoAlternativeRouteName, new {Version= "1"});
var loginBackgroundImage = Url.RouteUrl(BackOfficeGraphicsController.LoginBackGroundRouteName, new {Version= "1"});
var usernameIsEmail = SecuritySettings.Value.UsernameIsEmail;
var allowUserInvite = EmailSender.CanSendRequiredEmail();
var allowPasswordReset = SecuritySettings.Value.AllowPasswordReset && EmailSender.CanSendRequiredEmail();
@@ -73,8 +73,8 @@
}
</style>
<div id="noscript-container">
<h1 class="uui-h3" style="display: inline-flex; align-items: center; gap: 10px">
<img aria-hidden="true" alt="logo" src="@loginLogoImage" style="width: 100%" />
<h1 aria-hidden="true" class="uui-h3" style="display: inline-flex; align-items: center; gap: 10px">
<img alt="logo" src="@loginLogoImageAlternative" style="width: 100%" />
</h1>
<p>For full functionality of Umbraco CMS it is necessary to enable JavaScript.</p>
<p>Here are the <a href="https://www.enable-javascript.com/" target="_blank" rel="noopener" style="text-decoration: underline;">instructions how to enable JavaScript in your web browser</a>.</p>

View File

@@ -1,4 +1,5 @@
@using Microsoft.Extensions.Options
@using Umbraco.Cms.Api.Management.Controllers.Security
@using Umbraco.Cms.Core.Configuration.Models
@using Umbraco.Cms.Core.Hosting
@using Umbraco.Cms.Core.Routing
@@ -8,6 +9,7 @@
@inject IOptions<GlobalSettings> globalSettings
@{
var backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment);
var logoImage = Url.RouteUrl(BackOfficeGraphicsController.LogoRouteName, new {Version= "1"});
}
<!doctype html>
<html class="no-js" lang="en">
@@ -26,7 +28,7 @@
<article>
<div>
<div class="logo" aria-hidden="true">
<img alt="Umbraco" src="umbraco/backoffice/assets/umbraco_logomark_white.svg" width="91" height="91" />
<img alt="logo" src="@logoImage" height="91"/>
</div>
<h1>Welcome to your Umbraco installation</h1>

View File

@@ -0,0 +1,16 @@
# Umbraco static assets
The files in this directory are static assets that are used by the Umbraco backoffice, installer, public-facing sites, and login screen. These files are served by the Umbraco backend and are not intended to be used by the front-end of your website.
## Structure
The assets are structured in the following way:
| Name | Description | Usage |
| ---- |--------------------------------------------------------------------|---------------------------------------------------------------------------------|
| `login.jpg` | The background image for the login screen. | /umbraco/management/api/v1/security/back-office/graphics/login-background |
| `logo.svg` | The Umbraco logo for the Backoffice and other public facing sites. | /umbraco/management/api/v1/security/back-office/graphics/logo |
| `logo_dark.svg` | The Umbraco logo in dark mode for the login screen. | /umbraco/management/api/v1/security/back-office/graphics/login-logo-alternative |
| `logo_light.svg` | The Umbraco logo in light mode for the login screen. | /umbraco/management/api/v1/security/back-office/graphics/login-logo |
All assets are linked up through the BackOfficeGraphicsController which uses the constants defined in [ContentSettings](../../../../Umbraco.Core/Configuration/Models/ContentSettings.cs).

View File

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

View File

Before

Width:  |  Height:  |  Size: 942 B

After

Width:  |  Height:  |  Size: 942 B

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -19,9 +19,10 @@ public class ContentSettings
internal const string StaticDisallowedUploadFiles = "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,xamlx";
internal const bool StaticShowDeprecatedPropertyEditors = false;
internal const string StaticLoginBackgroundImage = "login/login.jpg";
internal const string StaticLoginLogoImage = "login/logo_light.svg";
internal const string StaticLoginLogoImageAlternative = "login/logo_dark.svg";
internal const string StaticLoginBackgroundImage = "assets/login.jpg";
internal const string StaticLoginLogoImage = "assets/logo_light.svg";
internal const string StaticLoginLogoImageAlternative = "assets/logo_dark.svg";
internal const string StaticBackOfficeLogo = "assets/logo.svg";
internal const bool StaticHideBackOfficeLogo = false;
internal const bool StaticDisableDeleteWhenReferenced = false;
internal const bool StaticDisableUnpublishWhenReferenced = false;
@@ -83,10 +84,17 @@ public class ContentSettings
[DefaultValue(StaticLoginLogoImageAlternative)]
public string LoginLogoImageAlternative { get; set; } = StaticLoginLogoImageAlternative;
/// <summary>
/// Gets or sets a value for the path to the backoffice logo.
/// </summary>
[DefaultValue(StaticBackOfficeLogo)]
public string BackOfficeLogo { get; set; } = StaticBackOfficeLogo;
/// <summary>
/// Gets or sets a value indicating whether to hide the backoffice umbraco logo or not.
/// </summary>
[DefaultValue(StaticHideBackOfficeLogo)]
[Obsolete("This setting is no longer used and will be removed in future versions. An alternative BackOffice logo can be set using the BackOfficeLogo setting.")]
public bool HideBackOfficeLogo { get; set; } = StaticHideBackOfficeLogo;
/// <summary>

View File

@@ -167,8 +167,8 @@ export class UmbAppErrorElement extends UmbLitElement {
return html`
<div id="background"></div>
<div id="logo" aria-hidden="true">
<img src="/umbraco/backoffice/assets/umbraco_logomark_white.svg" alt="Umbraco" />
<div id="logo">
<umb-app-logo></umb-app-logo>
</div>
<div id="container" class="uui-text">
@@ -222,10 +222,6 @@ export class UmbAppErrorElement extends UmbLitElement {
height: 30px;
}
#logo img {
height: 100%;
}
#container {
position: relative;
display: flex;

View File

@@ -0,0 +1,46 @@
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit';
@customElement('umb-app-logo')
export class UmbAppLogoElement extends UmbLitElement {
@state()
private _logoUrl?: string;
constructor() {
super();
this.consumeContext(UMB_APP_CONTEXT, (instance) => {
this._logoUrl = `${instance.getServerUrl()}/umbraco/management/api/v1/security/back-office/graphics/logo`;
});
}
/**
* Do not use shadow DOM for this element.
* @returns {this} The element instance.
*/
override createRenderRoot(): this {
return this;
}
override render() {
if (!this._logoUrl) {
return nothing;
}
return html`
<img
aria-hidden="true"
loading="eager"
src="/umbraco/management/api/v1/security/back-office/graphics/logo"
alt="logo"
style="height: 100%" />
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-app-logo': UmbAppLogoElement;
}
}

View File

@@ -1,3 +1,4 @@
export type * from './app-context-config.interface.js';
export * from './app-logo.element.js';
export * from './app.element.js';
export * from './app.context.js';

View File

@@ -5,6 +5,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { UMB_NEWVERSION_MODAL, UMB_SYSINFO_MODAL } from '@umbraco-cms/backoffice/sysinfo';
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
@customElement('umb-backoffice-header-logo')
export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
@@ -17,6 +18,9 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
@state()
private _serverUpgradeCheck = false;
@state()
private _serverUrl = '';
#backofficeContext?: typeof UMB_BACKOFFICE_CONTEXT.TYPE;
constructor() {
@@ -34,6 +38,10 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
this.#backofficeContext = context;
});
this.consumeContext(UMB_APP_CONTEXT, (context) => {
this._serverUrl = context.getServerUrl();
});
}
protected override async firstUpdated() {
@@ -50,15 +58,16 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
override render() {
return html`
<uui-button id="logo" look="primary" label="Umbraco" compact popovertarget="logo-popover">
<img src="/umbraco/backoffice/assets/umbraco_logomark_white.svg" alt="Umbraco" />
<uui-button id="logo" look="primary" label="Logo" compact popovertarget="logo-popover">
<umb-app-logo id="logo-img"></umb-app-logo>
</uui-button>
<uui-popover-container id="logo-popover" placement="bottom-start">
<umb-popover-layout>
<div id="modal">
<img
src="/umbraco/backoffice/assets/umbraco_logo_blue.svg"
alt="Umbraco"
aria-hidden="true"
src="${this._serverUrl}/umbraco/management/api/v1/security/back-office/graphics/login-logo-alternative"
alt="logo"
width="300"
height="82"
loading="lazy" />
@@ -107,9 +116,9 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
--uui-button-background-color: transparent;
}
#logo > img {
#logo-img {
display: block;
height: var(--uui-size-10);
width: var(--uui-size-10);
}
#modal {

View File

@@ -5,10 +5,8 @@ import { css, html, LitElement, customElement } from '@umbraco-cms/backoffice/ex
export class UmbInstallerLayoutElement extends LitElement {
override render() {
return html`
<!-- <div id="background" aria-hidden="true"></div> -->
<div id="logo" aria-hidden="true">
<img src="/umbraco/backoffice/assets/umbraco_logomark_white.svg" alt="Umbraco" />
<div id="logo">
<umb-app-logo></umb-app-logo>
</div>
<main id="container">
@@ -32,7 +30,6 @@ export class UmbInstallerLayoutElement extends LitElement {
position: relative;
height: 100%;
background-color: var(--uui-color-default);
background-color: hsla(240, 68%, 11%, 1);
background-image: radial-gradient(at 99% 2%, hsla(212, 40%, 12%, 1) 0px, transparent 50%),
radial-gradient(at 98% 95%, hsla(255, 40%, 12%, 1) 0px, transparent 50%),
@@ -52,10 +49,6 @@ export class UmbInstallerLayoutElement extends LitElement {
z-index: 10;
}
#logo img {
height: 100%;
}
#container {
container: container / inline-size;
width: 100%;

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1256.6 344.8"><g fill="#283A97"><path d="M5 172.2C5.1 79.7 80.2 4.9 172.6 5s167.3 75.2 167.2 167.6c-.1 92.4-75 167.2-167.4 167.2C79.9 339.7 5 264.7 5 172.2zm164 57.3c-13 .4-26-.8-38.7-3.5-9.4-1.9-17.3-8.2-21.2-17-3.8-8.7-5.6-22.2-5.5-40.4.1-9.5.7-19 1.8-28.4 1.1-9.2 2.2-16.8 3.2-22.8l1.1-5.9v-.5c0-1.7-1.2-3.1-2.8-3.4l-21.6-3.4H85c-1.6 0-2.9 1.1-3.3 2.6-.4 1.4-.6 2.4-1.2 5.7-1.2 6.4-2.4 12.6-3.6 21.6-1.3 9.8-2.1 19.7-2.4 29.6-.5 6.9-.5 13.8 0 20.7.5 18.3 3.7 33 9.4 43.9s15.6 18.8 29.4 23.6c13.8 4.8 33 7.2 57.7 7.1h3.1c24.7.1 43.9-2.2 57.7-7.1 13.8-4.8 23.6-12.7 29.4-23.6 5.8-11 8.9-25.6 9.4-43.9.5-6.9.5-13.8 0-20.7-.3-9.9-1.1-19.8-2.4-29.6-1.3-9-2.4-15.1-3.6-21.6-.6-3.3-.9-4.3-1.2-5.7-.4-1.5-1.7-2.6-3.3-2.6h-.6l-21.6 3.4c-1.6.3-2.8 1.7-2.8 3.4v.5l1.2 5.9c1.1 6 2.2 13.6 3.3 22.7 1.1 9.4 1.7 18.9 1.8 28.4.2 18.2-1.7 31.6-5.5 40.4-3.8 8.7-11.6 15-20.9 17-12.7 2.7-25.7 3.9-38.7 3.5l-7.3.1zM1155.2 186.1c0-33.4 9.6-56.9 48.2-56.9s48.2 23.5 48.2 56.9-9.6 56.9-48.2 56.9-48.2-23.5-48.2-56.9zm71.9 0c0-23.2-3-36.5-23.6-36.5s-23.6 13.3-23.6 36.5 3.1 36.5 23.6 36.5 23.6-13.4 23.6-36.5zM487.2 238.7c.6 1 1.6 1.6 2.8 1.6h9c1.8 0 3.2-1.4 3.2-3.2V135c0-1.8-1.4-3.2-3.2-3.2h-18c-1.8 0-3.2 1.4-3.2 3.2v81c-7.7 4.3-16.4 6.5-25.2 6.3-11.5 0-17.2-5-17.2-16.1V135c0-1.8-1.4-3.2-3.2-3.2h-17.9c-1.8 0-3.2 1.4-3.2 3.2v73.4c0 20.8 9.8 34.5 37.3 34.5 12.6-.1 24.9-4.1 35.2-11.3l3.2 7.2.4-.1zM689.8 163.7c0-20.6-10.2-34.5-35.9-34.5-12.4 0-24.5 3.8-34.8 10.7-4.6-6.7-13.3-10.7-27.8-10.7-11.8.2-23.3 4.2-32.8 11.3l-3.2-7.2c-.6-1-1.6-1.7-2.8-1.7h-9c-1.8 0-3.2 1.4-3.2 3.2v102.1c0 1.8 1.4 3.2 3.2 3.2h18c1.8 0 3.2-1.4 3.2-3.2v-80.8c6.9-4.1 14.8-6.3 22.8-6.3 9.8 0 15.4 3.6 15.4 14v73.4c0 1.8 1.4 3.2 3.2 3.2h18c1.8 0 3.2-1.4 3.2-3.2v-81.1c6.8-4.2 14.8-6.4 22.8-6.4 9.6 0 15.4 3.6 15.4 14v73.4c0 1.8 1.4 3.2 3.2 3.2h18c1.8 0 3.2-1.4 3.2-3.2l-.1-73.4zM745.4 231.7c10.2 7.5 22.5 11.5 35.2 11.3 31.7 0 43.4-21.3 43.4-56.9s-11.7-56.9-43.4-56.9c-10.3.1-20.4 3-29.1 8.5v-32.8c0-1.8-1.4-3.2-3.2-3.2h-18c-1.8 0-3.2 1.4-3.2 3.2v132.3c0 1.8 1.4 3.2 3.2 3.2h9c1.2 0 2.2-.6 2.8-1.6l3.3-7.1zm29.5-9.4c-8.2 0-16.3-2.1-23.4-6.3v-59.9c7.1-4.1 15.2-6.3 23.4-6.3 21.3 0 24.5 16.3 24.5 36.2s-3.2 36.3-24.5 36.3zM913.9 150.5c-2.8-.4-5.6-.6-8.5-.5-9.7-.4-19.3 1.9-27.8 6.5v80.6c0 1.8-1.4 3.2-3.2 3.2h-18c-1.8 0-3.2-1.4-3.2-3.2V135c0-1.8 1.4-3.2 3.2-3.2h9c1.2 0 2.2.6 2.8 1.6l3.2 7.2c9.9-7.5 22-11.5 34.4-11.3 2.8 0 5.7.2 8.5.7 1.7 0 3 2.7 3 4.4v13c0 1.8-1.4 3.2-3.2 3.2h-.2M976.1 190.2c-10.7 1.3-17.2 5.4-17.2 16.7 0 8.3 3.6 16.1 16.7 16.1 8.3.1 16.4-2.4 23.2-7.2v-28.1l-22.7 2.5zm28.7 41.5C995.5 239 984 243 972.1 243c-28 0-37.3-17.4-37.3-34.8 0-23.5 15.2-33.4 39.7-35.4l24.4-1.9v-5.4c0-11.1-5.2-15.4-21.3-15.4-10.1 0-20.1 1.7-29.6 4.9-.3.1-.7.1-1 0-1.8 0-3.2-1.4-3.2-3.2v-14.4c0-1.4.8-2.6 2.1-3.1 10.8-3.6 22.1-5.4 33.5-5.4 35.6 0 43.8 15.6 43.8 38.7v69.3c0 1.8-1.4 3.2-3.2 3.2h-9c-1.2 0-2.2-.6-2.8-1.6l-3.4-6.8zM1128.6 218h1c1.8 0 3.2 1.4 3.2 3.2v14.4c0 1.3-.8 2.5-2 3-8.9 3.2-18.4 4.8-27.9 4.6-38.5 0-50.3-23-50.3-56.9s11.8-56.9 50.3-56.9c9.4-.2 18.9 1.2 27.8 4.4 1.2.5 2 1.7 2 3v14.5c0 1.8-1.4 3.2-3.2 3.2-.4.1-.7.1-1.1 0-7.8-2.5-16-3.7-24.2-3.6-21.1 0-27.2 14.4-27.2 35.4s6.1 35.4 27.2 35.4c8.2.1 16.4-1.1 24.2-3.6"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1256.6 344.8"><g fill="#fff"><path d="M5 172.2C5.1 79.7 80.2 4.9 172.6 5s167.3 75.2 167.2 167.6c-.1 92.4-75 167.2-167.4 167.2C79.9 339.7 4.9 264.7 5 172.2zm164 57.3c-13 .4-26-.8-38.7-3.5-9.4-1.9-17.3-8.2-21.2-17-3.8-8.7-5.6-22.2-5.5-40.4.1-9.5.7-19 1.8-28.4 1.1-9.2 2.2-16.8 3.2-22.8l1.1-5.9v-.5c0-1.7-1.2-3.1-2.8-3.4l-21.6-3.4H85c-1.6 0-2.9 1.1-3.3 2.6-.4 1.4-.6 2.4-1.2 5.7-1.2 6.4-2.4 12.6-3.6 21.6-1.3 9.8-2.1 19.7-2.4 29.6-.5 6.9-.5 13.8 0 20.7.5 18.3 3.7 33 9.4 43.9s15.6 18.8 29.4 23.6c13.8 4.8 33 7.2 57.7 7.1h3.1c24.7.1 43.9-2.2 57.7-7.1 13.8-4.8 23.6-12.7 29.4-23.6 5.8-11 8.9-25.6 9.4-43.9.5-6.9.5-13.8 0-20.7-.3-9.9-1.1-19.8-2.4-29.6-1.3-8.9-2.4-15.1-3.6-21.6-.6-3.3-.9-4.3-1.2-5.7-.4-1.5-1.7-2.6-3.3-2.6h-.6l-21.6 3.4c-1.6.3-2.8 1.7-2.8 3.4v.5l1.2 5.9c1.1 6 2.2 13.6 3.3 22.7 1.1 9.4 1.7 18.9 1.8 28.4.2 18.2-1.7 31.6-5.5 40.4-3.8 8.7-11.6 15-20.9 17-12.7 2.7-25.7 3.9-38.7 3.5l-7.3.1zM1155.2 186.1c0-33.4 9.6-56.9 48.2-56.9s48.2 23.5 48.2 56.9-9.6 56.9-48.2 56.9-48.2-23.5-48.2-56.9zm71.9 0c0-23.2-3-36.5-23.6-36.5s-23.6 13.3-23.6 36.5 3.1 36.5 23.6 36.5 23.6-13.4 23.6-36.5zM487.2 238.7c.6 1 1.6 1.6 2.8 1.6h9c1.8 0 3.2-1.4 3.2-3.2V135c0-1.8-1.4-3.2-3.2-3.2h-18c-1.8 0-3.2 1.4-3.2 3.2v81c-7.7 4.3-16.4 6.5-25.2 6.3-11.5 0-17.2-5-17.2-16.1V135c0-1.8-1.4-3.2-3.2-3.2h-17.9c-1.8 0-3.2 1.4-3.2 3.2v73.4c0 20.8 9.8 34.5 37.3 34.5 12.6-.1 24.9-4.1 35.2-11.3l3.2 7.2.4-.1zM689.8 163.7c0-20.6-10.2-34.5-35.9-34.5-12.4 0-24.5 3.8-34.8 10.7-4.6-6.7-13.3-10.7-27.8-10.7-11.8.2-23.3 4.2-32.8 11.3l-3.2-7.2c-.6-1-1.6-1.7-2.8-1.7h-9c-1.8 0-3.2 1.4-3.2 3.2v102.1c0 1.8 1.4 3.2 3.2 3.2h18c1.8 0 3.2-1.4 3.2-3.2v-80.8c6.9-4.1 14.8-6.3 22.8-6.3 9.8 0 15.4 3.6 15.4 14v73.4c0 1.8 1.4 3.2 3.2 3.2h18c1.8 0 3.2-1.4 3.2-3.2v-81.1c6.8-4.2 14.8-6.4 22.8-6.4 9.6 0 15.4 3.6 15.4 14v73.4c0 1.8 1.4 3.2 3.2 3.2h18c1.8 0 3.2-1.4 3.2-3.2l-.1-73.4zM745.4 231.7c10.2 7.5 22.5 11.5 35.2 11.3 31.7 0 43.4-21.3 43.4-56.9s-11.7-56.9-43.4-56.9c-10.3.1-20.4 3-29.1 8.5v-32.9c0-1.8-1.4-3.2-3.2-3.2h-18c-1.8 0-3.2 1.4-3.2 3.2v132.3c0 1.8 1.4 3.2 3.2 3.2h9c1.2 0 2.2-.6 2.8-1.6l3.3-7zm29.5-9.4c-8.2 0-16.3-2.1-23.4-6.3v-59.9c7.1-4.1 15.2-6.3 23.4-6.3 21.3 0 24.5 16.3 24.5 36.2s-3.2 36.3-24.5 36.3zM913.9 150.5c-2.8-.4-5.6-.6-8.5-.5-9.7-.4-19.3 1.9-27.8 6.5v80.6c0 1.8-1.4 3.2-3.2 3.2h-18c-1.8 0-3.2-1.4-3.2-3.2V135c0-1.8 1.4-3.2 3.2-3.2h9c1.2 0 2.2.6 2.8 1.6l3.2 7.2c9.9-7.5 22-11.5 34.4-11.3 2.8 0 5.7.2 8.5.7 1.7 0 3 2.7 3 4.4v13c0 1.8-1.4 3.2-3.2 3.2l-.2-.1M976.1 190.2c-10.7 1.3-17.2 5.4-17.2 16.7 0 8.3 3.6 16.1 16.7 16.1 8.3.1 16.4-2.4 23.2-7.2v-28.1l-22.7 2.5zm28.7 41.5C995.5 239 984 243 972.1 243c-28 0-37.3-17.4-37.3-34.8 0-23.5 15.2-33.4 39.7-35.4l24.4-1.9v-5.4c0-11.1-5.2-15.4-21.3-15.4-10.1 0-20.1 1.7-29.6 4.9-.3.1-.7.1-1 0-1.8 0-3.2-1.4-3.2-3.2v-14.4c0-1.4.8-2.6 2.1-3.1 10.8-3.6 22.1-5.4 33.5-5.4 35.6 0 43.8 15.6 43.8 38.7v69.3c0 1.8-1.4 3.2-3.2 3.2h-9c-1.2 0-2.2-.6-2.8-1.6l-3.4-6.8zM1128.6 218h1c1.8 0 3.2 1.4 3.2 3.2v14.4c0 1.3-.8 2.5-2 3-8.9 3.2-18.4 4.8-27.9 4.6-38.5 0-50.3-23-50.3-56.9s11.8-56.9 50.3-56.9c9.4-.2 18.9 1.2 27.8 4.4 1.2.5 2 1.7 2 3v14.5c0 1.8-1.4 3.2-3.2 3.2-.4.1-.7.1-1.1 0-7.8-2.5-16-3.7-24.2-3.6-21.1 0-27.2 14.4-27.2 35.4s6.1 35.4 27.2 35.4c8.2.1 16.4-1.1 24.2-3.6"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -1,3 +1,4 @@
import { handlers as backofficeHandlers } from './handlers/backoffice.handlers.js';
import { handlers as configHandlers } from './handlers/config.handlers.js';
import { handlers as cultureHandlers } from './handlers/culture.handlers.js';
import { handlers as dataTypeHandlers } from './handlers/data-type/index.js';
@@ -32,12 +33,13 @@ import { handlers as templateHandlers } from './handlers/template/index.js';
import { handlers as upgradeHandlers } from './handlers/upgrade.handlers.js';
import { handlers as userGroupsHandlers } from './handlers/user-group/index.js';
import { handlers as userHandlers } from './handlers/user/index.js';
//import * as manifestsHandlers from './handlers/manifests.handlers.js';
import * as manifestsHandlers from './handlers/manifests.handlers.js';
import * as serverHandlers from './handlers/server.handlers.js';
import { handlers as documentBlueprintHandlers } from './handlers/document-blueprint/index.js';
import { handlers as temporaryFileHandlers } from './handlers/temporary-file/index.js';
const handlers = [
...backofficeHandlers,
...configHandlers,
...cultureHandlers,
...dataTypeHandlers,
@@ -76,6 +78,7 @@ const handlers = [
...temporaryFileHandlers,
...serverHandlers.serverInformationHandlers,
serverHandlers.serverRunningHandler,
...manifestsHandlers.manifestEmptyHandlers,
];
/* TODO: find solution to run with different handlers across vite mocks and web-test-runner mocks

View File

@@ -902,7 +902,7 @@ export const data: Array<UmbMockDocumentModel> = [
Some value for the RTE with an <a href="https://google.com">external link</a> and an <a type="document" href="/{localLink:c05da24d-7740-447b-9cdc-bd8ce2172e38}">internal link</a> foo foo
</p>
<p>
<img width="500" height="332" loading="lazy" alt="Jason" src="/umbraco/backoffice/assets/login.jpg" />
<img width="384" height="228" loading="lazy" alt="Installer illustration" src="/umbraco/backoffice/assets/installer-illustration.svg" />
</p>
<p>End of test content</p>
`,
@@ -921,7 +921,7 @@ export const data: Array<UmbMockDocumentModel> = [
</p>
<div class="umb-macro-holder TestMacro umb-macro-mce_1 mceNonEditable"><!-- <?UMBRACO_MACRO macroAlias="TestMacro" /> --><ins>Macro alias: <strong>TestMacro</strong></ins></div>
<p>
<img width="500" height="332" loading="lazy" alt="Jason" src="/umbraco/backoffice/assets/login.jpg" />
<img width="384" height="228" loading="lazy" alt="Installer illustration" src="/umbraco/backoffice/assets/installer-illustration.svg" />
</p>
<p>End of test content</p>
`,

View File

@@ -0,0 +1,50 @@
const { rest } = window.MockServiceWorker;
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import logoUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/logo.svg';
import loginLogoUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/logo_light.svg';
import loginLogoAlternativeUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/logo_dark.svg';
import loginBackgroundUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/login.jpg';
export const handlers = [
rest.get(umbracoPath('/security/back-office/graphics/logo'), async (req, res, ctx) => {
const imageBuffer = await fetch(logoUrl)
.then((res) => res.arrayBuffer());
return res(
ctx.set('Content-Length', imageBuffer.byteLength.toString()),
ctx.set('Content-Type', 'image/svg+xml'),
ctx.body(imageBuffer)
);
}),
rest.get(umbracoPath('/security/back-office/graphics/login-logo'), async (req, res, ctx) => {
const imageBuffer = await fetch(loginLogoUrl)
.then((res) => res.arrayBuffer());
return res(
ctx.set('Content-Length', imageBuffer.byteLength.toString()),
ctx.set('Content-Type', 'image/svg+xml'),
ctx.body(imageBuffer)
);
}),
rest.get(umbracoPath('/security/back-office/graphics/login-logo-alternative'), async (req, res, ctx) => {
const imageBuffer = await fetch(loginLogoAlternativeUrl)
.then((res) => res.arrayBuffer());
return res(
ctx.set('Content-Length', imageBuffer.byteLength.toString()),
ctx.set('Content-Type', 'image/svg+xml'),
ctx.body(imageBuffer)
);
}),
rest.get(umbracoPath('/security/back-office/graphics/login-background'), async (req, res, ctx) => {
const imageBuffer = await fetch(loginBackgroundUrl)
.then((res) => res.arrayBuffer());
return res(
ctx.set('Content-Length', imageBuffer.byteLength.toString()),
ctx.set('Content-Type', 'image/jpeg'),
ctx.body(imageBuffer)
);
}),
];

View File

@@ -5,12 +5,16 @@ import { UMB_AUTH_CONTEXT } from '../auth.context.token.js';
import type { UmbAuthProviderDefaultProps } from '../types.js';
import type { UmbModalAppAuthConfig, UmbModalAppAuthValue } from './umb-app-auth-modal.token.js';
import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit';
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
@customElement('umb-app-auth-modal')
export class UmbAppAuthModalElement extends UmbModalBaseElement<UmbModalAppAuthConfig, UmbModalAppAuthValue> {
@state()
private _error?: string;
@state()
private _serverUrl = '';
get props(): UmbAuthProviderDefaultProps {
return {
userLoginState: this.data?.userLoginState ?? 'loggingIn',
@@ -34,17 +38,33 @@ export class UmbAppAuthModalElement extends UmbModalBaseElement<UmbModalAppAuthC
);
}
override connectedCallback(): void {
super.connectedCallback();
this.consumeContext(UMB_APP_CONTEXT, (context) => {
this._serverUrl = context.getServerUrl();
this.style.setProperty(
'--image',
`url('${this._serverUrl}/umbraco/management/api/v1/security/back-office/graphics/login-background') no-repeat center center/cover`,
);
});
}
override render() {
return html`
<div id="layout">
<img
id="logo-on-background"
src="/umbraco/backoffice/assets/umbraco_logo_blue.svg"
src="${this._serverUrl}/umbraco/management/api/v1/security/back-office/graphics/login-logo-alternative"
alt="Logo"
aria-hidden="true"
part="auth-logo-background" />
<div id="graphic" aria-hidden="true">
<img part="auth-logo" id="logo-on-image" src="/umbraco/backoffice/assets/umbraco_logo_white.svg" alt="Logo" />
<img
part="auth-logo"
id="logo-on-image"
src="${this._serverUrl}/umbraco/management/api/v1/security/back-office/graphics/login-logo"
alt="Logo" />
<svg
id="curve-top"
width="1746"
@@ -119,7 +139,6 @@ export class UmbAppAuthModalElement extends UmbModalBaseElement<UmbModalAppAuthC
display: block;
background: rgb(244, 244, 244);
--image: url('/umbraco/backoffice/assets/login.jpg') no-repeat center center/cover;
--curves-color: var(--umb-login-curves-color, #f5c1bc);
--curves-display: var(--umb-login-curves-display, inline);
}

View File

@@ -38,8 +38,8 @@
}
</style>
<div id="noscript-container">
<h1 class="uui-h3" style="display: inline-flex; align-items: center; gap: 10px">
<img aria-hidden="true" alt="logo" src="/logo_dark.svg" style="width: 100%" />
<h1 aria-hidden="true" class="uui-h3" style="display: inline-flex; align-items: center; gap: 10px">
<img alt="logo" src="/umbraco/management/api/v1/security/back-office/graphics/login-logo-alternative" style="width: 100%" />
</h1>
<p>For full functionality of Umbraco CMS it is necessary to enable JavaScript.</p>
<p>Here are the <a href="https://www.enable-javascript.com/" target="_blank" rel="noopener"
@@ -47,9 +47,9 @@
</div>
</noscript>
<umb-auth
logo-image="/logo_light.svg"
logo-image-alternative="/logo_dark.svg"
background-image="/login.jpg"
logo-image="/umbraco/management/api/v1/security/back-office/graphics/login-logo"
logo-image-alternative="/umbraco/management/api/v1/security/back-office/graphics/login-logo-alternative"
background-image="/umbraco/management/api/v1/security/back-office/graphics/login-background"
username-is-email="true"
allow-password-reset
allow-user-invite></umb-auth>

View File

@@ -1,27 +1,28 @@
{
"name": "login",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"watch": "tsc && vite build --watch",
"preview": "vite preview"
},
"engines": {
"node": ">=20.8",
"npm": ">=10.1"
},
"dependencies": {
},
"devDependencies": {
"name": "login",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"watch": "tsc && vite build --watch",
"preview": "vite preview"
},
"engines": {
"node": ">=20.8",
"npm": ">=10.1"
},
"dependencies": {},
"devDependencies": {
"@umbraco-cms/backoffice": "^15.0.0",
"msw": "^2.6.4",
"typescript": "^5.6.3",
"vite": "^5.4.11",
"vite-tsconfig-paths": "^5.1.2"
},
"msw": {
"workerDirectory": "public"
}
}
"typescript": "^5.6.3",
"vite": "^5.4.11",
"vite-tsconfig-paths": "^5.1.2"
},
"msw": {
"workerDirectory": [
"public"
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

View File

@@ -8,8 +8,8 @@
* - Please do NOT serve this file on production.
*/
const PACKAGE_VERSION = '2.4.9'
const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'
const PACKAGE_VERSION = '2.6.4'
const INTEGRITY_CHECKSUM = 'ca7800994cc8bfb5eb961e037c877074'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()
@@ -62,7 +62,12 @@ self.addEventListener('message', async function (event) {
sendToClient(client, {
type: 'MOCKING_ENABLED',
payload: true,
payload: {
client: {
id: client.id,
frameType: client.frameType,
},
},
})
break
}
@@ -155,6 +160,10 @@ async function handleRequest(event, requestId) {
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId)
if (activeClientIds.has(event.clientId)) {
return client
}
if (client?.frameType === 'top-level') {
return client
}
@@ -183,12 +192,14 @@ async function getResponse(event, client, requestId) {
const requestClone = request.clone()
function passthrough() {
const headers = Object.fromEntries(requestClone.headers.entries())
// Cast the request headers to a new Headers instance
// so the headers can be manipulated with.
const headers = new Headers(requestClone.headers)
// Remove internal MSW request header so the passthrough request
// complies with any potential CORS preflight checks on the server.
// Some servers forbid unknown request headers.
delete headers['x-msw-intention']
// Remove the "accept" header value that marked this request as passthrough.
// This prevents request alteration and also keeps it compliant with the
// user-defined CORS policies.
headers.delete('accept', 'msw/passthrough')
return fetch(requestClone, { headers })
}

View File

@@ -0,0 +1,53 @@
import { HttpHandler, http, HttpResponse } from "msw";
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import logoUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/logo.svg';
import loginLogoUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/logo_light.svg';
import loginLogoAlternativeUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/logo_dark.svg';
import loginBackgroundUrl from '../../../../Umbraco.Cms.StaticAssets/wwwroot/umbraco/assets/login.jpg';
export const handlers: HttpHandler[] = [
http.get(umbracoPath('/security/back-office/graphics/logo'), async () => {
const imageBuffer = await fetch(logoUrl)
.then((res) => res.arrayBuffer());
return HttpResponse.arrayBuffer(imageBuffer, {
headers: {
'Content-Length': imageBuffer.byteLength.toString(),
'Content-Type': 'image/svg+xml'
}
});
}),
http.get(umbracoPath('/security/back-office/graphics/login-logo'), async () => {
const imageBuffer = await fetch(loginLogoUrl)
.then((res) => res.arrayBuffer());
return HttpResponse.arrayBuffer(imageBuffer, {
headers: {
'Content-Length': imageBuffer.byteLength.toString(),
'Content-Type': 'image/svg+xml'
}
});
}),
http.get(umbracoPath('/security/back-office/graphics/login-logo-alternative'), async () => {
const imageBuffer = await fetch(loginLogoAlternativeUrl)
.then((res) => res.arrayBuffer());
return HttpResponse.arrayBuffer(imageBuffer, {
headers: {
'Content-Length': imageBuffer.byteLength.toString(),
'Content-Type': 'image/svg+xml'
}
});
}),
http.get(umbracoPath('/security/back-office/graphics/login-background'), async () => {
const imageBuffer = await fetch(loginBackgroundUrl)
.then((res) => res.arrayBuffer());
return HttpResponse.arrayBuffer(imageBuffer, {
headers: {
'Content-Length': imageBuffer.byteLength.toString(),
'Content-Type': 'image/jpeg'
}
});
}),
];

View File

@@ -1,6 +1,6 @@
import { handlers as backofficeHandlers } from './backoffice.handlers.js';
import { handlers as loginHandlers } from './login.handlers.js';
import type { HttpHandler } from "msw";
const handlers: HttpHandler[] = [...loginHandlers];
const handlers = [...backofficeHandlers, ...loginHandlers];
export { handlers };