Browse Source

Builder.

pull/312/head
Sebastian Stehle 7 years ago
parent
commit
3153aed816
  1. 34
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs
  2. 27
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs
  3. 27
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs
  4. 70
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs
  5. 19
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/JsonFieldBuilder.cs
  6. 19
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/NumberFieldBuilder.cs
  7. 130
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs
  8. 59
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs
  9. 19
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsBuilder.cs
  10. 161
      src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs
  11. 310
      src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs
  12. 623
      src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs
  13. 17
      src/Squidex.Infrastructure/StringExtensions.cs
  14. 17
      src/Squidex/app/features/apps/pages/apps-page.component.html
  15. 12
      tests/Squidex.Infrastructure.Tests/StringExtensionsTests.cs

34
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs

@ -0,0 +1,34 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class AssetFieldBuilder : FieldBuilder
{
public AssetFieldBuilder(CreateSchemaField field)
: base(field)
{
}
public AssetFieldBuilder MustBeImage()
{
Properties<AssetsFieldProperties>().MustBeImage = true;
return this;
}
public AssetFieldBuilder RequireSingle()
{
Properties<AssetsFieldProperties>().MaxItems = 2;
return this;
}
}
}

27
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs

@ -0,0 +1,27 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class BooleanFieldBuilder : FieldBuilder
{
public BooleanFieldBuilder(CreateSchemaField field)
: base(field)
{
}
public BooleanFieldBuilder AsToggle()
{
Properties<BooleanFieldProperties>().Editor = BooleanFieldEditor.Toggle;
return this;
}
}
}

27
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs

@ -0,0 +1,27 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class DateTimeFieldBuilder : FieldBuilder
{
public DateTimeFieldBuilder(CreateSchemaField field)
: base(field)
{
}
public DateTimeFieldBuilder AsDateTime()
{
Properties<DateTimeFieldProperties>().Editor = DateTimeFieldEditor.DateTime;
return this;
}
}
}

70
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs

@ -0,0 +1,70 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public abstract class FieldBuilder
{
private readonly CreateSchemaField field;
protected T Properties<T>() where T : FieldProperties
{
return field.Properties as T;
}
protected FieldBuilder(CreateSchemaField field)
{
this.field = field;
}
public FieldBuilder Label(string label)
{
field.Properties.Label = label;
return this;
}
public FieldBuilder Hints(string hints)
{
field.Properties.Hints = hints;
return this;
}
public FieldBuilder Localizable()
{
field.Partitioning = Partitioning.Invariant.Key;
return this;
}
public FieldBuilder Disabled()
{
field.IsDisabled = true;
return this;
}
public FieldBuilder Required()
{
field.Properties.IsRequired = true;
return this;
}
public FieldBuilder ShowInList()
{
field.Properties.IsListField = true;
return this;
}
}
}

19
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/JsonFieldBuilder.cs

@ -0,0 +1,19 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class JsonFieldBuilder : FieldBuilder
{
public JsonFieldBuilder(CreateSchemaField field)
: base(field)
{
}
}
}

19
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/NumberFieldBuilder.cs

@ -0,0 +1,19 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class NumberFieldBuilder : FieldBuilder
{
public NumberFieldBuilder(CreateSchemaField field)
: base(field)
{
}
}
}

130
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs

