Skip to content

Commit 393566a

Browse files
V14: Adds preview.js (#16305)
* Adds `preview.js` to replace the legacy `umbraco.websitepreview.min.js` script. Updates the content's `Id` with `Key`. * allow any protected route to render the backoffice * optimise component so it doesn't need to observe its attributes and use the popover API to show it on top of the content * handle case where the culture could be set to "invariant" - we just want to set the "lang" attribute to the default ui language * convert 'end preview' into an api request and reset the style of the button * minimize function * move static text into constants --------- Co-authored-by: Jacob Overgaard <[email protected]>
1 parent 5a75488 commit 393566a

File tree

7 files changed

+156
-142
lines changed

7 files changed

+156
-142
lines changed

src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,6 @@ private void MapMinimalBackOffice(IEndpointRouteBuilder endpoints)
8888
Controller = ControllerExtensions.GetControllerName<BackOfficeDefaultController>(),
8989
Action = nameof(BackOfficeDefaultController.Index),
9090
},
91-
constraints: new { slug = @"^(section.*|preview|upgrade|install|oauth_complete|logout|error)$" });
91+
constraints: new { slug = @"^(section|preview|upgrade|install|oauth_complete|logout|error).*$" });
9292
}
9393
}

src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Index.cshtml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
@using System.Globalization
1+
@using Microsoft.Extensions.Options
22
@using Umbraco.Cms.Api.Management.Extensions
3+
@using Umbraco.Cms.Core.Configuration.Models
34
@using Umbraco.Cms.Core.Manifest
45
@using Umbraco.Cms.Core.Serialization
56
@using Umbraco.Cms.Web.Common.Hosting
67
@using Umbraco.Extensions
78
@inject IBackOfficePathGenerator BackOfficePathGenerator
89
@inject IPackageManifestService PackageManifestService
910
@inject IJsonSerializer JsonSerializer
11+
@inject IOptions<GlobalSettings> GlobalSettings
1012

1113
@{
1214
var backOfficePath = BackOfficePathGenerator.BackOfficePath;
1315
var backOfficeAssetsPath = BackOfficePathGenerator.BackOfficeAssetsPath;
1416
}
1517

1618
<!DOCTYPE html>
17-
<html lang="@CultureInfo.CurrentCulture.Name">
19+
<html lang="@GlobalSettings.Value.DefaultUILanguage">
1820

1921
<head>
2022
<base href="@backOfficePath.EnsureEndsWith('/')" />
@@ -30,7 +32,7 @@
3032
</head>
3133

3234
<body class="uui-font uui-text" style="margin: 0; padding: 0; overflow: hidden">
33-
<umb-app culture="@CultureInfo.CurrentCulture.Name"></umb-app>
35+
<umb-app></umb-app>
3436
</body>
3537

3638
</html>

src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
@using System.Globalization
21
@using Microsoft.AspNetCore.Mvc.TagHelpers
32
@using Microsoft.Extensions.Options;
43
@using Umbraco.Cms.Api.Management.Extensions
@@ -35,7 +34,7 @@
3534
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
3635

