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