@ -0,0 +1,130 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public sealed class SchemaBuilder
{
private readonly CreateSchema command;
public SchemaBuilder(CreateSchema command)
{
this.command = command;
}
public static SchemaBuilder Create(string name)
{
return new SchemaBuilder(new CreateSchema
{
Name = name.ToKebabCase(),
Publish = true,
Properties = new SchemaProperties
{
Label = name
}
});
}
public SchemaBuilder Singleton()
{
command.Singleton = true;
return this;
}
public SchemaBuilder AddAssets(string name, Action<AssetFieldBuilder> configure)
{
var field = AddField<AssetsFieldProperties>(name);
configure(new AssetFieldBuilder(field));
return this;
}
public SchemaBuilder AddBoolean(string name, Action<BooleanFieldBuilder> configure)
{
var field = AddField<BooleanFieldProperties>(name);
configure(new BooleanFieldBuilder(field));
return this;
}
public SchemaBuilder AddDateTime(string name, Action<DateTimeFieldBuilder> configure)
{
var field = AddField<DateTimeFieldProperties>(name);
configure(new DateTimeFieldBuilder(field));
return this;
}
public SchemaBuilder AddJson(string name, Action<JsonFieldBuilder> configure)
{
var field = AddField<JsonFieldProperties>(name);
configure(new JsonFieldBuilder(field));
return this;
}
public SchemaBuilder AddNumber(string name, Action<NumberFieldBuilder> configure)
{
var field = AddField<NumberFieldProperties>(name);
configure(new NumberFieldBuilder(field));
return this;
}
public SchemaBuilder AddString(string name, Action<StringFieldBuilder> configure)
{
var field = AddField<StringFieldProperties>(name);
configure(new StringFieldBuilder(field));
return this;
}
public SchemaBuilder AddTags(string name, Action<TagsFieldBuilder> configure)
{
var field = AddField<TagsFieldProperties>(name);
configure(new TagsFieldBuilder(field));
return this;
}
private CreateSchemaField AddField<T>(string name) where T : FieldProperties, new()
{
var field = new CreateSchemaField
{
Name = name.ToCamelCase(),
Properties = new T
{
Label = name
}
};
command.Fields = command.Fields ?? new List<CreateSchemaField>();
command.Fields.Add(field);
return field;
}
public CreateSchema Build()
{
return command;
}
}
}

59
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs

@ -0,0 +1,59 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using System.Collections.Immutable;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class StringFieldBuilder : FieldBuilder
{
public StringFieldBuilder(CreateSchemaField field)
: base(field)
{
}
public StringFieldBuilder AsTextArea()
{
Properties<StringFieldProperties>().Editor = StringFieldEditor.TextArea;
return this;
}
public StringFieldBuilder AsRichText()
{
Properties<StringFieldProperties>().Editor = StringFieldEditor.RichText;
return this;
}
public StringFieldBuilder AsDropDown(params string[] values)
{
Properties<StringFieldProperties>().AllowedValues = ImmutableList.Create(values);
Properties<StringFieldProperties>().Editor = StringFieldEditor.Dropdown;
return this;
}
public StringFieldBuilder Pattern(string pattern, string message = null)
{
Properties<StringFieldProperties>().Pattern = pattern;
Properties<StringFieldProperties>().PatternMessage = message;
return this;
}
public StringFieldBuilder Length(int maxLength, int minLength = 0)
{
Properties<StringFieldProperties>().MaxLength = maxLength;
Properties<StringFieldProperties>().MinLength = minLength;
return this;
}
}
}

19
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsBuilder.cs

@ -0,0 +1,19 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Entities.Schemas.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
public class TagsFieldBuilder : FieldBuilder
{
public TagsFieldBuilder(CreateSchemaField field)
: base(field)
{
}
}
}

161
src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs

@ -6,11 +6,10 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Templates.Builders;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
@ -23,8 +22,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private const string TemplateName = "Blog";
private const string SlugScript = @"
var data = ctx.data;
data.slug = { iv: slugify(data.title.iv) };
if (data.title && data.title.iv) {
data.slug = { iv: slugify(data.title.iv) };
}
replace(data);";
@ -103,60 +104,27 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private async Task<NamedId<Guid>> CreatePostsSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "posts",
Publish = true,
Properties = new SchemaProperties
{
Label = "Posts"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "title",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
MaxLength = 100,
MinLength = 0,
Label = "Title"
}
},
new CreateSchemaField
{
Name = "slug",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Slug,
IsRequired = false,
IsListField = true,
MaxLength = 100,
MinLength = 0,
Label = "Slug (Autogenerated)"
},
IsDisabled = true
},
new CreateSchemaField
{
Name = "text",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.RichText,
IsRequired = true,
IsListField = false,
Label = "Text"
}
}
}
};
await publish(command);
var schemaId = NamedId.Of(command.SchemaId, command.Name);
var schema =
SchemaBuilder.Create("Posts")
.AddString("Title", f => f
.Length(100)
.Required()
.ShowInList()
.Hints("The title of the post."))
.AddString("Text", f => f
.AsRichText()
.Length(100)
.Required()
.Hints("The text of the post."))
.AddString("Slug", f => f
.Disabled()
.Label("Slug (Autogenerated)")
.Hints("Autogenerated slug that can be used to identity the post."))
.Build();
await publish(schema);
var schemaId = NamedId.Of(schema.SchemaId, schema.Name);
await publish(new ConfigureScripts
{
@ -170,60 +138,27 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private async Task<NamedId<Guid>> CreatePagesSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "pages",
Publish = true,
Properties = new SchemaProperties
{
Label = "Pages"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "title",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
MaxLength = 100,
MinLength = 0,
Label = "Title"
}
},
new CreateSchemaField
{
Name = "slug",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Slug,
IsRequired = false,
IsListField = true,
MaxLength = 100,
MinLength = 0,
Label = "Slug (Autogenerated)"
},
IsDisabled = true
},
new CreateSchemaField
{
Name = "text",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.RichText,
IsRequired = true,
IsListField = false,
Label = "Text"
}
}
}
};
await publish(command);
var schemaId = NamedId.Of(command.SchemaId, command.Name);
var schema =
SchemaBuilder.Create("Pages")
.AddString("Title", f => f
.Length(100)
.Required()
.ShowInList()
.Hints("The title of the page."))
.AddString("Text", f => f
.AsRichText()
.Length(100)
.Required()
.Hints("The text of the page."))
.AddString("Slug", f => f
.Disabled()
.Label("Slug (Autogenerated)")
.Hints("Autogenerated slug that can be used to identity the page."))
.Build();
await publish(schema);
var schemaId = NamedId.Of(schema.SchemaId, schema.Name);
await publish(new ConfigureScripts
{
@ -235,4 +170,4 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
return schemaId;
}
}
}
}

310
src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs

