Skip to content

Commit 2d8127c

Browse files
committed
Merge remote-tracking branch 'origin/main' into our-self-host
# Conflicts: # README.md
2 parents 0a9dd8c + 09b25f5 commit 2d8127c

File tree

9 files changed

+212
-54
lines changed

9 files changed

+212
-54
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Microsoft.AspNetCore.Razor.TagHelpers;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace Our.Umbraco.TagHelpers.Tests.Helpers
9+
{
10+
internal static class TestContextHelpers
11+
{
12+
public static TagHelperContext GetTagHelperContext(string id = "testid")
13+
{
14+
return new TagHelperContext(
15+
tagName: "p",
16+
allAttributes: new TagHelperAttributeList(),
17+
items: new Dictionary<object, object>(),
18+
uniqueId: id);
19+
}
20+
21+
public static TagHelperOutput GetTagHelperOutput(
22+
string tagName = "p",
23+
TagHelperAttributeList attributes = null,
24+
string childContent = "some child content")
25+
{
26+
attributes ??= new TagHelperAttributeList();
27+
28+
return new TagHelperOutput(
29+
tagName,
30+
attributes,
31+
getChildContentAsync: (useCachedResult, encoder) =>
32+
{
33+
var tagHelperContent = new DefaultTagHelperContent();
34+
var content = tagHelperContent.SetHtmlContent(childContent);
35+
return Task.FromResult(content);
36+
});
37+
}
38+
}
39+
}
Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using Microsoft.AspNetCore.Razor.TagHelpers;
22
using NUnit.Framework;
3-
using System;
4-
using System.Collections.Generic;
5-
using Our.Umbraco.TagHelpers;
63
using System.Threading.Tasks;
4+
using Our.Umbraco.TagHelpers.Tests.Helpers;
75

