diff --git a/Turnstile.cs b/Turnstile.cs index b25b199..c549412 100644 --- a/Turnstile.cs +++ b/Turnstile.cs @@ -9,9 +9,30 @@ namespace TurnstileTag; public interface ITurnstile { + /// + /// Generates the placeholder which renders the turnstile challenge. + /// + public IHtmlContent GetInputHtml(); + + + /// + /// Generates the script tag which loads the turnstile library. + /// + public IHtmlContent GetScriptHtml(); + + /// + /// + /// IHtmlContent GenerateTurnstileFormHtml(ViewContext viewContext); - IHtmlContent GetHtml(); + + /// + /// + /// Task ValidateRequestAsync(HttpContext httpContext); + + /// + /// + /// Task Verify(string value); } @@ -27,6 +48,7 @@ public class Turnstile : ITurnstile } + /// public async Task ValidateRequestAsync(HttpContext httpContext) { ArgumentNullException.ThrowIfNull(httpContext); @@ -61,6 +83,7 @@ public class Turnstile : ITurnstile } + /// public async Task Verify(string value) { using HttpClient http = new(); @@ -74,20 +97,25 @@ public class Turnstile : ITurnstile } - /// - /// Generates the placeholder which renders the turnstile challenge. - /// - public IHtmlContent GetHtml() + /// + public IHtmlContent GetInputHtml() { HtmlContentBuilder builder = new(); builder.AppendHtml($"
"); - builder.AppendHtml($""); - // - //
return builder; } + /// + public IHtmlContent GetScriptHtml() + { + HtmlContentBuilder builder = new(); + builder.AppendHtml($""); + return builder; + } + + + /// public IHtmlContent GenerateTurnstileFormHtml(ViewContext viewContext) { ArgumentNullException.ThrowIfNull(viewContext); @@ -103,7 +131,8 @@ public class Turnstile : ITurnstile formContext.FormData.Add("turnstile", true); } - return GetHtml(); + viewContext.ViewData.TryAdd("turnstile", true); + return GetInputHtml(); } diff --git a/TurnstileAttributeTagHelper.cs b/TurnstileAttributeTagHelper.cs new file mode 100644 index 0000000..e9ab405 --- /dev/null +++ b/TurnstileAttributeTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace TurnstileTag; + +[HtmlTargetElement("form", Attributes = "cf-turnstile")] +public class TurnstileAttributeTagHelper : TagHelper +{ + readonly ITurnstile _turnstile; + + + public TurnstileAttributeTagHelper(ITurnstile turnstile) + { + _turnstile = turnstile; + } + + /// + /// Whether turnstile should be activated. + /// + public bool? CfTurnstile { get; set; } + + /// + /// Gets the of the executing view. + /// + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext? ViewContext { get; set; } + + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (CfTurnstile ?? false) + { + IHtmlContent content = _turnstile.GenerateTurnstileFormHtml(ViewContext!); + if (content != null) + { + output.PostContent.AppendHtml(content); + } + } + } +} \ No newline at end of file diff --git a/TurnstileScriptTagHelper.cs b/TurnstileScriptTagHelper.cs new file mode 100644 index 0000000..ace1308 --- /dev/null +++ b/TurnstileScriptTagHelper.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.Options; + +namespace TurnstileTag; + +[HtmlTargetElement("cf-turnstile-script", TagStructure = TagStructure.NormalOrSelfClosing)] +public class TurnstileScriptTagHelper : TagHelper +{ + /// + /// Force rendering of script tag. + /// Otherwise it is only rendered when a turnstile input was rendered. + /// + public bool Force { get; set; } + + readonly IOptions _options; + + /// + /// Gets the of the executing view. + /// + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext? ViewContext { get; set; } + + + public TurnstileScriptTagHelper(IOptions options) + { + _options = options; + } + + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (Force || (ViewContext != null && ViewContext.ViewData.ContainsKey("turnstile"))) + { + output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "script"; + output.Attributes.Add(new TagHelperAttribute("async")); + output.Attributes.Add(new TagHelperAttribute("defer")); + output.Attributes.Add("src", _options.Value.ApiUrl + "/api.js"); + } + else + { + output.SuppressOutput(); + } + } +} \ No newline at end of file diff --git a/TurnstileTagHelper.cs b/TurnstileTagHelper.cs index da35a76..e533c59 100644 --- a/TurnstileTagHelper.cs +++ b/TurnstileTagHelper.cs @@ -1,24 +1,15 @@ -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.Options; namespace TurnstileTag; -[HtmlTargetElement("form", Attributes = "cf-turnstile")] +[HtmlTargetElement("cf-turnstile-input", TagStructure = TagStructure.NormalOrSelfClosing)] public class TurnstileTagHelper : TagHelper { - readonly ITurnstile _turnstile; + readonly IOptions _options; - public TurnstileTagHelper(ITurnstile turnstile) - { - _turnstile = turnstile; - } - - /// - /// Whether turnstile should be activated. - /// - public bool? CfTurnstile { get; set; } /// /// Gets the of the executing view. @@ -28,15 +19,18 @@ public class TurnstileTagHelper : TagHelper public ViewContext? ViewContext { get; set; } + public TurnstileTagHelper(IOptions options) + { + _options = options; + } + + public override void Process(TagHelperContext context, TagHelperOutput output) { - if (CfTurnstile ?? false) - { - IHtmlContent content = _turnstile.GenerateTurnstileFormHtml(ViewContext!); - if (content != null) - { - output.PostContent.AppendHtml(content); - } - } + output.TagMode = TagMode.StartTagAndEndTag; + output.TagName = "div"; + output.Attributes.Add("class", "cf-turnstile"); + output.Attributes.Add("data-sitekey", _options.Value.PublicKey); + ViewContext!.ViewData.TryAdd("turnstile", true); } } \ No newline at end of file