@ -0,0 +1,310 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Templates.Builders;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Entities.Apps.Templates
{
public sealed class CreateIdentityCommandMiddleware : ICommandMiddleware
{
private const string TemplateName = "Identity";
private const string NormalizeScript = @"
var data = ctx.data;
if (data.userName && data.userName.iv) {
data.normalizedUserName = { iv: data.userName.iv.toUpperCase() };
}
if (data.email && data.email.iv) {
data.normalizedEmail = { iv: data.email.iv.toUpperCase() };
}
replace(data);";
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp))
{
var appId = NamedId.Of(createApp.AppId, createApp.Name);
var publish = new Func<ICommand, Task>(command =>
{
if (command is IAppCommand appCommand)
{
appCommand.AppId = appId;
}
return context.CommandBus.PublishAsync(command);
});
await Task.WhenAll(
CreateApiResourcesSchemaAsync(publish),
CreateAuthenticationSchemeSchemaAsync(publish),
CreateClientSchemaAsync(publish),
CreateIdentityResourcesSchemaAsync(publish),
CreateSettingsSchemaAsync(publish),
CreateUsersSchemaAsync(publish),
CreateClientAsync(publish, appId.Id));
}
await next();
}
private static bool IsRightTemplate(CreateApp createApp)
{
return string.Equals(createApp.Template, TemplateName, StringComparison.OrdinalIgnoreCase);
}
private static async Task CreateClientAsync(Func<ICommand, Task> publish, Guid appId)
{
await publish(new AttachClient { Id = "default", AppId = appId });
}
private async Task<NamedId<Guid>> CreateAuthenticationSchemeSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("Authentication Schemes")
.AddString("Provider", f => f
.AsDropDown("Facebook", "Google", "Microsoft", "Twitter")
.Required()
.ShowInList()
.Hints("The name and type of the provider."))
.AddString("Client Id", f => f
.Required()
.ShowInList()
.Hints("The client id that you must configure at the external provider."))
.AddString("Client Secret", f => f
.Required()
.Hints("The client secret that you must configure at the external provider."))
.AddTags("Scopes", f => f
.Hints("Additional scopes you want from the provider."))
.Build();
await publish(schema);
return NamedId.Of(schema.SchemaId, schema.Name);
}
private Task CreateClientSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("clients")
.AddString("Client Id", f => f
.Required()
.Hints("Unique id of the client."))
.AddString("Client Name", f => f
.Localizable()
.Hints("Client display name (used for logging and consent screen)."))
.AddString("Client Uri", f => f
.Localizable()
.Hints("URI to further information about client (used on consent screen)."))
.AddAssets("Logo", f => f
.MustBeImage()
.Hints("URI to client logo (used on consent screen)."))
.AddTags("Client Secrets", f => f
.Hints("Client secrets - only relevant for flows that require a secret."))
.AddTags("Allowed Scopes", f => f
.Hints("Specifies the api scopes that the client is allowed to request."))
.AddTags("Allowed Grant Types", f => f
.Hints("Specifies the allowed grant types (legal combinations of AuthorizationCode, Implicit, Hybrid, ResourceOwner, ClientCredentials)."))
.AddTags("Redirect Uris", f => f
.Hints("Specifies allowed URIs to return tokens or authorization codes to"))
.AddTags("Post Logout Redirect Uris", f => f
.Hints("Specifies allowed URIs to redirect to after logout."))
.AddTags("Allowed Cors Origins", f => f
.Hints("Gets or sets the allowed CORS origins for JavaScript clients."))
.AddBoolean("Require Consent", f => f
.AsToggle()
.Hints("Specifies whether a consent screen is required."))
.AddBoolean("Allow Offline Access", f => f
.AsToggle()
.Hints("Gets or sets a value indicating whether to allow offline access."))
.Build();
return publish(schema);
}
private Task CreateSettingsSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("Settings").Singleton()
.AddString("Site Name", f => f
.Localizable()
.Hints("The name of your website."))
.AddAssets("Logo", f => f
.MustBeImage()
.Hints("Logo that is rendered in the header."))
.AddString("Footer Text", f => f
.Localizable()
.Hints("The optional footer text."))
.AddString("PrivacyPolicyUrl", f => f
.Localizable()
.Hints("The link to your privacy policies."))
.AddString("LegalUrl", f => f
.Localizable()
.Hints("The link to your legal information."))
.AddString("Email Confirmation Text", f => f
.AsTextArea()
.Localizable()
.Hints("The text for the confirmation email."))
.AddString("Email Confirmation Subject", f => f
.AsTextArea()
.Localizable()
.Hints("The subject for the confirmation email."))
.AddString("Email Password Reset Text", f => f
.AsTextArea()
.Localizable()
.Hints("The text for the password reset email."))
.AddString("Email Password Reset Subject", f => f
.AsTextArea()
.Localizable()
.Hints("The subject for the password reset email."))
.AddString("Terms of Service Url", f => f
.Localizable()
.Hints("The link to your tems of service."))
.AddString("Bootstrap Url", f => f
.Hints("The link to a custom bootstrap theme."))
.AddString("Styles Url", f => f
.Hints("The link to a stylesheet."))
.AddString("SMTP From", f => f
.Hints("The SMTP sender address."))
.AddString("SMTP Server", f => f
.Hints("The smpt server."))
.AddString("SMTP Username", f => f
.Hints("The username for your SMTP server."))
.AddString("SMTP Password", f => f
.Hints("The password for your SMTP server."))
.AddString("Google Analytics Id", f => f
.Hints("The id to your google analytics account."))
.Build();
return publish(schema);
}
private async Task CreateUsersSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("Users")
.AddString("Username", f => f
.Required()
.ShowInList()
.Hints("The unique username to login."))
.AddString("Email", f => f
.Pattern(@"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$", "Must be an email address.")
.Required()
.ShowInList()
.Hints("The unique email to login."))
.AddString("Phone Number", f => f
.Hints("Phone number of the user."))
.AddString("Roles", f => f
.Hints("The roles of the user."))
.AddJson("Claims", f => f
.Hints("The claims of the user."))
.AddBoolean("Email Confirmed", f => f
.AsToggle()
.Hints("Indicates if the email is confirmed."))
.AddBoolean("Phone Number Confirmed", f => f
.AsToggle()
.Hints("Indicates if the phone number is confirmed."))
.AddBoolean("LockoutEnabled", f => f
.AsToggle()
.Hints("Toggle on to lock out the user."))
.AddDateTime("Lockout End Date Utc", f => f
.AsDateTime()
.Disabled()
.Hints("Indicates when the lockout ends."))
.AddJson("Login", f => f
.Disabled()
.Hints("Login information for querying."))
.AddJson("Logins ", f => f
.Disabled()
.Hints("Login information."))
.AddJson("Tokens ", f => f
.Disabled()
.Hints("Login tokens."))
.AddNumber("Access Failed Count", f => f
.Disabled()
.Hints("The number of failed login attempts."))
.AddString("Password Hash", f => f
.Disabled()
.Hints("The hashed password."))
.AddString("Normalized Email", f => f
.Disabled()
.Hints("The normalized email for querying."))
.AddString("Normalized Username", f => f
.Disabled()
.Hints("The normalized user name for querying."))
.AddString("Security Stamp", f => f
.Disabled()
.Hints("Internal security stamp"))
.Build();
await publish(schema);
var schemaId = NamedId.Of(schema.SchemaId, schema.Name);
await publish(new ConfigureScripts
{
SchemaId = schemaId.Id,
ScriptCreate = NormalizeScript,
ScriptUpdate = NormalizeScript
});
}
private Task CreateApiResourcesSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("API Resources")
.AddString("Name", f => f
.Required()
.ShowInList()
.Hints("The unique name of the API."))
.AddString("Display Name", f => f
.Localizable()
.Hints("The display name of the API."))
.AddString("Description", f => f
.Localizable()
.Hints("The description name of the API."))
.AddTags("User Claims", f => f
.Hints("List of accociated user claims that should be included when this resource is requested."))
.Build();
return publish(schema);
}
private Task CreateIdentityResourcesSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("Identity Resources")
.AddString("Name", f => f
.Required()
.ShowInList()
.Hints("The unique name of the identity information."))
.AddString("Display Name", f => f
.Localizable()
.Hints("The display name of the identity information."))
.AddString("Description", f => f
.Localizable()
.Hints("The description name of the identity information."))
.AddTags("User Claims", f => f
.Hints("List of accociated user claims that should be included when this resource is requested."))
.AddBoolean("Required", f => f
.Hints("Specifies whether the user can de-select the scope on the consent screen."))
.Build();
return publish(schema);
}
}
}