86
namespace Our.Umbraco.TagHelpers.Tests
97
{
@@ -15,8 +13,8 @@ public async Task Given_Predicate_Return_Contents_Or_Empty(bool predicate, strin
1513
{
1614
// Arrange
1715
var id = "unique-id";
18-
var tagHelperContext = GetTagHelperContext(id);
19-
var tagHelperOutput = GetTagHelperOutput(
16+
var tagHelperContext = TestContextHelpers.GetTagHelperContext(id);
17+
var tagHelperOutput = TestContextHelpers.GetTagHelperOutput(
2018
attributes: new TagHelperAttributeList(),
2119
childContent: childContent);
2220
tagHelperOutput.Content.SetContent(childContent);
@@ -31,32 +29,5 @@ public async Task Given_Predicate_Return_Contents_Or_Empty(bool predicate, strin
3129
// Assert
3230
Assert.AreEqual(expected, content);
3331
}
34-
35-
private static TagHelperContext GetTagHelperContext(string id = "testid")
36-
{
37-
return new TagHelperContext(
38-
tagName: "p",
39-
allAttributes: new TagHelperAttributeList(),
40-
items: new Dictionary<object, object>(),
41-
uniqueId: id);
42-
}
43-
44-
private static TagHelperOutput GetTagHelperOutput(
45-
string tagName = "p",
46-
TagHelperAttributeList attributes = null,
47-
string childContent = "some child content")
48-
{
49-
attributes = attributes ?? new TagHelperAttributeList { { "attr", "value" } };
50-
51-
return new TagHelperOutput(
52-
tagName,
53-
attributes,
54-
getChildContentAsync: (useCachedResult, encoder) =>
55-
{
56-
var tagHelperContent = new DefaultTagHelperContent();
57-
var content = tagHelperContent.SetHtmlContent(childContent);
58-
return Task.FromResult<TagHelperContent>(content);
59-
});
60-
}
6132
}
6233
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System.IO;
2+
using System.Text.Encodings.Web;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Razor.TagHelpers;
5+
using NUnit.Framework;
6+
using Our.Umbraco.TagHelpers.Tests.Helpers;
7+
using Umbraco.Cms.Core.Models;
8+
9+
namespace Our.Umbraco.TagHelpers.Tests
10+
{
11+
public class LinkTagHelperTests
12+
{
13+
private static readonly Link _onSiteLink = new() { Url = "/", Name = "example" };
14+
15+
private static readonly Link _externalLink = new() { Url = "/", Name = "example", Target = "_blank", Type = LinkType.External };
16+
17+
private TagHelperContext _tagHelperContext;
18+
19+
[SetUp]
20+
public void SetUp()
21+
{
22+
_tagHelperContext = TestContextHelpers.GetTagHelperContext("link");
23+
}
24+
25+
[TestCase("", "example")]
26+
[TestCase(null, "example")]
27+
[TestCase("content", "content")]
28+
public async Task Internal_Link_Renders_AnchorAroundContentOrLinkName(string childContent, string expectedContent)
29+
{
30+
var output = TestContextHelpers.GetTagHelperOutput("our-link", childContent: childContent);
31+
output.Content.SetContent(childContent);
32+
33+
LinkTagHelper tagHelper = new() { Link = _onSiteLink };
34+
35+
var markup = await GetMarkupAsync(tagHelper, output);
36+
Assert.AreEqual($"<a href=\"/\">{expectedContent}</a>", markup);
37+
}
38+
39+
[TestCase("", "example")]
40+
[TestCase(null, "example")]
41+
[TestCase("content", "content")]
42+
public async Task External_Link_Renders_AnchorAroundContentOrLinkName(string childContent, string expectedContent)
43+
{
44+
var output = TestContextHelpers.GetTagHelperOutput("our-link", childContent: childContent);
45+
output.Content.SetContent(childContent);
46+
47+
LinkTagHelper tagHelper = new() { Link = _externalLink };
48+
49+
var markup = await GetMarkupAsync(tagHelper, output);
50+
Assert.AreEqual($"<a href=\"/\" target=\"_blank\" rel=\"noopener\">{expectedContent}</a>", markup);
51+
}
52+
53+
[Test]
54+
public async Task NoUrl_WithoutFallback_RendersNothing()
55+
{
56+
var output = TestContextHelpers.GetTagHelperOutput("our-link", childContent: string.Empty);
57+
output.Content.SetContent(string.Empty);
58+
59+
LinkTagHelper tagHelper = new() { Link = new() };
60+
61+
var markup = await GetMarkupAsync(tagHelper, output);
62+
Assert.AreEqual(string.Empty, markup);
63+
}
64+
65+
[Test]
66+
public async Task Null_Link_WithoutFallback_RendersNothing()
67+
{
68+
var output = TestContextHelpers.GetTagHelperOutput("our-link", childContent: string.Empty);
69+
output.Content.SetContent(string.Empty);
70+
71+
LinkTagHelper tagHelper = new() { Link = null };
72+
73+
var markup = await GetMarkupAsync(tagHelper, output);
74+
Assert.AreEqual(string.Empty, markup);
75+
}
76+
77+
[TestCase("", "")]
78+
[TestCase(null, "")]
79+
[TestCase("content", "content")]
80+
public async Task Null_Link_WithFallback_NoElement_RendersContent(string childContent, string expectedContent)
81+
{
82+
var output = TestContextHelpers.GetTagHelperOutput("our-link", childContent: childContent);
83+
output.Content.SetContent(childContent);
84+
85+
LinkTagHelper tagHelper = new() { Link = null, Fallback = true };
86+
87+
var markup = await GetMarkupAsync(tagHelper, output);
88+
Assert.AreEqual(expectedContent, markup);
89+
}
90+
91+
[TestCase("", "")]
92+
[TestCase(null, "")]
93+
[TestCase("content", "<div>content</div>")]
94+
public async Task Null_Link_WithFallback_AndElement_RendersContent(string childContent, string expectedContent)
95+
{
96+
var output = TestContextHelpers.GetTagHelperOutput("our-link", childContent: childContent);
97+
output.Content.SetContent(childContent);
98+
99+
LinkTagHelper tagHelper = new() { Link = null, Fallback = true, FallbackElement = "div" };
100+
101+
var markup = await GetMarkupAsync(tagHelper, output);
102+
Assert.AreEqual(expectedContent, markup);
103+
}
104+
105+
private async Task<string> GetMarkupAsync(LinkTagHelper tagHelper, TagHelperOutput output)
106+
{
107+
await tagHelper.ProcessAsync(_tagHelperContext, output);
108+
109+
using var txtWriter = new StringWriter();
110+
output.WriteTo(txtWriter, HtmlEncoder.Default);
111+
return txtWriter.ToString();
112+
}
113+
}
114+
}

Our.Umbraco.TagHelpers.Tests/Our.Umbraco.TagHelpers.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net5.0</TargetFramework>

Our.Umbraco.TagHelpers/ImgTagHelper.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,17 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
180180
height = (originalHeight / originalWidth) * width;
181181
}
182182

183-
#region Autogenerate alt text
183+
#region Autogenerate alt text if unspecfied
184184
if (string.IsNullOrWhiteSpace(ImgAlt))
185185
{
186186
output.Attributes.Add("alt", GetImageAltText(MediaItem));
187187
}
188+
else
189+
{
190+
output.Attributes.Add("alt", ImgAlt);
191+
}
188192
#endregion
193+
189194
#endregion
190195
}
191196
else if (!string.IsNullOrEmpty(FileSource))
@@ -196,11 +201,15 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
196201

197202
imgSrc = AddQueryToUrl(FileSource, "width", width.ToString());
198203

199-
#region Autogenerate alt text
204+
#region Autogenerate alt text if unspecfied
200205
if (string.IsNullOrWhiteSpace(ImgAlt))
201206
{
202207
output.Attributes.Add("alt", GetImageAltText(FileSource));
203208
}
209+
else
210+
{
211+
output.Attributes.Add("alt", ImgAlt);
212+
}
204213
#endregion
205214

206215
#region If width & height are not defined then return a basic <img> with just a src, alt & class (if provided)

Our.Umbraco.TagHelpers/LinkTagHelper.cs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,42 @@ public class LinkTagHelper : TagHelper
1010
[HtmlAttributeName("Link")]
1111
public Link? Link { get; set; }
1212

13+
/// <summary>
14+
/// If the link should render child content even if there is no link
15+
/// </summary>
16+
[HtmlAttributeName("Fallback")]
17+
public bool Fallback { get; set; }
18+
19+
/// <summary>
20+
/// element to replace the anchor with when there is no link i.e div
21+
/// </summary>
22+
[HtmlAttributeName("FallbackElement")]
23+
public string? FallbackElement { get; set; }
24+
1325
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
1426
{
15-
// Ensure we have a Url set on the LinkPicker property editor in Umbraco
16-
if (string.IsNullOrWhiteSpace(Link?.Url))
17-
{
18-
output.SuppressOutput();
19-
return;
20-
}
21-
2227
// If the <our-link /> is self closing
2328
// Ensure that our <a></a> always has a matching end tag
2429
output.TagMode = TagMode.StartTagAndEndTag;
25-
2630
output.TagName = "a";
31+
2732
var childContent = await output.GetChildContentAsync();
2833

34+
// Ensure we have a Url set on the LinkPicker property editor in Umbraco
35+
if (string.IsNullOrWhiteSpace(Link?.Url))
36+
{
37+
if (Fallback && !childContent.IsEmptyOrWhiteSpace)
38+
{
39+
output.TagName = FallbackElement;
40+
}
41+
else
42+
{
43+
output.SuppressOutput();
44+
}
45+
46+
return;
47+
}
48+
2949
// If we use the TagHelper <umb-link></umb-link>
3050
// Without child DOM elements then it will use the Link Name property inside the <a> it generates
3151
if (childContent.IsEmptyOrWhiteSpace)

Our.Umbraco.TagHelpers/Services/BackofficeUserAccessor.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Microsoft.AspNetCore.Authentication;
2-
using Microsoft.AspNetCore.Authentication.Cookies;
1+
using Microsoft.AspNetCore.Authentication.Cookies;
32
using Microsoft.AspNetCore.Http;
43
using Microsoft.Extensions.Options;
54
using System.Security.Claims;
@@ -26,6 +25,11 @@ public BackofficeUserAccessor(
2625
_httpContextAccessor = httpContextAccessor;
2726
}
2827

28+
29+
/// <summary>
30+
/// Updated to use ChunkingCookieManager as per Sean Maloney's answer on our.umbraco.com
31+
/// https://our.umbraco.com/forum/umbraco-9/106857-how-do-i-determine-if-a-backoffice-user-is-logged-in-from-a-razor-view#comment-341847
32+
/// </summary>
2933
public ClaimsIdentity BackofficeUser
3034
{
3135
get
@@ -35,16 +39,17 @@ public ClaimsIdentity BackofficeUser
3539
if (httpContext == null)
3640
return new ClaimsIdentity();
3741

38-
CookieAuthenticationOptions cookieOptions = _cookieOptionsSnapshot.Get(global::Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
39-
string? backOfficeCookie = httpContext.Request.Cookies[cookieOptions.Cookie.Name!];
42+
var cookieOptions = _cookieOptionsSnapshot.Get(global::Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
43+
var cookieManager = new ChunkingCookieManager();
44+
var backOfficeCookie = cookieManager.GetRequestCookie(httpContext, cookieOptions.Cookie.Name!);
4045

4146
if (string.IsNullOrEmpty(backOfficeCookie))
4247
return new ClaimsIdentity();
4348

44-
AuthenticationTicket? unprotected = cookieOptions.TicketDataFormat.Unprotect(backOfficeCookie!);
45-
ClaimsIdentity backOfficeIdentity = unprotected!.Principal.GetUmbracoIdentity();
49+
var unprotected = cookieOptions.TicketDataFormat.Unprotect(backOfficeCookie!);
50+
var backOfficeIdentity = unprotected?.Principal.GetUmbracoIdentity();
4651

47-
return backOfficeIdentity;
52+
return backOfficeIdentity ?? new ClaimsIdentity();
4853
}
4954
}
5055
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using Microsoft.AspNetCore.Mvc.TagHelpers.Cache;
2-
using System.Collections.Generic;
2+
using System.Collections.Concurrent;
33

44
namespace Our.Umbraco.TagHelpers.Services
55
{
66
public interface IUmbracoTagHelperCacheKeys
77
{
8-
Dictionary<string, CacheTagKey> CacheKeys { get; }
8+
ConcurrentDictionary<string, CacheTagKey> CacheKeys { get; }
99
}
1010
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Microsoft.AspNetCore.Mvc.TagHelpers.Cache;
2-
using System.Collections.Generic;
2+
using System.Collections.Concurrent;
33

44
namespace Our.Umbraco.TagHelpers.Services
55
{
@@ -9,6 +9,6 @@ namespace Our.Umbraco.TagHelpers.Services
99
/// </summary>
1010
public class UmbracoTagHelperCacheKeys : IUmbracoTagHelperCacheKeys
1111
{
12-
public Dictionary<string,CacheTagKey> CacheKeys { get; } = new Dictionary<string,CacheTagKey>();
12+
public ConcurrentDictionary<string,CacheTagKey> CacheKeys { get; } = new ConcurrentDictionary<string,CacheTagKey>();
1313
}
1414
}

0 commit comments

Comments
 (0)