diff --git a/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagLiquidParserTag.cs b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagLiquidParserTag.cs new file mode 100644 index 0000000..303f796 --- /dev/null +++ b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagLiquidParserTag.cs @@ -0,0 +1,50 @@ +using Fluid; +using Fluid.Ast; +using Fluid.Values; +using Lombiq.HelpfulLibraries.OrchardCore.Liquid; +using OrchardCore.DisplayManagement.Liquid.Tags; +using System.Collections.Generic; +using System.IO; +using System.Text.Encodings.Web; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.GoogleTag; + +public class GoogleTagLiquidParserTag : ILiquidParserTag +{ + public async ValueTask WriteToAsync( + IReadOnlyList argumentsList, + TextWriter writer, + TextEncoder encoder, + TemplateContext context) + { + var arguments = new List + { + new(null, new LiteralExpression(new StringValue(GoogleTagViewModel.ShapeType))), + }; + + foreach (var argument in argumentsList) + { + if (argument.Name == "property_id") + { + await AddStringAsync(arguments, nameof(GoogleTagViewModel.GoogleTagPropertyId), argument, context); + } + else if (argument.Name == "cookie_domain") + { + await AddStringAsync(arguments, nameof(GoogleTagViewModel.CookieDomain), argument, context); + } + } + + return await ShapeTag.WriteToAsync(arguments, writer, encoder, context); + } + + private static async Task AddStringAsync( + List arguments, + string newName, + FilterArgument argument, + TemplateContext context) + { + var newValue = await argument.Expression.EvaluateAsync(context); + arguments.Add(new(newName, new LiteralExpression(newValue))); + } +} diff --git a/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagTagHelper.cs b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagTagHelper.cs new file mode 100644 index 0000000..366e869 --- /dev/null +++ b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagTagHelper.cs @@ -0,0 +1,26 @@ +using Lombiq.HelpfulLibraries.OrchardCore.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; +using OrchardCore.DisplayManagement; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.GoogleTag; + +[HtmlTargetElement("google-tag")] +public class GoogleTagTagHelper : ShapeTagHelperBase +{ + [HtmlAttributeName("property-id")] + public string PropertyId { get; set; } + + [HtmlAttributeName("cookie-domain")] + public string CookieDomain { get; set; } + + public GoogleTagTagHelper(IDisplayHelper displayHelper, IShapeFactory shapeFactory) + : base(displayHelper, shapeFactory) + { + } + + protected override string ShapeType => GoogleTagViewModel.ShapeType; + + protected override ValueTask GetViewModelAsync(TagHelperContext context, TagHelperOutput output) => + ValueTask.FromResult(new GoogleTagViewModel(PropertyId, CookieDomain)); +} diff --git a/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagViewModel.cs b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagViewModel.cs new file mode 100644 index 0000000..8064e1c --- /dev/null +++ b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/GoogleTagViewModel.cs @@ -0,0 +1,20 @@ +using OrchardCore.DisplayManagement.Views; + +namespace Lombiq.HelpfulExtensions.Extensions.GoogleTag; + +public class GoogleTagViewModel : ShapeViewModel +{ + public const string ShapeType = "GoogleTag"; + + public string GoogleTagPropertyId { get; set; } + public string CookieDomain { get; set; } + + public GoogleTagViewModel() => Metadata.Type = ShapeType; + + public GoogleTagViewModel(string googleTagPropertyId, string cookieDomain) + : this() + { + GoogleTagPropertyId = googleTagPropertyId; + CookieDomain = cookieDomain; + } +} diff --git a/Lombiq.HelpfulExtensions/Extensions/GoogleTag/Startup.cs b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/Startup.cs new file mode 100644 index 0000000..2f16dd9 --- /dev/null +++ b/Lombiq.HelpfulExtensions/Extensions/GoogleTag/Startup.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Modules; + +namespace Lombiq.HelpfulExtensions.Extensions.GoogleTag; + +[Feature(FeatureIds.GoogleTag)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddTagHelpers(); + services.AddLiquidParserTag("google_tag"); + } +} diff --git a/Lombiq.HelpfulExtensions/FeatureIds.cs b/Lombiq.HelpfulExtensions/FeatureIds.cs index 3cd3b3e..180370d 100644 --- a/Lombiq.HelpfulExtensions/FeatureIds.cs +++ b/Lombiq.HelpfulExtensions/FeatureIds.cs @@ -19,4 +19,5 @@ public static class FeatureIds public const string Workflows = FeatureIdPrefix + nameof(Workflows); public const string Trumbowyg = FeatureIdPrefix + nameof(Trumbowyg); public const string ResetPasswordActivity = Workflows + "." + nameof(ResetPasswordActivity); + public const string GoogleTag = FeatureIdPrefix + nameof(GoogleTag); } diff --git a/Lombiq.HelpfulExtensions/Lombiq.HelpfulExtensions.csproj b/Lombiq.HelpfulExtensions/Lombiq.HelpfulExtensions.csproj index c1c19ae..a125f0f 100644 --- a/Lombiq.HelpfulExtensions/Lombiq.HelpfulExtensions.csproj +++ b/Lombiq.HelpfulExtensions/Lombiq.HelpfulExtensions.csproj @@ -35,18 +35,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/Lombiq.HelpfulExtensions/Manifest.cs b/Lombiq.HelpfulExtensions/Manifest.cs index 4aaf914..265e8f3 100644 --- a/Lombiq.HelpfulExtensions/Manifest.cs +++ b/Lombiq.HelpfulExtensions/Manifest.cs @@ -146,3 +146,10 @@ Category = "Content", Description = "Adds option for inserting code snippets in Trumbowyg editor." )] + +[assembly: Feature( + Id = GoogleTag, + Name = "Lombiq Helpful Extensions - Google Tag", + Category = "Content", + Description = "Adds a shape along with Razor and Liquid tag helpers for Google Analytics." +)] diff --git a/Lombiq.HelpfulExtensions/Views/GoogleTag.cshtml b/Lombiq.HelpfulExtensions/Views/GoogleTag.cshtml new file mode 100644 index 0000000..2d59261 --- /dev/null +++ b/Lombiq.HelpfulExtensions/Views/GoogleTag.cshtml @@ -0,0 +1,38 @@ +@model dynamic + +@using Lombiq.HelpfulLibraries.OrchardCore.Security +@using Microsoft.AspNetCore.Http.Features; +@using Microsoft.Extensions.Hosting; +@using Lombiq.HelpfulExtensions.Extensions.GoogleTag + +@inject IHostEnvironment HostEnvironment + +@{ + var trackingConsentFeature = ViewContext.HttpContext.Features.Get(); +} + +@if (HostEnvironment.IsProduction() && trackingConsentFeature is not { CanTrack: false }) +{ + GoogleAnalyticsContentSecurityPolicyProvider.EnableForCurrentRequest(Context); + + var viewModel = Model as GoogleTagViewModel ?? new GoogleTagViewModel + { + GoogleTagPropertyId = Model.GoogleTagPropertyId, + CookieDomain = Model.CookieDomain, + }; + var cookieDomain = string.Empty; + + if (!string.IsNullOrEmpty(viewModel.CookieDomain)) + { + cookieDomain = $", {{'cookie_domain': '{viewModel.CookieDomain}' }}"; + } + + + +} diff --git a/Readme.md b/Readme.md index 66a3992..979ace5 100644 --- a/Readme.md +++ b/Readme.md @@ -222,6 +222,12 @@ builder.WhenContentTypeEditor("BlogPost").RegisterFootScript(Lombiq.HelpfulExten builder.WhenContentTypeEditor("BlogPost").RegisterStylesheet(Lombiq.HelpfulExtensions.Constants.ResourceNames.TrumbowygHighlight); ``` +### Google Tag + +Adds a shape along with Razor and Liquid tag helpers for Google Analytics, using . + +You can use the `` Razor tag helper in _cshtml_ files or the `{% google_tag property_id: "...", cookie_domain: "auto" %}` parser tag in Liquid. + ## Contributing and support Bug reports, feature requests, comments, questions, code contributions and love letters are warmly welcome. You can send them to us via GitHub issues and pull requests. Please adhere to our [open-source guidelines](https://lombiq.com/open-source-guidelines) while doing so.