diff --git a/BTCPayServer.Common/TextTemplate.cs b/BTCPayServer.Common/TextTemplate.cs index faddf8408..11f5e33be 100644 --- a/BTCPayServer.Common/TextTemplate.cs +++ b/BTCPayServer.Common/TextTemplate.cs @@ -10,6 +10,9 @@ namespace BTCPayServer; public class TextTemplate(string template) { static readonly Regex _interpolationRegex = new Regex(@"\{([^}]+)\}", RegexOptions.Compiled | RegexOptions.CultureInvariant); + public Func NotFoundReplacement { get; set; } = path => $"[NotFound({path})]"; + public Func ParsingErrorReplacement { get; set; } = path => $"[ParsingError({path})]"; + public string Render(JObject model) { model = (JObject)ToLowerCase(model); @@ -23,11 +26,11 @@ public class TextTemplate(string template) try { var token = model.SelectToken(path); - return token?.ToString() ?? $""; + return token?.ToString() ?? NotFoundReplacement(initial); } catch { - return $""; + return ParsingErrorReplacement(initial); } }); } diff --git a/BTCPayServer.Data/Data/EmailRuleData.cs b/BTCPayServer.Data/Data/EmailRuleData.cs index cf836672d..76ac04468 100644 --- a/BTCPayServer.Data/Data/EmailRuleData.cs +++ b/BTCPayServer.Data/Data/EmailRuleData.cs @@ -39,6 +39,12 @@ public class EmailRuleData : BaseEntityData [Required] [Column("to")] public string[] To { get; set; } = null!; + [Required] + [Column("cc")] + public string[] CC { get; set; } = null!; + [Required] + [Column("bcc")] + public string[] BCC { get; set; } = null!; [Required] [Column("subject")] diff --git a/BTCPayServer.Data/Migrations/20251107131717_emailccbcc.cs b/BTCPayServer.Data/Migrations/20251107131717_emailccbcc.cs new file mode 100644 index 000000000..3c4566a46 --- /dev/null +++ b/BTCPayServer.Data/Migrations/20251107131717_emailccbcc.cs @@ -0,0 +1,43 @@ +using BTCPayServer.Data; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BTCPayServer.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251107131717_emailccbcc")] + public partial class emailccbcc : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "bcc", + table: "email_rules", + type: "text[]", + nullable: false, + defaultValue: new string[0]); + + migrationBuilder.AddColumn( + name: "cc", + table: "email_rules", + type: "text[]", + nullable: false, + defaultValue: new string[0]); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "bcc", + table: "email_rules"); + + migrationBuilder.DropColumn( + name: "cc", + table: "email_rules"); + } + } +} diff --git a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs index c0f857dab..4ab9fe105 100644 --- a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -280,11 +280,21 @@ namespace BTCPayServer.Migrations .HasColumnName("additional_data") .HasDefaultValueSql("'{}'::jsonb"); + b.Property("BCC") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("bcc"); + b.Property("Body") .IsRequired() .HasColumnType("text") .HasColumnName("body"); + b.Property("CC") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("cc"); + b.Property("Condition") .HasColumnType("text") .HasColumnName("condition"); diff --git a/BTCPayServer.Tests/FastTests.cs b/BTCPayServer.Tests/FastTests.cs index 74934c591..e9674b965 100644 --- a/BTCPayServer.Tests/FastTests.cs +++ b/BTCPayServer.Tests/FastTests.cs @@ -20,6 +20,7 @@ using BTCPayServer.HostedServices; using BTCPayServer.Hosting; using BTCPayServer.JsonConverters; using BTCPayServer.Payments; +using BTCPayServer.Plugins.Emails.Views; using BTCPayServer.Rating; using BTCPayServer.Services; using BTCPayServer.Services.Apps; @@ -2305,6 +2306,16 @@ bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku Assert.Equal("1-of-xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL-xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL-[legacy]", parsedDescriptor.AccountDerivation.ToString()); } + [Fact] + [Trait("FastTest", "FastTest")] + public void CanParseEmailDestination() + { + var vm = new StoreEmailRuleViewModel(); + var actual = vm.AsArray("\"Nicolas, The, Great\" ,{SomeTemplate} ,\"Madd,Test\" "); + string[] expected = ["\"Nicolas, The, Great\" ", "{SomeTemplate}", "\"Madd,Test\" "]; + Assert.Equal(expected, actual); + } + [Fact] [Trait("Altcoins", "Altcoins")] public void CanCalculateCryptoDue2() diff --git a/BTCPayServer/Components/MainNav/Default.cshtml b/BTCPayServer/Components/MainNav/Default.cshtml index 23c874541..5dc3a1b58 100644 --- a/BTCPayServer/Components/MainNav/Default.cshtml +++ b/BTCPayServer/Components/MainNav/Default.cshtml @@ -314,7 +314,7 @@ Roles