623
src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs

@ -6,14 +6,11 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Templates.Builders;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
@ -41,11 +38,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
await Task.WhenAll(
CreateBasicsAsync(publish),
CreateProjectsSchemaAsync(publish),
CreateExperienceSchemaAsync(publish),
CreateSkillsSchemaAsync(publish),
CreateEducationSchemaAsync(publish),
CreateExperienceSchemaAsync(publish),
CreateProjectsSchemaAsync(publish),
CreatePublicationsSchemaAsync(publish),
CreateSkillsSchemaAsync(publish),
CreateClientAsync(publish, appId.Id));
}
@ -86,150 +83,40 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private async Task<NamedId<Guid>> CreateBasicsSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "basics",
Properties = new SchemaProperties
{
Label = "Basics"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "firstName",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "First Name",
Hints = "Your first name"
}
},
new CreateSchemaField
{
Name = "lastName",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Last Name",
Hints = "Your last name"
}
},
new CreateSchemaField
{
Name = "profession",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.TextArea,
IsRequired = true,
IsListField = false,
Label = "Profession",
Hints = "Define your profession"
}
},
new CreateSchemaField
{
Name = "image",
Properties = new AssetsFieldProperties
{
IsRequired = false,
IsListField = false,
MustBeImage = true,
Label = "Image",
Hints = "Your image"
}
},
new CreateSchemaField
{
Name = "summary",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.TextArea,
IsRequired = false,
IsListField = false,
Label = "Summary",
Hints = "Write a short summary about yourself"
}
},
new CreateSchemaField
{
Name = "githubLink",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "Github",
Hints = "An optional link to your Github account"
}
},
new CreateSchemaField
{
Name = "blogLink",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "Blog",
Hints = "An optional link to your blog"
}
},
new CreateSchemaField
{
Name = "twitterLink",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "Twitter",
Hints = "An optional link to your twitter account"
}
},
new CreateSchemaField
{
Name = "linkedInLink",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "LinkedIn",
Hints = "An optional link to your LinkedIn account"
}
},
new CreateSchemaField
{
Name = "emailAddress",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "Email Address",
Hints = "An optional email address to contact you"
}
},
new CreateSchemaField
{
Name = "legalTerms",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.TextArea,
IsRequired = false,
IsListField = false,
Label = "Legal terms",
Hints = "The terms to fulfill legal requirements"
}
}
},
Publish = true
};
var command =
SchemaBuilder.Create("basics")
.AddString("First Name", f => f
.Required()
.ShowInList()
.Hints("Your first name."))
.AddString("Last Name", f => f
.Required()
.ShowInList()
.Hints("Your last name."))
.AddAssets("Image", f => f
.MustBeImage()
.Hints("Your profile image."))
.AddString("Profession", f => f
.AsTextArea()
.Required()
.Hints("Describe your profession."))
.AddString("Summary", f => f
.AsTextArea()
.Hints("Write a short summary about yourself."))
.AddString("Legal Terms", f => f
.AsTextArea()
.Hints("The terms to fulfill legal requirements."))
.AddString("Github Link", f => f
.Hints("An optional link to your Github account."))
.AddString("Blog Link", f => f
.Hints("An optional link to your Blog."))
.AddString("Twitter Link", f => f
.Hints("An optional link to your Twitter account."))
.AddString("LinkedIn Link", f => f
.Hints("An optional link to your LinkedIn account."))
.AddString("Email Address", f => f
.Hints("An optional email address to contact you."))
.Build();
await publish(command);
@ -238,317 +125,104 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private async Task<NamedId<Guid>> CreateProjectsSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "projects",
Properties = new SchemaProperties
{
Label = "Projects"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "name",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Name",
Hints = "The name of the projection"
}
},
new CreateSchemaField
{
Name = "description",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.TextArea,
IsRequired = true,
IsListField = false,
Label = "Description",
Hints = "Describe your project"
}
},
new CreateSchemaField
{
Name = "image",
Properties = new AssetsFieldProperties
{
IsRequired = true,
IsListField = false,
MustBeImage = true,
Label = "Image",
Hints = "An image or screenshot for your project"
}
},
new CreateSchemaField
{
Name = "label",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "Label",
Hints = "An optional label to categorize your project, e.g. 'Open Source'"
}
},
new CreateSchemaField
{
Name = "link",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "link",
Hints = "The logo of the company or organization you worked for"
}
},
new CreateSchemaField
{
Name = "year",
Properties = new NumberFieldProperties
{
IsRequired = false,
IsListField = false,
Label = "Year",
Hints = "The year, when you realized the project, used for sorting only"
}
}
},
Publish = true
};
await publish(command);
return NamedId.Of(command.SchemaId, command.Name);
var schema =
SchemaBuilder.Create("projects")
.AddString("Name", f => f
.Required()
.ShowInList()
.Hints("The name of your project."))
.AddString("Description", f => f
.AsTextArea()
.Required()
.Hints("Describe your project."))
.AddAssets("Image", f => f
.MustBeImage()
.Required()
.Hints("An image or screenshot for your project."))
.AddString("Label", f => f
.AsTextArea()
.Hints("An optional label to categorize your project, e.g. 'Open Source'."))
.AddString("Link", f => f
.Hints("An optional link to your project."))
.AddNumber("Year", f => f
.Hints("The year, when you realized the project, used for sorting only."))
.Build();
await publish(schema);
return NamedId.Of(schema.SchemaId, schema.Name);
}
private async Task<NamedId<Guid>> CreateExperienceSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "experience",
Properties = new SchemaProperties
{
Label = "Experience"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "position",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Position",
Hints = "Your position in this job"
}
},
new CreateSchemaField
{
Name = "company",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Company",
Hints = "The company or organization you worked for"
}
},
new CreateSchemaField
{
Name = "logo",
Properties = new AssetsFieldProperties
{
IsRequired = false,
IsListField = false,
MustBeImage = true,
Label = "Logo",
Hints = "The logo of the company or organization you worked for"
}
},
new CreateSchemaField
{
Name = "from",
Properties = new DateTimeFieldProperties
{
Editor = DateTimeFieldEditor.Date,
IsRequired = true,
IsListField = false,
Label = "Start Date",
Hints = "The start date"
}
},
new CreateSchemaField
{
Name = "to",
Properties = new DateTimeFieldProperties
{
Editor = DateTimeFieldEditor.Date,
IsRequired = false,
IsListField = false,
Label = "End Date",
Hints = "The end date, keep empty if you still work there"
}
}
},
Publish = true
};
await publish(command);
return NamedId.Of(command.SchemaId, command.Name);
var schema =
SchemaBuilder.Create("experience")
.AddString("Position", f => f
.Required()
.ShowInList()
.Hints("Your position in this job."))
.AddString("Company", f => f
.Required()
.ShowInList()
.Hints("The company or organization you worked for."))
.AddAssets("Logo", f => f
.MustBeImage()
.Hints("The logo of the company or organization you worked for."))
.AddDateTime("From", f => f
.Required()
.Hints("The start date."))
.AddDateTime("To", f => f
.Hints("The end date, keep empty if you still work there."))
.Build();
await publish(schema);
return NamedId.Of(schema.SchemaId, schema.Name);
}
private async Task<NamedId<Guid>> CreateEducationSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "education",
Properties = new SchemaProperties
{
Label = "Education"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "degree",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Degree",
Hints = "The degree you got or achieved"
}
},
new CreateSchemaField
{
Name = "school",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "School",
Hints = "The school or university"
}
},
new CreateSchemaField
{
Name = "logo",
Properties = new AssetsFieldProperties
{
IsRequired = false,
IsListField = false,
MustBeImage = true,
Label = "Logo",
Hints = "The logo of the school"
}
},
new CreateSchemaField
{
Name = "from",
Properties = new DateTimeFieldProperties
{
Editor = DateTimeFieldEditor.Date,
IsRequired = true,
IsListField = false,
Label = "Start Date",
Hints = "The start date"
}
},
new CreateSchemaField
{
Name = "to",
Properties = new DateTimeFieldProperties
{
Editor = DateTimeFieldEditor.Date,
IsRequired = false,
IsListField = false,
Label = "End Date",
Hints = "The end date, keep empty if you still study there"
}
}
},
Publish = true
};
await publish(command);
return NamedId.Of(command.SchemaId, command.Name);
var schema =
SchemaBuilder.Create("Experience")
.AddString("Degree", f => f
.Required()
.ShowInList()
.Hints("The degree you got or achieved."))
.AddString("School", f => f
.Required()
.ShowInList()
.Hints("The school or university."))
.AddAssets("Logo", f => f
.MustBeImage()
.Hints("The logo of the school or university."))
.AddDateTime("From", f => f
.Required()
.Hints("The start date."))
.AddDateTime("To", f => f
.Hints("The end date, keep empty if you still study there."))
.Build();
await publish(schema);
return NamedId.Of(schema.SchemaId, schema.Name);
}
private async Task<NamedId<Guid>> CreatePublicationsSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "publications",
Properties = new SchemaProperties
{
Label = "Publications"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "name",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Name",
Hints = "The name or title of your publication"
}
},
new CreateSchemaField
{
Name = "description",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.TextArea,
IsRequired = false,
IsListField = false,
Label = "Description",
Hints = "Describe the content of your publication"
}
},
new CreateSchemaField
{
Name = "cover",
Properties = new AssetsFieldProperties
{
IsRequired = true,
IsListField = false,
MustBeImage = true,
Label = "Cover",
Hints = "The cover of your publication"
}
},
new CreateSchemaField
{
Name = "link",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = false,
IsListField = false,
Label = "Link",
Hints = "An optional link to your publication"
}
}
},
Publish = true
};
var command =
SchemaBuilder.Create("Publications")
.AddString("Name", f => f
.Required()
.ShowInList()
.Hints("The name or title of your publication."))
.AddAssets("Cover", f => f
.MustBeImage()
.Hints("The cover of your publication."))
.AddString("Description", f => f
.Hints("Describe the content of your publication."))
.AddString("Link", f => f
.Hints("Optional link to your publication."))
.Build();
await publish(command);
@ -557,43 +231,18 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private async Task<NamedId<Guid>> CreateSkillsSchemaAsync(Func<ICommand, Task> publish)
{
var command = new CreateSchema
{
Name = "skills",
Properties = new SchemaProperties
{
Label = "Skills"
},
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "name",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Input,
IsRequired = true,
IsListField = true,
Label = "Name",
Hints = "The name for your skill"
}
},
new CreateSchemaField
{
Name = "experience",
Properties = new StringFieldProperties
{
Editor = StringFieldEditor.Dropdown,
IsRequired = true,
IsListField = true,
AllowedValues = ImmutableList.Create("Beginner", "Advanced", "Professional", "Expert"),
Label = "Experience",
Hints = "The level of experience"
}
}
},
Publish = true
};
var command =
SchemaBuilder.Create("Skills")
.AddString("Name", f => f
.Required()
.ShowInList()
.Hints("The name of the skill."))
.AddString("Experience", f => f
.AsDropDown("Beginner", "Advanced", "Professional", "Expert")
.Required()
.ShowInList()
.Hints("The level of experience."))
.Build();
await publish(command);