3736
<!DOCTYPE html>
38-
<html lang="@CultureInfo.CurrentCulture.Name.ToLowerInvariant()">
37+
<html lang="@GlobalSettings.Value.DefaultUILanguage">
3938
<head>
4039
<meta charset="UTF-8"/>
4140
<base href="@backOfficePath.EnsureEndsWith('/')"/>
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
!(() => {
2+
"use strict";
3+
4+
// Quick exit if within an iframe.
5+
if (window.self !== window.top) return;
6+
7+
const styles = `
8+
.umbraco-preview-badge {
9+
display: inline-flex;
10+
background: rgba(27, 38, 79, 0.9);
11+
color: #fff;
12+
font-size: 87.5%;
13+
justify-content: center;
14+
align-items: center;
15+
box-shadow: 0 5px 10px rgba(0, 0, 0, .2), 0 1px 2px rgba(0, 0, 0, .2);
16+
line-height: 1;
17+
pointer-events:none;
18+
animation: umbraco-preview-badge--effect 10s 1.2s ease both;
19+
border-radius: 3px 3px 0 0;
20+
}
21+
.umbraco-preview-badge[popover] {
22+
inset: unset;
23+
position: fixed;
24+
bottom: 0;
25+
left: 50%;
26+
transform: translate(-50%, 55px);
27+
}
28+
@keyframes umbraco-preview-badge--effect {
29+
0% { transform: translate(-50%, 55px); animation-timing-function: ease-out; }
30+
1.5% { transform: translate(-50%, -20px); animation-timing-function: ease-in; }
31+
5.0% { transform: translate(-50%, -8px); animation-timing-function: ease-in; }
32+
7.5% { transform: translate(-50%, -4px); animation-timing-function: ease-in; }
33+
9.2% { transform: translate(-50%, -2px); animation-timing-function: ease-in; }
34+
3.5%, 6.5%, 8.5% { transform: translate(-50%, 0); animation-timing-function: ease-out; }
35+
9.7% { transform: translate(-50%, 0); animation-timing-function: ease-out; }
36+
10.0% { transform: translate(-50%, 0); }
37+
60% { transform: translate(-50%, 0); animation-timing-function: ease-out; }
38+
61.5% { transform: translate(-50%, -24px); animation-timing-function: ease-in; }
39+
65.0% { transform: translate(-50%, -8px); animation-timing-function: ease-in; }
40+
67.5% { transform: translate(-50%, -4px); animation-timing-function: ease-in; }
41+
69.2% { transform: translate(-50%, -2px); animation-timing-function: ease-in; }
42+
63.5%, 66.5%, 68.5% { transform: translate(-50%, 0); animation-timing-function: ease-out; }
43+
69.7% { transform: translate(-50%, 0); animation-timing-function: ease-out; }
44+
70.0% { transform: translate(-50%, 0); }
45+
100.0% { transform: translate(-50%, 0); }
46+
}
47+
.umbraco-preview-badge__header {
48+
padding: 1em;
49+
font-weight: bold;
50+
pointer-events:none;
51+
}
52+
.umbraco-preview-badge__a {
53+
background: inherit;
54+
width: 3em;
55+
padding: 1em;
56+
display: flex;
57+
flex-shrink: 0;
58+
justify-content: center;
59+
align-items: center;
60+
align-self: stretch;
61+
color:white;
62+
text-decoration:none;
63+
font-weight: bold;
64+
border: 0;
65+
border-left: 1px solid hsla(0,0%,100%,.25);
66+
pointer-events:all;
67+
cursor: pointer;
68+
}
69+
.umbraco-preview-badge__a svg {
70+
width: 1em;
71+
height:1em;
72+
}
73+
.umbraco-preview-badge__a:hover {
74+
background: #202d5e00;
75+
}
76+
`;
77+
78+
const exitIcon = `
79+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
80+
<title>Click to end preview mode</title>
81+
<path fill="#fff" d="M5273.1 2400.1v-2c0-2.8-5-4-9.7-4s-9.7 1.3-9.7 4v2a7 7 0 002 4.9l5 4.9c.3.3.4.6.4 1v6.4c0 .4.2.7.6.8l2.9.9c.5.1 1-.2 1-.8v-7.2c0-.4.2-.7.4-1l5.1-5a7 7 0 002-4.9zm-9.7-.1c-4.8 0-7.4-1.3-7.5-1.8.1-.5 2.7-1.8 7.5-1.8s7.3 1.3 7.5 1.8c-.2.5-2.7 1.8-7.5 1.8z"/>
82+
<path fill="#fff" d="M5268.4 2410.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1h-4.3zM5272.7 2413.7h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1zM5272.7 2417h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1 0-.5-.4-1-1-1z"/>
83+
<path fill="#fff" d="M78.2 13l-8.7 11.7a32.5 32.5 0 11-51.9 25.8c0-10.3 4.7-19.7 12.9-25.8L21.8 13a47 47 0 1056.4 0z"/>
84+
<path fill="#fff" d="M42.7 2.5h14.6v49.4H42.7z"/>
85+
</svg>`;
86+
87+
class UmbWebsitePreviewElement extends HTMLElement {
88+
connectedCallback() {
89+
this.#render();
90+
}
91+
92+
async #endPreview() {
93+
await fetch(`/umbraco/management/api/v1/preview`, {
94+
method: "DELETE"
95+
});
96+
97+
window.location.href = this.getAttribute("url") ?? '/';
98+
}
99+
100+
#render() {
101+
const path = this.getAttribute("path") ?? '/umbraco';
102+
const unique = this.getAttribute("unique") ?? '';
103+
104+
const shadow = this.attachShadow({ mode: "open" });
105+
106+
const wrapper = document.createElement("div");
107+
wrapper.id = "umbracoPreviewBadge";
108+
wrapper.className = "umbraco-preview-badge";
109+
wrapper.popover = "manual";
110+
111+
const title = document.createElement("span");
112+
title.className = "umbraco-preview-badge__header";
113+
title.textContent = "Preview mode";
114+
115+
wrapper.appendChild(title);
116+
117+
const btnOpen = document.createElement('a');
118+
btnOpen.classList.add('umbraco-preview-badge__a', 'open');
119+
btnOpen.title = 'Open preview in BackOffice';
120+
btnOpen.href = `${path}/preview/?id=${unique}`;
121+
btnOpen.innerHTML = '&hellip;';
122+
123+
wrapper.appendChild(btnOpen);
124+
125+
const btnExit = document.createElement("button");
126+
btnExit.type = "button";
127+
btnExit.classList.add("umbraco-preview-badge__a", "end");
128+
btnExit.title = "End preview mode";
129+
btnExit.innerHTML = exitIcon;
130+
btnExit.onclick = () => this.#endPreview();
131+
132+
wrapper.appendChild(btnExit);
133+
134+
const style = document.createElement("style");
135+
136+
style.textContent = styles;
137+
138+
shadow.appendChild(style);
139+
shadow.appendChild(wrapper);
140+
}
141+
}
142+
143+
window.customElements.define("umb-website-preview", UmbWebsitePreviewElement);
144+
})();

