Browse Source

Confirm text for subscriptions and fixes to formatter.

pull/502/head
Sebastian 6 years ago
parent
commit
8b4e5cf765
  1. 25
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs
  2. 4
      backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/ConfigAppLimitsPlan.cs
  3. 4
      backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/IAppLimitsPlan.cs
  4. 10
      backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlanDto.cs
  5. 20
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  6. 12
      frontend/app/features/settings/pages/plans/plan.component.html
  7. 28
      frontend/app/shared/services/plans.service.spec.ts
  8. 4
      frontend/app/shared/services/plans.service.ts
  9. 4
      frontend/app/shared/state/plans.state.spec.ts

25
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs

@ -12,12 +12,15 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Security;
using Squidex.Shared.Identity;
using Squidex.Shared.Users; using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Core.HandleRules namespace Squidex.Domain.Apps.Core.HandleRules
@ -27,8 +30,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules
private const string Fallback = "null"; private const string Fallback = "null";
private const string ScriptSuffix = ")"; private const string ScriptSuffix = ")";
private const string ScriptPrefix = "Script("; private const string ScriptPrefix = "Script(";
private static readonly Regex RegexPatternOld = new Regex(@"^(?<Type>[^_]*)_(?<Path>.*)", RegexOptions.Compiled); private static readonly Regex RegexPatternOld = new Regex(@"^(?<Type>[^_]*)_(?<Path>[^\s]*)", RegexOptions.Compiled);
private static readonly Regex RegexPatternNew = new Regex(@"^\{(?<Type>[^_]*)_(?<Path>.*)\}", RegexOptions.Compiled); private static readonly Regex RegexPatternNew = new Regex(@"^\{(?<Type>[^_]*)_(?<Path>[^\s]*)\}", RegexOptions.Compiled);
private readonly List<(char[] Pattern, Func<EnrichedEvent, string?> Replacer)> patterns = new List<(char[] Pattern, Func<EnrichedEvent, string?> Replacer)>(); private readonly List<(char[] Pattern, Func<EnrichedEvent, string?> Replacer)> patterns = new List<(char[] Pattern, Func<EnrichedEvent, string?> Replacer)>();
private readonly IJsonSerializer jsonSerializer; private readonly IJsonSerializer jsonSerializer;
private readonly IUrlGenerator urlGenerator; private readonly IUrlGenerator urlGenerator;
@ -324,6 +327,24 @@ namespace Squidex.Domain.Apps.Core.HandleRules
} }
else if (current != null) else if (current != null)
{ {
if (current is IUser user)
{
var type = segment;
if (string.Equals(type, "Name", StringComparison.OrdinalIgnoreCase))
{
type = SquidexClaimTypes.DisplayName;
}
var claim = user.Claims.FirstOrDefault(x => string.Equals(x.Type, type, StringComparison.OrdinalIgnoreCase));
if (claim != null)
{
current = claim.Value;
continue;
}
}
const BindingFlags bindingFlags = const BindingFlags bindingFlags =
BindingFlags.FlattenHierarchy | BindingFlags.FlattenHierarchy |
BindingFlags.Public | BindingFlags.Public |

4
backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/ConfigAppLimitsPlan.cs

@ -15,10 +15,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans
public string Costs { get; set; } public string Costs { get; set; }
public string? ConfirmText { get; set; }
public string? YearlyCosts { get; set; } public string? YearlyCosts { get; set; }
public string? YearlyId { get; set; } public string? YearlyId { get; set; }
public string? YearlyConfirmText { get; set; }
public long BlockingApiCalls { get; set; } public long BlockingApiCalls { get; set; }
public long MaxApiCalls { get; set; } public long MaxApiCalls { get; set; }

4
backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/IAppLimitsPlan.cs

@ -15,10 +15,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans
string Costs { get; } string Costs { get; }
string? ConfirmText { get; }
string? YearlyCosts { get; } string? YearlyCosts { get; }
string? YearlyId { get; } string? YearlyId { get; }
string? YearlyConfirmText { get; }
long BlockingApiCalls { get; } long BlockingApiCalls { get; }
long MaxApiCalls { get; } long MaxApiCalls { get; }

10
backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlanDto.cs

@ -31,6 +31,16 @@ namespace Squidex.Areas.Api.Controllers.Plans.Models
[Required] [Required]
public string Costs { get; set; } public string Costs { get; set; }
/// <summary>
/// An optional confirm text for the monthly subscription.
/// </summary>
public string? ConfirmText { get; set; }
/// <summary>
/// An optional confirm text for the yearly subscription.
/// </summary>
public string? YearlyConfirmText { get; set; }
/// <summary> /// <summary>
/// The yearly costs of the plan. /// The yearly costs of the plan.
/// </summary> /// </summary>

20
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs

@ -97,6 +97,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
[Theory] [Theory]
[InlineData("Name $APP_NAME has id $APP_ID")] [InlineData("Name $APP_NAME has id $APP_ID")]
[InlineData("Name ${$EVENT_APPID.NAME} has id ${EVENT_APPID.ID}")]
[InlineData("Script(`Name ${event.appId.name} has id ${event.appId.id}`)")] [InlineData("Script(`Name ${event.appId.name} has id ${event.appId.id}`)")]
public void Should_format_app_information_from_event(string script) public void Should_format_app_information_from_event(string script)
{ {
@ -120,19 +121,32 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
} }
[Theory] [Theory]
[InlineData("Date: $TIMESTAMP_DATE, Full: $TIMESTAMP_DATETIME")] [InlineData("Full: $TIMESTAMP_DATETIME")]
[InlineData("Script(`Date: ${formatDate(event.timestamp, 'yyyy-MM-dd')}, Full: ${formatDate(event.timestamp, 'yyyy-MM-dd-hh-mm-ss')}`)")] [InlineData("Script(`Full: ${formatDate(event.timestamp, 'yyyy-MM-dd-hh-mm-ss')}`)")]
public void Should_format_timestamp_information_from_event(string script) public void Should_format_timestamp_information_from_event(string script)
{ {
var @event = new EnrichedContentEvent { Timestamp = now }; var @event = new EnrichedContentEvent { Timestamp = now };
var result = sut.Format(script, @event); var result = sut.Format(script, @event);
Assert.Equal($"Date: {now:yyyy-MM-dd}, Full: {now:yyyy-MM-dd-hh-mm-ss}", result); Assert.Equal($"Full: {now:yyyy-MM-dd-hh-mm-ss}", result);
}
[Theory]
[InlineData("Date: $TIMESTAMP_DATE")]
[InlineData("Script(`Date: ${formatDate(event.timestamp, 'yyyy-MM-dd')}`)")]
public void Should_format_timestamp_date_information_from_event(string script)
{
var @event = new EnrichedContentEvent { Timestamp = now };
var result = sut.Format(script, @event);
Assert.Equal($"Date: {now:yyyy-MM-dd}", result);
} }
[Theory] [Theory]
[InlineData("From $MENTIONED_NAME ($MENTIONED_EMAIL, $MENTIONED_ID)")] [InlineData("From $MENTIONED_NAME ($MENTIONED_EMAIL, $MENTIONED_ID)")]
[InlineData("From ${COMMENT_MENTIONEDUSER.NAME} (${COMMENT_MENTIONEDUSER.EMAIL}, ${COMMENT_MENTIONEDUSER.ID})")]
[InlineData("Script(`From ${event.mentionedUser.name} (${event.mentionedUser.email}, ${event.mentionedUser.id})`)")] [InlineData("Script(`From ${event.mentionedUser.name} (${event.mentionedUser.email}, ${event.mentionedUser.id})`)")]
public void Should_format_email_and_display_name_from_mentioned_user(string script) public void Should_format_email_and_display_name_from_mentioned_user(string script)
{ {

12
frontend/app/features/settings/pages/plans/plan.component.html

@ -22,7 +22,11 @@
&#10003; Selected &#10003; Selected
</button> </button>
<button *ngIf="!planInfo.isSelected" class="btn btn-block btn-success" [disabled]="plansState.isDisabled | async" (click)="changeMonthly()"> <button *ngIf="!planInfo.isSelected" class="btn btn-block btn-success" [disabled]="plansState.isDisabled | async"
(sqxConfirmClick)="changeMonthly()"
[confirmTitle]="'Change subscription'"
[confirmText]="planInfo.plan.confirmText"
[confirmRequired]="planInfo.plan.confirmText">
Change Change
</button> </button>
</div> </div>
@ -37,7 +41,11 @@
&#10003; Selected &#10003; Selected
</button> </button>
<button *ngIf="!planInfo.isYearlySelected" class="btn btn-block btn-success" [disabled]="plansState.isDisabled | async" (click)="changeYearly()"> <button *ngIf="!planInfo.isYearlySelected" class="btn btn-block btn-success" [disabled]="plansState.isDisabled | async"
(sqxConfirmClick)="changeYearly()"
[confirmTitle]="'Change subscription'"
[confirmText]="planInfo.plan.yearlyConfirmText"
[confirmRequired]="planInfo.plan.yearlyConfirmText">
Change Change
</button> </button>
</div> </div>

28
frontend/app/shared/services/plans.service.spec.ts

@ -60,18 +60,22 @@ describe('PlansService', () => {
id: 'free', id: 'free',
name: 'Free', name: 'Free',
costs: '14 €', costs: '14 €',
confirmText: 'Change for 14 € per month?',
yearlyId: 'free_yearly', yearlyId: 'free_yearly',
yearlyCosts: '12 €', yearlyCosts: '120 €',
yearlyConfirmText: 'Change for 120 € per year?',
maxApiCalls: 1000, maxApiCalls: 1000,
maxAssetSize: 1500, maxAssetSize: 1500,
maxContributors: 2500 maxContributors: 2500
}, },
{ {
id: 'prof', id: 'professional',
name: 'Prof', name: 'Professional',
costs: '18 €', costs: '18 €',
yearlyId: 'prof_yearly', confirmText: 'Change for 18 € per month?',
yearlyCosts: '16 €', yearlyId: 'professional_yearly',
yearlyCosts: '160 €',
yearlyConfirmText: 'Change for 160 € per year?',
maxApiCalls: 4000, maxApiCalls: 4000,
maxAssetSize: 5500, maxAssetSize: 5500,
maxContributors: 6500 maxContributors: 6500
@ -89,8 +93,18 @@ describe('PlansService', () => {
currentPlanId: '123', currentPlanId: '123',
planOwner: '456', planOwner: '456',
plans: [ plans: [
new PlanDto('free', 'Free', '14 €', 'free_yearly', '12 €', 1000, 1500, 2500), new PlanDto(
new PlanDto('prof', 'Prof', '18 €', 'prof_yearly', '16 €', 4000, 5500, 6500) 'free', 'Free', '14 €',
'Change for 14 € per month?',
'free_yearly', '120 €',
'Change for 120 € per year?',
1000, 1500, 2500),
new PlanDto(
'professional', 'Professional', '18 €',
'Change for 18 € per month?',
'professional_yearly', '160 €',
'Change for 160 € per year?',
4000, 5500, 6500)
], ],
hasPortal: true hasPortal: true
}, },

4
frontend/app/shared/services/plans.service.ts

@ -32,8 +32,10 @@ export class PlanDto {
public readonly id: string, public readonly id: string,
public readonly name: string, public readonly name: string,
public readonly costs: string, public readonly costs: string,
public readonly confirmText: string | undefined,
public readonly yearlyId: string, public readonly yearlyId: string,
public readonly yearlyCosts: string, public readonly yearlyCosts: string,
public readonly yearlyConfirmText: string | undefined,
public readonly maxApiCalls: number, public readonly maxApiCalls: number,
public readonly maxAssetSize: number, public readonly maxAssetSize: number,
public readonly maxContributors: number public readonly maxContributors: number
@ -75,8 +77,10 @@ export class PlansService {
item.id, item.id,
item.name, item.name,
item.costs, item.costs,
item.confirmText,
item.yearlyId, item.yearlyId,
item.yearlyCosts, item.yearlyCosts,
item.yearlyConfirmText,
item.maxApiCalls, item.maxApiCalls,
item.maxAssetSize, item.maxAssetSize,
item.maxContributors)), item.maxContributors)),

4
frontend/app/shared/state/plans.state.spec.ts

@ -33,8 +33,8 @@ describe('PlansState', () => {
currentPlanId: 'id1', currentPlanId: 'id1',
planOwner: creator, planOwner: creator,
plans: [ plans: [
new PlanDto('id1', 'name1', '100€', 'id1_yearly', '200€', 1, 1, 1), new PlanDto('id1', 'name1', '100€', undefined, 'id1_yearly', '200€', undefined, 1, 1, 1),
new PlanDto('id2', 'name2', '400€', 'id2_yearly', '800€', 2, 2, 2) new PlanDto('id2', 'name2', '400€', undefined, 'id2_yearly', '800€', undefined, 2, 2, 2)
], ],
hasPortal: true hasPortal: true
}; };

Loading…
Cancel
Save