17
src/Squidex.Infrastructure/StringExtensions.cs

@ -343,6 +343,23 @@ namespace Squidex.Infrastructure
return sb.ToString();
}
public static string ToKebabCase(this string value)
{
var sb = new StringBuilder();
foreach (var part in value.Split('-', '_', ' '))
{
if (sb.Length > 0)
{
sb.Append("-");
}
sb.Append(part.ToLower());
}
return sb.ToString();
}
public static string ToCamelCase(this string value)
{
value = value.ToPascalCase();

17
src/Squidex/app/features/apps/pages/apps-page.component.html

@ -74,6 +74,23 @@
</div>
</div>
</div>
<div class="card card-template card-href" (click)="createNewApp('Identity')">
<div class="card-body">
<div class="card-image">
<img src="/images/add-identity.png" />
</div>
<h4 class="card-title">New Identity App</h4>
<div class="card-text">
<div>Create app for Squidex Identity.</div>
<div>
<a href="https://github.com/Squidex/squidex-identity" (click)="$event.stopPropagation()" target="_blank">Project</a>
</div>
</div>
</div>
</div>
</div>
<ng-container *sqxModalView="addAppDialog;onRoot:true">

12
tests/Squidex.Infrastructure.Tests/StringExtensionsTests.cs

@ -21,6 +21,18 @@ namespace Squidex.Infrastructure
Assert.Equal("fallback", value.WithFallback("fallback"));
}
[Theory]
[InlineData("", "")]
[InlineData("m", "m")]
[InlineData("m y", "m-y")]
[InlineData("M Y", "m-y")]
[InlineData("M_Y", "m-y")]
[InlineData("M_Y ", "m-y")]
public void Should_convert_to_kebap_case(string input, string output)
{
Assert.Equal(output, input.ToKebabCase());
}
[Theory]
[InlineData("", "")]
[InlineData("m", "M")]

Loading…
Cancel
Save