src/Umbraco.Core/Configuration/Models/ContentSettings.cs

Lines changed: 3 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -13,140 +13,9 @@ public class ContentSettings
1313
{
1414
internal const bool StaticResolveUrlsFromTextString = false;
1515

16-
internal const string StaticDefaultPreviewBadge =
17-
@"
18-
<div id=""umbracoPreviewBadge"" class=""umbraco-preview-badge"">
19-
<span class=""umbraco-preview-badge__header"">Preview mode</span>
20-
<a href=""{0}/preview/?id={2}"" class=""umbraco-preview-badge__a open"" title=""Open preview in BackOffice"">
21-
22-
</a>
23-
<a href=""{0}/preview/end?redir={1}"" class=""umbraco-preview-badge__a end"" title=""End preview mode"">
24-
<svg viewBox=""0 0 100 100"" xmlns=""http://www.w3.org/2000/svg""><title>Click to end preview mode</title><path fill=""#fff"" d=""M5273.1 2400.1v-2c0-2.8-5-4-9.7-4s-9.7 1.3-9.7 4v2a7 7 0 002 4.9l5 4.9c.3.3.4.6.4 1v6.4c0 .4.2.7.6.8l2.9.9c.5.1 1-.2 1-.8v-7.2c0-.4.2-.7.4-1l5.1-5a7 7 0 002-4.9zm-9.7-.1c-4.8 0-7.4-1.3-7.5-1.8.1-.5 2.7-1.8 7.5-1.8s7.3 1.3 7.5 1.8c-.2.5-2.7 1.8-7.5 1.8z""/><path fill=""#fff"" d=""M5268.4 2410.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1h-4.3zM5272.7 2413.7h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1zM5272.7 2417h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1 0-.5-.4-1-1-1z""/><path fill=""#fff"" d=""M78.2 13l-8.7 11.7a32.5 32.5 0 11-51.9 25.8c0-10.3 4.7-19.7 12.9-25.8L21.8 13a47 47 0 1056.4 0z""/><path fill=""#fff"" d=""M42.7 2.5h14.6v49.4H42.7z""/></svg>
25-
</a>
26-
</div>
27-
<style type=""text/css"">
28-
.umbraco-preview-badge {{
29-
position: fixed;
30-
bottom: 0;
31-
display: inline-flex;
32-
background: rgba(27, 38, 79, 0.9);
33-
color: #fff;
34-
font-size: 12px;
35-
z-index: 99999999;
36-
justify-content: center;
37-
align-items: center;
38-
box-shadow: 0 5px 10px rgba(0, 0, 0, .2), 0 1px 2px rgba(0, 0, 0, .2);
39-
line-height: 1;
40-
pointer-events:none;
41-
left: 50%;
42-
transform: translate(-50%, 40px);
43-
animation: umbraco-preview-badge--effect 10s 1.2s ease both;
44-
border-radius: 3px 3px 0 0;
45-
}}
46-
@keyframes umbraco-preview-badge--effect {{
47-
0% {{
48-
transform: translate(-50%, 40px);
49-
animation-timing-function: ease-out;
50-
}}
51-
1.5% {{
52-
transform: translate(-50%, -20px);
53-
animation-timing-function: ease-in;
54-
}}
55-
5.0% {{
56-
transform: translate(-50%, -8px);
57-
animation-timing-function: ease-in;
58-
}}
59-
7.5% {{
60-
transform: translate(-50%, -4px);
61-
animation-timing-function: ease-in;
62-
}}
63-
9.2% {{
64-
transform: translate(-50%, -2px);
65-
animation-timing-function: ease-in;
66-
}}
67-
3.5%,
68-
6.5%,
69-
8.5% {{
70-
transform: translate(-50%, 0);
71-
animation-timing-function: ease-out;
72-
}}
73-
9.7% {{
74-
transform: translate(-50%, 0);
75-
animation-timing-function: ease-out;
76-
}}
77-
10.0% {{
78-
transform: translate(-50%, 0);
79-
}}
80-
81-
82-
60% {{
83-
transform: translate(-50%, 0);
84-
animation-timing-function: ease-out;
85-
}}
86-
61.5% {{
87-
transform: translate(-50%, -20px);
88-
animation-timing-function: ease-in;
89-
}}
90-
65.0% {{
91-
transform: translate(-50%, -8px);
92-
animation-timing-function: ease-in;
93-
}}
94-
67.5% {{
95-
transform: translate(-50%, -4px);
96-
animation-timing-function: ease-in;
97-
}}
98-
69.2% {{
99-
transform: translate(-50%, -2px);
100-
animation-timing-function: ease-in;
101-
}}
102-
63.5%,
103-
66.5%,
104-
68.5% {{
105-
transform: translate(-50%, 0);
106-
animation-timing-function: ease-out;
107-
}}
108-
69.7% {{
109-
transform: translate(-50%, 0);
110-
animation-timing-function: ease-out;
111-
}}
112-
70.0% {{
113-
transform: translate(-50%, 0);
114-
}}
115-
100.0% {{
116-
transform: translate(-50%, 0);
117-
}}
118-
}}
119-
.umbraco-preview-badge__header {{
120-
padding: 1em;
121-
font-weight: bold;
122-
pointer-events:none;
123-
}}
124-
.umbraco-preview-badge__a {{
125-
width: 3em;
126-
padding: 1em;
127-
display: flex;
128-
flex-shrink: 0;
129-
align-items: center;
130-
align-self: stretch;
131-
color:white;
132-
text-decoration:none;
133-
font-weight: bold;
134-
border-left: 1px solid hsla(0,0%,100%,.25);
135-
pointer-events:all;
136-
}}
137-
.umbraco-preview-badge__a svg {{
138-
width: 1em;
139-
height:1em;
140-
}}
141-
.umbraco-preview-badge__a:hover {{
142-
background: #202d5e;
143-
}}
144-
.umbraco-preview-badge__end svg {{
145-
fill: #fff;
146-
width:1em;
147-
}}
148-
</style>
149-
<script type=""text/javascript"" data-umbraco-path=""{0}"" src=""{0}/js/umbraco.websitepreview.min.js""></script>";
16+
internal const string StaticDefaultPreviewBadge = @"
17+
<script src=""{0}/website/preview.js""></script>
18+
<umb-website-preview path=""{0}"" url=""{1}"" unique=""{2}""></umb-website-preview>";
15019

15120
internal const string StaticDisallowedUploadFiles = "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,xamlx";
15221
internal const bool StaticShowDeprecatedPropertyEditors = false;

src/Umbraco.Web.Common/Views/UmbracoViewPage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public void WriteUmbracoContent(TagHelperOutput tagHelperOutput)
142142
ContentSettings.PreviewBadge,
143143
HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoPath),
144144
Context.Request.GetEncodedUrl(),
145-
UmbracoContext.PublishedRequest?.PublishedContent?.Id);
145+
UmbracoContext.PublishedRequest?.PublishedContent?.Key);
146146
}
147147
else
148148
{

src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public static IHtmlContent PreviewBadge(
8787
contentSettings.PreviewBadge,
8888
hostingEnvironment.ToAbsolute(globalSettings.UmbracoPath),
8989
WebUtility.UrlEncode(httpContextAccessor.GetRequiredHttpContext().Request.Path),
90-
umbracoContext.PublishedRequest?.PublishedContent?.Id);
90+
umbracoContext.PublishedRequest?.PublishedContent?.Key);
9191
return new HtmlString(htmlBadge);
9292
}
9393

0 commit comments

Comments
 (0)