Browse Source

Merge branch 'master' of github.com:Squidex/squidex into feature_conditions_rule_scripting

pull/341/head
Sebastian Stehle 7 years ago
parent
commit
fd74958c81
  1. 11
      Dockerfile
  2. 12
      Dockerfile.build
  3. 58
      extensions/Squidex.Extensions/Actions/Email/EmailAction.cs
  4. 84
      extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs
  5. 3
      src/Squidex/AppServices.cs
  6. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs
  7. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs
  8. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs
  9. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs
  10. 5
      src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs
  11. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  12. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  13. 3
      src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs
  14. 5
      src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  15. 2
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs
  16. 3
      src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs
  17. 7
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  18. 2
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  19. 3
      src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs
  20. 3
      src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs
  21. 2
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs
  22. 7
      src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  23. 2
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  24. 5
      src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  25. 3
      src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs
  26. 3
      src/Squidex/Config/Web/WebExtensions.cs
  27. 24
      src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs
  28. 10
      src/Squidex/Pipeline/ETagExtensions.cs
  29. 34
      src/Squidex/Pipeline/ETagFilter.cs
  30. 14
      src/Squidex/Pipeline/ETagOptions.cs
  31. 2
      src/Squidex/Pipeline/IGenerateEtag.cs
  32. 10
      src/Squidex/app-config/karma-test-shim.js
  33. 12
      src/Squidex/app-config/karma.conf.js
  34. 23
      src/Squidex/app-config/karma.coverage.conf.js
  35. 41
      src/Squidex/app-config/tslint/linter.js
  36. 157
      src/Squidex/app-config/tslint/plugin.js
  37. 6
      src/Squidex/app-config/webpack.config.js
  38. 3
      src/Squidex/app-config/webpack.run.base.js
  39. 6
      src/Squidex/app-config/webpack.run.dev.js
  40. 10
      src/Squidex/app-config/webpack.run.prod.js
  41. 13
      src/Squidex/app-config/webpack.test.js
  42. 2
      src/Squidex/app/features/content/shared/content-item.component.html
  43. 2
      src/Squidex/app/features/content/shared/field-editor.component.html
  44. 1
      src/Squidex/app/features/rules/declarations.ts
  45. 2
      src/Squidex/app/features/rules/module.ts
  46. 128
      src/Squidex/app/features/rules/pages/rules/actions/email-action.component.html
  47. 6
      src/Squidex/app/features/rules/pages/rules/actions/email-action.component.scss
  48. 70
      src/Squidex/app/features/rules/pages/rules/actions/email-action.component.ts
  49. 2
      src/Squidex/app/features/rules/pages/rules/rule-element.component.html
  50. 7
      src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html
  51. 2
      src/Squidex/app/framework/angular/forms/toggle.component.html
  52. 19
      src/Squidex/app/framework/angular/forms/toggle.component.ts
  53. 4
      src/Squidex/app/framework/angular/http/caching.interceptor.ts
  54. 2
      src/Squidex/app/shared/components/language-selector.component.html
  55. 3
      src/Squidex/app/shared/components/language-selector.component.scss
  56. 7
      src/Squidex/appsettings.json
  57. 392
      src/Squidex/package-lock.json
  58. 31
      src/Squidex/package.json
  59. 10
      src/Squidex/wwwroot/scripts/editor-sdk.js
  60. 24
      tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs
  61. 87
      tests/Squidex.Tests/Pipeline/ETagFilterTests.cs

11
Dockerfile

@ -3,20 +3,19 @@
#
FROM squidex/dotnet:2.2-sdk-chromium-phantomjs-node as builder
WORKDIR /src
COPY src/Squidex/package*.json /tmp/
# Install Node packages
RUN cd /tmp && npm install
RUN cd /tmp && npm install --loglevel=error
COPY . .
WORKDIR /
# Build Frontend
RUN cp -a /tmp/node_modules /src/Squidex/ \
&& cd /src/Squidex \
RUN cp -a /tmp/node_modules src/Squidex/ \
&& cd src/Squidex \
&& npm run test:coverage \
&& npm run build:copy \
&& npm run build
# Test Backend

12
Dockerfile.build

@ -1,20 +1,18 @@
FROM squidex/dotnet:2.2-sdk-chromium-phantomjs-node as builder
WORKDIR /src
COPY src/Squidex/package*.json /tmp/
RUN cd /tmp \
&& npm install \
&& npm rebuild node-sass
# Install Node packages
RUN cd /tmp && npm install --loglevel=error
COPY . .
WORKDIR /
# Build Frontend
RUN cp -a /tmp/node_modules /src/Squidex/ \
RUN cp -a /tmp/node_modules src/Squidex/ \
&& cd /src/Squidex \
&& npm run test:coverage \
&& npm run build:copy \
&& npm run build
# Test Backend

58
extensions/Squidex.Extensions/Actions/Email/EmailAction.cs

@ -0,0 +1,58 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Extensions.Actions.Email
{
[RuleActionHandler(typeof(EmailActionHandler))]
[RuleAction(
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M28 5h-24c-2.209 0-4 1.792-4 4v13c0 2.209 1.791 4 4 4h24c2.209 0 4-1.791 4-4v-13c0-2.208-1.791-4-4-4zM2 10.25l6.999 5.25-6.999 5.25v-10.5zM30 22c0 1.104-0.898 2-2 2h-24c-1.103 0-2-0.896-2-2l7.832-5.875 4.368 3.277c0.533 0.398 1.166 0.6 1.8 0.6 0.633 0 1.266-0.201 1.799-0.6l4.369-3.277 7.832 5.875zM30 20.75l-7-5.25 7-5.25v10.5zM17.199 18.602c-0.349 0.262-0.763 0.4-1.199 0.4s-0.851-0.139-1.2-0.4l-12.8-9.602c0-1.103 0.897-2 2-2h24c1.102 0 2 0.897 2 2l-12.801 9.602z'/></svg>",
IconColor = "#333300",
Display = "Send an email",
Description = "Send an email with a custom SMTP server.",
ReadMore = "https://en.wikipedia.org/wiki/Email")]
public sealed class EmailAction : RuleAction
{
[Required]
[Display(Name = "ServerHost", Description = "The IP address or host to the SMTP server.")]
public string ServerHost { get; set; }
[Required]
[Display(Name = "ServerPort", Description = "The port to the SMTP server.")]
public int ServerPort { get; set; }
[Required]
[Display(Name = "ServerUseSsl", Description = "Specify whether the SMPT client uses Secure Sockets Layer (SSL) to encrypt the connection.")]
public bool ServerUseSsl { get; set; }
[Required]
[Display(Name = "ServerUsername", Description = "The username for the SMTP server.")]
public string ServerUsername { get; set; }
[Required]
[Display(Name = "ServerPassword", Description = "The password for the SMTP server.")]
public string ServerPassword { get; set; }
[Required]
[Display(Name = "MessageFrom", Description = "The email sending address.")]
public string MessageFrom { get; set; }
[Required]
[Display(Name = "MessageTo", Description = "The email message will be sent to.")]
public string MessageTo { get; set; }
[Required]
[Display(Name = "MessageSubject", Description = "The subject line for this email message.")]
public string MessageSubject { get; set; }
[Required]
[Display(Name = "MessageBody", Description = "The message body.")]
public string MessageBody { get; set; }
}
}

84
extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs

@ -0,0 +1,84 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
namespace Squidex.Extensions.Actions.Email
{
public sealed class EmailActionHandler : RuleActionHandler<EmailAction, EmailJob>
{
public EmailActionHandler(RuleEventFormatter formatter)
: base(formatter)
{
}
protected override (string Description, EmailJob Data) CreateJob(EnrichedEvent @event, EmailAction action)
{
var ruleJob = new EmailJob
{
ServerHost = action.ServerHost,
ServerUseSsl = action.ServerUseSsl,
ServerPassword = action.ServerPassword,
ServerPort = action.ServerPort,
ServerUsername = Format(action.ServerUsername, @event),
MessageFrom = Format(action.MessageFrom, @event),
MessageTo = Format(action.MessageTo, @event),
MessageSubject = Format(action.MessageSubject, @event),
MessageBody = Format(action.MessageBody, @event)
};
var description = $"Send an email to {action.MessageTo}";
return (description, ruleJob);
}
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(EmailJob job)
{
using (var client = new SmtpClient(job.ServerHost, job.ServerPort))
{
client.EnableSsl = job.ServerUseSsl;
client.Credentials = new NetworkCredential(job.ServerUsername, job.ServerPassword);
using (var message = new MailMessage(job.MessageFrom, job.MessageTo))
{
message.Subject = job.MessageSubject;
message.Body = job.MessageBody;
await client.SendMailAsync(message);
}
}
return ("Completed", null);
}
}
public sealed class EmailJob
{
public int ServerPort { get; set; }
public string ServerHost { get; set; }
public string ServerUsername { get; set; }
public string ServerPassword { get; set; }
public bool ServerUseSsl { get; set; }
public string MessageFrom { get; set; }
public string MessageTo { get; set; }
public string MessageSubject { get; set; }
public string MessageBody { get; set; }
}
}

3
src/Squidex/AppServices.cs

@ -19,6 +19,7 @@ using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Extensions.Actions.Twitter;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Diagnostics;
using Squidex.Pipeline;
using Squidex.Pipeline.Robots;
namespace Squidex
@ -60,6 +61,8 @@ namespace Squidex
config.GetSection("robots"));
services.Configure<GCHealthCheckOptions>(
config.GetSection("healthz:gc"));
services.Configure<ETagOptions>(
config.GetSection("etags"));
services.Configure<MyContentsControllerOptions>(
config.GetSection("contentsController"));

3
src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs

@ -8,6 +8,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var response = App.Clients.Select(ClientDto.FromKvp).ToArray();
Response.Headers["ETag"] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
return Ok(response);
}

3
src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var response = ContributorsDto.FromApp(App, appPlansProvider);
Response.Headers["ETag"] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
return Ok(response);
}

3
src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs

@ -8,6 +8,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
@ -45,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var response = AppLanguageDto.FromApp(App);
Response.Headers["ETag"] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
return Ok(response);
}

3
src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs

@ -9,6 +9,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Commands;
@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var response = App.Patterns.Select(AppPatternDto.FromKvp).OrderBy(x => x.Name).ToArray();
Response.Headers["ETag"] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
return Ok(response);
}

5
src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var response = RolesDto.FromApp(App);
Response.Headers["ETag"] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
return Ok(response);
}
@ -70,7 +71,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var response = await permissionsProvider.GetPermissionsAsync(App);
Response.Headers["ETag"] = string.Join(";", response).Sha256Base64();
Response.Headers[HeaderNames.ETag] = string.Join(";", response).Sha256Base64();
return Ok(response);
}

3
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -9,6 +9,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps.Commands;
@ -65,7 +66,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
var response = entities.ToArray(a => AppDto.FromApp(a, userId, userPermissions, appPlansProvider));
Response.Headers["ETag"] = response.ToManyEtag();
Response.Headers[HeaderNames.ETag] = response.ToManyEtag();
return Ok(response);
}

2
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -20,7 +20,7 @@ using Squidex.Shared;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
public sealed class AppDto : IGenerateEtag
public sealed class AppDto : IGenerateETag
{
/// <summary>
/// The name of the app.

3
src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs

@ -9,6 +9,7 @@ using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
@ -67,7 +68,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
return NotFound();
}
Response.Headers["ETag"] = entity.FileVersion.ToString();
Response.Headers[HeaderNames.ETag] = entity.FileVersion.ToString();
return new FileCallbackResult(entity.MimeType, entity.FileName, async bodyStream =>
{

5
src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using NSwag.Annotations;
using Squidex.Areas.Api.Controllers.Assets.Models;
using Squidex.Areas.Api.Controllers.Contents;
@ -111,7 +112,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
Response.Headers["Surrogate-Key"] = response.Items.ToSurrogateKeys();
}
Response.Headers["ETag"] = response.Items.ToManyEtag(response.Total);
Response.Headers[HeaderNames.ETag] = response.Items.ToManyEtag(response.Total);
return Ok(response);
}
@ -148,7 +149,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
Response.Headers["Surrogate-Key"] = entity.Id.ToString();
}
Response.Headers["ETag"] = entity.Version.ToString();
Response.Headers[HeaderNames.ETag] = entity.Version.ToString();
return Ok(response);
}

2
src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs

@ -16,7 +16,7 @@ using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Assets.Models
{
public sealed class AssetDto : IGenerateEtag
public sealed class AssetDto : IGenerateETag
{
/// <summary>
/// The id of the asset.

3
src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs

@ -8,6 +8,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Orleans;
using Squidex.Areas.Api.Controllers.Comments.Models;
using Squidex.Domain.Apps.Entities.Comments;
@ -56,7 +57,7 @@ namespace Squidex.Areas.Api.Controllers.Comments
var result = await grainFactory.GetGrain<ICommentGrain>(commentsId).GetCommentsAsync(version);
var response = CommentsDto.FromResult(result);
Response.Headers["ETag"] = response.Version.ToString();
Response.Headers[HeaderNames.ETag] = response.Version.ToString();
return Ok(response);
}

7
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -10,6 +10,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using NodaTime;
using NodaTime.Text;
using Squidex.Areas.Api.Controllers.Contents.Models;
@ -141,7 +142,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
Response.Headers["Surrogate-Key"] = response.Items.ToSurrogateKeys();
}
Response.Headers["ETag"] = response.Items.ToManyEtag(response.Total);
Response.Headers[HeaderNames.ETag] = response.Items.ToManyEtag(response.Total);
return Ok(response);
}
@ -175,7 +176,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
Response.Headers["Surrogate-Key"] = content.Id.ToString();
}
Response.Headers["ETag"] = content.Version.ToString();
Response.Headers[HeaderNames.ETag] = content.Version.ToString();
return Ok(response);
}
@ -211,7 +212,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
Response.Headers["Surrogate-Key"] = content.Id.ToString();
}
Response.Headers["ETag"] = content.Version.ToString();
Response.Headers[HeaderNames.ETag] = content.Version.ToString();
return Ok(response.Data);
}

2
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -20,7 +20,7 @@ using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Contents.Models
{
public sealed class ContentDto : IGenerateEtag
public sealed class ContentDto : IGenerateETag
{
/// <summary>
/// The if of the content item.

3
src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs

@ -7,6 +7,7 @@
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Pipeline;
@ -41,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Languages
{
var response = Language.AllLanguages.Select(LanguageDto.FromLanguage).ToArray();
Response.Headers["Etag"] = "1";
Response.Headers[HeaderNames.ETag] = "1";
return Ok(response);
}

3
src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Plans.Models;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure.Commands;
@ -52,7 +53,7 @@ namespace Squidex.Areas.Api.Controllers.Plans
var response = AppPlansDto.FromApp(App, appPlansProvider, hasPortal);
Response.Headers["ETag"] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
return Ok(response);
}

2
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs

@ -18,7 +18,7 @@ using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Rules.Models
{
public sealed class RuleDto : IGenerateEtag
public sealed class RuleDto : IGenerateETag
{
/// <summary>
/// The id of the rule.

7
src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using NodaTime;
using Squidex.Areas.Api.Controllers.Rules.Models;
using Squidex.Domain.Apps.Entities;
@ -59,7 +60,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
{
var response = RuleElementRegistry.Actions.ToDictionary(x => x.Key, x => SimpleMapper.Map(x.Value, new RuleElementDto()));
Response.Headers["Etag"] = RuleActionsEtag;
Response.Headers[HeaderNames.ETag] = RuleActionsEtag;
return Ok(response);
}
@ -79,7 +80,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
{
var response = RuleElementRegistry.Triggers.ToDictionary(x => x.Key, x => SimpleMapper.Map(x.Value, new RuleElementDto()));
Response.Headers["Etag"] = RuleTriggersEtag;
Response.Headers[HeaderNames.ETag] = RuleTriggersEtag;
return Ok(response);
}
@ -103,7 +104,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
var response = entities.Select(RuleDto.FromRule).ToArray();
Response.Headers["ETag"] = response.ToManyEtag(0);
Response.Headers[HeaderNames.ETag] = response.ToManyEtag(0);
return Ok(response);
}

2
src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -15,7 +15,7 @@ using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
public sealed class SchemaDto : IGenerateEtag
public sealed class SchemaDto : IGenerateETag
{
/// <summary>
/// The id of the schema.

5
src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Schemas.Models;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Schemas;
@ -54,7 +55,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
var response = schemas.ToArray(SchemaDto.FromSchema);
Response.Headers["ETag"] = response.ToManyEtag();
Response.Headers[HeaderNames.ETag] = response.ToManyEtag();
return Ok(response);
}
@ -93,7 +94,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
var response = SchemaDetailsDto.FromSchema(entity);
Response.Headers["ETag"] = entity.Version.ToString();
Response.Headers[HeaderNames.ETag] = entity.Version.ToString();
return Ok(response);
}

3
src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs

@ -10,6 +10,7 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace Squidex.Areas.Frontend.Middlewares
{
@ -55,7 +56,7 @@ namespace Squidex.Areas.Frontend.Middlewares
memoryStream.Seek(0, SeekOrigin.Begin);
context.Response.Headers["Content-Length"] = memoryStream.Length.ToString();
context.Response.Headers[HeaderNames.ContentLength] = memoryStream.Length.ToString();
await memoryStream.CopyToAsync(responseBody);
}

3
src/Squidex/Config/Web/WebExtensions.cs

@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Net.Http.Headers;
using Squidex.Infrastructure.Json;
using Squidex.Pipeline;
using Squidex.Pipeline.Robots;
@ -63,7 +64,7 @@ namespace Squidex.Config.Web
var json = serializer.Serialize(response);
httpContext.Response.Headers["Content-Types"] = "text/json";
httpContext.Response.Headers[HeaderNames.ContentType] = "text/json";
return httpContext.Response.WriteAsync(json);
});

24
src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs

@ -9,6 +9,7 @@ using System;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
@ -32,23 +33,30 @@ namespace Squidex.Pipeline.CommandMiddlewares
return;
}
context.Command.ExpectedVersion = EtagVersion.Any;
var headers = httpContextAccessor.HttpContext.Request.Headers;
var headerMatch = headers["If-Match"].ToString();
if (!string.IsNullOrWhiteSpace(headerMatch) && long.TryParse(headerMatch, NumberStyles.Any, CultureInfo.InvariantCulture, out var expectedVersion))
{
context.Command.ExpectedVersion = expectedVersion;
}
else
if (headers.TryGetValue(HeaderNames.IfMatch, out var etag) && !string.IsNullOrWhiteSpace(etag))
{
context.Command.ExpectedVersion = EtagVersion.Any;
var etagValue = etag.ToString();
if (etagValue.StartsWith("W/", StringComparison.OrdinalIgnoreCase))
{
etagValue = etagValue.Substring(2);
}
if (long.TryParse(etagValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var expectedVersion))
{
context.Command.ExpectedVersion = expectedVersion;
}
}
await next();
if (context.Result<object>() is EntitySavedResult result)
{
httpContextAccessor.HttpContext.Response.Headers["ETag"] = result.Version.ToString();
httpContextAccessor.HttpContext.Response.Headers[HeaderNames.ETag] = result.Version.ToString();
}
}
}

10
src/Squidex/Pipeline/ETagExtensions.cs

@ -17,7 +17,7 @@ namespace Squidex.Pipeline
{
private static readonly int GuidLength = Guid.Empty.ToString().Length;
public static string ToManyEtag<T>(this IReadOnlyList<T> items, long total = 0) where T : IGenerateEtag
public static string ToManyEtag<T>(this IReadOnlyList<T> items, long total = 0) where T : IGenerateETag
{
using (Profiler.Trace("CalculateEtag"))
{
@ -27,7 +27,7 @@ namespace Squidex.Pipeline
}
}
private static string Unhashed<T>(IReadOnlyList<T> items, long total) where T : IGenerateEtag
private static string Unhashed<T>(IReadOnlyList<T> items, long total) where T : IGenerateETag
{
var sb = new StringBuilder((items.Count * (GuidLength + 4)) + 10);
@ -47,10 +47,10 @@ namespace Squidex.Pipeline
}
}
return sb.ToString();
return sb.ToString().Sha256Base64();
}
public static string ToSurrogateKeys<T>(this IReadOnlyList<T> items) where T : IGenerateEtag
public static string ToSurrogateKeys<T>(this IReadOnlyList<T> items) where T : IGenerateETag
{
if (items.Count == 0)
{
@ -70,7 +70,7 @@ namespace Squidex.Pipeline
return sb.ToString();
}
public static string ToEtag<T>(this T item) where T : IGenerateEtag
public static string ToEtag<T>(this T item) where T : IGenerateETag
{
return item.Version.ToString();
}

34
src/Squidex/Pipeline/ETagFilter.cs

@ -9,26 +9,44 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Squidex.Pipeline
{
public sealed class ETagFilter : IAsyncActionFilter
{
private readonly ETagOptions options;
public ETagFilter(IOptions<ETagOptions> options)
{
this.options = options.Value;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
var httpContext = context.HttpContext;
if (HttpMethods.IsGet(httpContext.Request.Method) &&
httpContext.Request.Headers.TryGetValue("If-None-Match", out var noneMatch) &&
httpContext.Response.StatusCode == 200 &&
httpContext.Response.Headers.TryGetValue("ETag", out var etag) &&
!string.IsNullOrWhiteSpace(noneMatch) &&
!string.IsNullOrWhiteSpace(etag) &&
string.Equals(etag, noneMatch, System.StringComparison.Ordinal))
if (httpContext.Response.Headers.TryGetValue(HeaderNames.ETag, out var etag) && !string.IsNullOrWhiteSpace(etag))
{
resultContext.Result = new StatusCodeResult(304);
string etagValue = etag;
if (!options.Strong)
{
etagValue = "W/" + etag;
httpContext.Response.Headers[HeaderNames.ETag] = etagValue;
}
if (HttpMethods.IsGet(httpContext.Request.Method) &&
httpContext.Response.StatusCode == 200 &&
httpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var noneMatch) && !string.IsNullOrWhiteSpace(noneMatch) &&
string.Equals(etagValue, noneMatch, System.StringComparison.Ordinal))
{
resultContext.Result = new StatusCodeResult(304);
}
}
}
}

14
src/Squidex/Pipeline/ETagOptions.cs

@ -0,0 +1,14 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Pipeline
{
public sealed class ETagOptions
{
public bool Strong { get; set; }
}
}

2
src/Squidex/Pipeline/IGenerateEtag.cs

@ -9,7 +9,7 @@ using System;
namespace Squidex.Pipeline
{
public interface IGenerateEtag
public interface IGenerateETag
{
Guid Id { get; }

10
src/Squidex/app-config/karma-test-shim.js

@ -21,14 +21,16 @@ testing.TestBed.initTestEnvironment(
var testContext = require.context('../app', true, /\.spec\.ts/);
/*
* get all the files, for each file, call the context function
/**
* Get all the files, for each file, call the context function
* that will require the file and load it up here. Context will
* loop and require those spec files here
* loop and require those spec files here.
*/
function requireAll(requireContext) {
return requireContext.keys().map(requireContext);
}
// requires and returns all modules that match
/**
* Requires and returns all modules that match.
*/
var modules = requireAll(testContext);

12
src/Squidex/app-config/karma.conf.js

@ -3,14 +3,14 @@
module.exports = function (config) {
var _config = {
/**
* Base path that will be used to resolve all patterns (e.g. files, exclude)
* Base path that will be used to resolve all patterns (e.g. files, exclude).
*/
basePath: '',
frameworks: ['jasmine'],
/**
* Load additional test shim to setup angular2 for testing
* Load additional test shim to setup angular2 for testing.
*/
files: [
{ pattern: './app-config/karma-test-shim.js', watched: false }
@ -33,21 +33,21 @@ module.exports = function (config) {
noInfo: true
},
/*
* Leave Jasmine Spec Runner output visible in browser
/**
* Leave Jasmine Spec Runner output visible in browser.
*/
client: {
clearContext: false
},
/*
/**
* Use a mocha style console reporter and html reporter.
*/
reporters: ['kjhtml', 'mocha'],
/**
* Run with chrome to enable debugging
* Run with chrome to enable debugging.
*
* available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
*/

23
src/Squidex/app-config/karma.coverage.conf.js

@ -3,14 +3,14 @@
module.exports = function (config) {
var _config = {
/**
* Base path that will be used to resolve all patterns (e.g. files, exclude)
* Base path that will be used to resolve all patterns (e.g. files, exclude).
*/
basePath: '',
frameworks: ['jasmine'],
/**
* Load additional test shim to setup angular2 for testing
* Load additional test shim to setup angular2 for testing.
*/
files: [
{ pattern: './app-config/karma-test-shim.js', watched: false }
@ -33,21 +33,20 @@ module.exports = function (config) {
noInfo: true
},
/*
* Use a mocha style console reporter, html reporter and the code coverage reporter
/**
* Use a mocha style console reporter, html reporter and the code coverage reporter.
*/
reporters: ['mocha', 'html', 'coverage-istanbul'],
// HtmlReporter configuration
htmlReporter: {
useCompactStyle: true,
/**
* Use the same folder like the html report for coverage reports
* Use the same folder like the html report for coverage reports.
*/
outputFile: '_test-output/tests.html',
/**
* Group the output by test suite (describe), equivalent to mocha reporter
* Group the output by test suite (describe), equivalent to mocha reporter.
*/
groupSuites: true
},
@ -64,22 +63,24 @@ module.exports = function (config) {
},
/**
* Disable continuous Integration mode, run only one time
* Disable continuous Integration mode, run only one time.
*/
singleRun: true,
customLaunchers: {
ChromeCustom: {
base: 'ChromeHeadless',
// We must disable the Chrome sandbox (Chrome's sandbox needs more permissions than Docker allows by default)
/**
* We must disable the Chrome sandbox (Chrome's sandbox needs more permissions than Docker allows by default).
*/
flags: ['--no-sandbox']
}
},
/**
* Run with chrome because phantom js does not provide all types, e.g. DragEvent
* Run with chrome because phantom js does not provide all types.
*
* available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
* Available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
*/
browsers: ['ChromeCustom']
};

41
src/Squidex/app-config/tslint/linter.js

@ -1,41 +0,0 @@
const { Runner, run } = require('tslint/lib/runner');
const options = JSON.parse(process.argv[2]) || {};
function logToParent(message) {
process.stdout.write(message);
}
function runLinter(runnerOptions, write) {
const logTsLint = message => {
write('tslint:' + message)
};
if (run) {
const logger = { log: logTsLint, error: logTsLint };
return run(runnerOptions, logger);
} else if (Runner) {
return new Promise(resolve => {
new Runner(runnerOptions, { write: logTsLint }).run(resolve);
});
} else {
write('tsinfo:Unable to launch tslint. No suitable runner found.');
}
}
logToParent('tsinfo:Linting started in separate process...');
const runnerOptions = Object.assign({
exclude: [],
format: 'json'
}, options);
runLinter(runnerOptions, logToParent)
.then(() => {
logToParent('tsinfo:Linting complete.');
process.exit();
}).catch(error => {
logToParent(`tserror:Error starting linter: ${error}\n${error.stack}`);
});

157
src/Squidex/app-config/tslint/plugin.js

@ -1,157 +0,0 @@
const path = require('path');
const chalk = require('chalk');
const { fork } = require('child_process');
function apply(options, compiler) {
let linterProcess;
let linterPromise;
let linterIteration = 0;
function compileHook() {
if (linterProcess && linterProcess.kill) {
// Exits any outstanding child process if one exists
linterProcess.kill();
}
let { files = [] } = options;
if (!files.length) {
process.stdout.write(chalk.yellow.bold('\n[tslint-plugin] No `files` option specified.\n'));
return;
}
options.files = Array.isArray(files) ? files : [files];
// Spawn a child process to run the linter
linterProcess = fork(path.resolve(__dirname, 'linter.js'), [JSON.stringify(options)], {
silent: true
});
// Use the iteration to cancel previous promises.
linterIteration++;
linterPromise = new Promise(resolve => {
const linterOutBuffer = [];
linterProcess.stdout.on('data', (message) => {
if (message) {
const msg = message.toString();
for (let line of msg.split('\n')) {
const indexOfSeparator = line.indexOf(':');
if (indexOfSeparator > 0) {
const type = line.substring(0, indexOfSeparator);
const body = line.substring(indexOfSeparator + 1);
switch (type) {
case 'tslint': {
const json = JSON.parse(body);
for (let item of json) {
linterOutBuffer.push(item);
}
break;
}
case 'tsinfo': {
process.stdout.write(chalk.cyan(`[tslint-plugin] ${body}\n`));
break;
}
case 'tserror': {
process.stderr.write(chalk.red(`[tslint-plugin] ${body}\n`));
break;
}
default: {
process.stderr.write(msg);
}
}
} else {
process.stdout.write(line);
}
}
}
});
linterProcess.once('exit', () => {
resolve({ iteration: linterIteration, out: linterOutBuffer });
// Clean up the linterProcess when finished
delete linterProcess;
});
});
}
function createError(message) {
const error = new Error(message);
delete error.stackTrace;
return error;
}
function emitHook(compilation, callback) {
if (linterPromise && options.waitForLinting) {
linterPromise.then(result => {
for (let r of result.out) {
const msg = `${r.name}:${r.startPosition.line + 1}:${r.startPosition.character + 1} [tslint] ${r.ruleName}: ${r.failure}`;
if (r.ruleSeverity === 'ERROR' || options.warningsAsError) {
compilation.errors.push(createError(msg));
} else {
compilation.warnings.push(createError(msg));
}
}
callback();
});
} else {
callback();
}
}
function doneHook() {
const currentIteration = linterIteration;
if (linterPromise && !options.waitForLinting) {
let isResolved = false;
linterPromise.then(result => {
isResolved = true;
// If the iterations are not the same another process has already been started and we cancel these results.
if (result.iteration === currentIteration) {
for (let r of result.out) {
const msg = `${r.name}:${r.startPosition.line + 1}:${r.startPosition.character + 1} [tslint] ${r.ruleName}: ${r.failure}`;
if (r.ruleSeverity === 'ERROR' || options.warningsAsError) {
process.stderr.write(chalk.red(msg + '\n'));
} else {
process.stdout.write(chalk.yellow(msg + '\n'));
}
}
}
});
if (!isResolved) {
process.stdout.write(chalk.cyan(`[tslint-plugin] Waiting for results...\n`));
}
}
}
if (compiler.hooks) {
// Webpack 4
compiler.hooks.compile.tap('TSLintWebpackPlugin', compileHook);
compiler.hooks.emit.tapAsync('TSLintWebpackPlugin', emitHook);
compiler.hooks.done.tap('TSLintWebpackPlugin', doneHook);
} else {
// Backwards compatibility
compiler.plugin('compile', compileHook);
compiler.plugin('emit', emitHook);
compiler.plugin('done', doneHook);
}
}
module.exports = function TSLintWebpackPlugin(options = {}) {
return {
apply: apply.bind(this, options)
};
};

6
src/Squidex/app-config/webpack.config.js

@ -33,7 +33,7 @@ module.exports = {
]
},
/*
/**
* Options affecting the normal modules.
*
* See: https://webpack.js.org/configuration/module/
@ -109,7 +109,7 @@ module.exports = {
},
plugins: [
/*
/**
* Puts each bundle into a file and appends the hash of the file to the path.
*
* See: https://github.com/webpack-contrib/mini-css-extract-plugin
@ -120,7 +120,7 @@ module.exports = {
options: {
htmlLoader: {
/**
* Define the root for images, so that we can use absolute url's
* Define the root for images, so that we can use absolute urls.
*
* See: https://github.com/webpack/html-loader#Advanced_Options
*/

3
src/Squidex/app-config/webpack.run.base.js

@ -10,8 +10,7 @@ const plugins = {
module.exports = webpackMerge(commonConfig, {
/**
* The entry point for the bundle
* Our Angular.js app
* The entry point for the bundle. Our Angular app.
*
* See: https://webpack.js.org/configuration/entry-context/
*/

6
src/Squidex/app-config/webpack.run.dev.js

@ -6,7 +6,7 @@
const plugins = {
// https://github.com/jrparish/tslint-webpack-plugin
TsLintPlugin: require('./tslint/plugin')
TsLintPlugin: require('tslint-webpack-plugin')
};
module.exports = webpackMerge(runConfig, {
@ -17,7 +17,9 @@ module.exports = webpackMerge(runConfig, {
output: {
filename: '[name].js',
// Set the public path, because we are running the website from another port (5000)
/**
* Set the public path, because we are running the website from another port (5000).
*/
publicPath: 'http://localhost:3000/'
},

10
src/Squidex/app-config/webpack.run.prod.js

@ -14,7 +14,7 @@ const plugins = {
// https://github.com/NMFR/optimize-css-assets-webpack-plugin
OptimizeCSSAssetsPlugin: require("optimize-css-assets-webpack-plugin"),
// https://github.com/jrparish/tslint-webpack-plugin
TsLintPlugin: require('./tslint/plugin')
TsLintPlugin: require('tslint-webpack-plugin')
};
helpers.removeLoaders(runConfig, ['scss', 'ts']);
@ -34,15 +34,13 @@ module.exports = webpackMerge(runConfig, {
/**
* Specifies the name of each output file on disk.
* IMPORTANT: You must not specify an absolute path here!
*
* See: https://webpack.js.org/configuration/output/#output-filename
*/
filename: '[name].js',
/**
* The filename of non-entry chunks as relative path
* inside the output.path directory.
* The filename of non-entry chunks as relative path inside the output.path directory.
*
* See: https://webpack.js.org/configuration/output/#output-chunkfilename
*/
@ -63,7 +61,7 @@ module.exports = webpackMerge(runConfig, {
rules: [{
test: /\.scss$/,
/*
* Extract the content from a bundle to a file
* Extract the content from a bundle to a file.
*
* See: https://github.com/webpack-contrib/extract-text-webpack-plugin
*/
@ -75,7 +73,7 @@ module.exports = webpackMerge(runConfig, {
loader: 'sass-loader'
}],
/*
* Do not include component styles
* Do not include component styles.
*/
include: helpers.root('app', 'theme'),
}, {

13
src/Squidex/app-config/webpack.test.js

@ -1,16 +1,15 @@
 const webpack = require('webpack'),
webpackMerge = require('webpack-merge'),
path = require('path'),
helpers = require('./helpers'),
commonConfig = require('./webpack.config.js');
const webpack = require('webpack'),
webpackMerge = require('webpack-merge'),
path = require('path'),
helpers = require('./helpers'),
commonConfig = require('./webpack.config.js');
module.exports = webpackMerge(commonConfig, {
mode: 'development',
/**
* Source map for Karma from the help of karma-sourcemap-loader & karma-webpack
* Source map for Karma from the help of karma-sourcemap-loader & karma-webpack.
*
* Do not change, leave as is or it wont work.
* See: https://webpack.js.org/configuration/devtool/
*/
devtool: 'inline-source-map'

2
src/Squidex/app/features/content/shared/content-item.component.html

@ -44,7 +44,7 @@
<div *ngSwitchCase="'Boolean'">
<div [ngSwitch]="field.properties['editor']">
<div *ngSwitchCase="'Toggle'">
<sqx-toggle [formControlName]="field.name"></sqx-toggle>
<sqx-toggle [formControlName]="field.name" [threeStates]="!field.properties.isRequired"></sqx-toggle>
</div>
<div *ngSwitchCase="'Checkbox'">
<div class="form-check form-check-inline">

2
src/Squidex/app/features/content/shared/field-editor.component.html

@ -84,7 +84,7 @@
<ng-container *ngSwitchCase="'Boolean'">
<ng-container [ngSwitch]="field.properties['editor']">
<ng-container *ngSwitchCase="'Toggle'">
<sqx-toggle [formControl]="control"></sqx-toggle>
<sqx-toggle [formControl]="control" [threeStates]="!field.properties.isRequired"></sqx-toggle>
</ng-container>
<ng-container *ngSwitchCase="'Checkbox'">
<input type="checkbox" [formControl]="control" class="form-check" sqxIndeterminateValue />

1
src/Squidex/app/features/rules/declarations.ts

@ -9,6 +9,7 @@ export * from './pages/rules/actions/algolia-action.component';
export * from './pages/rules/actions/azure-queue-action.component';
export * from './pages/rules/actions/discourse-action.component';
export * from './pages/rules/actions/elastic-search-action.component';
export * from './pages/rules/actions/email-action.component';
export * from './pages/rules/actions/fastly-action.component';
export * from './pages/rules/actions/medium-action.component';
export * from './pages/rules/actions/prerender-action.component';

2
src/Squidex/app/features/rules/module.ts

@ -21,6 +21,7 @@ import {
ContentChangedTriggerComponent,
DiscourseActionComponent,
ElasticSearchActionComponent,
EmailActionComponent,
FastlyActionComponent,
MediumActionComponent,
PrerenderActionComponent,
@ -66,6 +67,7 @@ const routes: Routes = [
AzureQueueActionComponent,
ContentChangedTriggerComponent,
DiscourseActionComponent,
EmailActionComponent,
ElasticSearchActionComponent,
FastlyActionComponent,
MediumActionComponent,

128
src/Squidex/app/features/rules/pages/rules/actions/email-action.component.html

@ -0,0 +1,128 @@
<div [formGroup]="actionForm" class="form-horizontal">
<div class="form-group row">
<label class="col-3 col-form-label" for="serverHost">ServerHost</label>
<div class="col-9">
<sqx-control-errors for="serverHost" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="serverHost" formControlName="serverHost" />
<small class="form-text text-muted">
The IP address or host to the SMTP server.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="serverPort">ServerPort</label>
<div class="col-9">
<sqx-control-errors for="serverPort" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="number" class="form-control" id="serverPort" formControlName="serverPort" />
<small class="form-text text-muted">
The port to the SMTP server.
</small>
</div>
</div>
<div class="form-group row">
<div class="col-9 offset-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="serverUseSsl" formControlName="serverUseSsl" />
<label class="form-check-label" for="serverUseSsl">
ServerUseSsl
</label>
</div>
<small class="form-text text-muted">
Specify whether the SMTP client uses Secure Sockets Layer (SSL) to encrypt the connection.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="serverUsername">ServerUsername</label>
<div class="col-9">
<sqx-control-errors for="serverUsername" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="serverUsername" formControlName="serverUsername" />
<small class="form-text text-muted">
The username for the SMTP server.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="serverPassword">ServerPassword</label>
<div class="col-9">
<sqx-control-errors for="serverPassword" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="password" class="form-control" id="serverPassword" formControlName="serverPassword" />
<small class="form-text text-muted">
The password for the SMTP server.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="messageFrom">MessageFrom</label>
<div class="col-9">
<sqx-control-errors for="messageFrom" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="messageFrom" formControlName="messageFrom" />
<small class="form-text text-muted">
The email sending address. Read the <a routerLink="help">help</a> section for information about advanced formatting.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="messageTo">MessageTo</label>
<div class="col-9">
<sqx-control-errors for="messageTo" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="messageTo" formControlName="messageTo" />
<small class="form-text text-muted">
The email message will be sent to. Read the <a routerLink="help">help</a> section for information about advanced formatting.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="messageSubject">MessageSubject</label>
<div class="col-9">
<sqx-control-errors for="messageSubject" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="messageSubject" formControlName="messageSubject" />
<small class="form-text text-muted">
The subject line for this email message. Read the <a routerLink="help">help</a> section for information about advanced formatting.
</small>
</div>
</div>
<div class="form-group row">
<label class="col-3 col-form-label" for="messageBody">MessageBody</label>
<div class="col-9">
<sqx-control-errors for="messageBody" [submitted]="actionFormSubmitted"></sqx-control-errors>
<textarea class="form-control" id="messageBody" formControlName="messageBody"></textarea>
<small class="form-text text-muted">
The message body. Read the <a routerLink="help">help</a> section for information about advanced formatting.
</small>
</div>
</div>
</div>

6
src/Squidex/app/features/rules/pages/rules/actions/email-action.component.scss

@ -0,0 +1,6 @@
@import '_vars';
@import '_mixins';
textarea {
height: 250px;
}

70
src/Squidex/app/features/rules/pages/rules/actions/email-action.component.ts

@ -0,0 +1,70 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'sqx-email-action',
styleUrls: ['./email-action.component.scss'],
templateUrl: './email-action.component.html'
})
export class EmailActionComponent implements OnInit {
@Input()
public action: any;
@Input()
public actionForm: FormGroup;
@Input()
public actionFormSubmitted = false;
public ngOnInit() {
this.actionForm.setControl('serverHost',
new FormControl(this.action.serverHost || 'smtp.gmail.com', [
Validators.required
]));
this.actionForm.setControl('serverPort',
new FormControl(this.action.serverPort || 465, [
Validators.required
]));
this.actionForm.setControl('serverUseSsl',
new FormControl(this.action.serverUseSsl || true));
this.actionForm.setControl('serverUsername',
new FormControl(this.action.serverUsername || '', [
Validators.required
]));
this.actionForm.setControl('serverPassword',
new FormControl(this.action.serverPassword || '', [
Validators.required
]));
this.actionForm.setControl('messageFrom',
new FormControl(this.action.messageFrom || '', [
Validators.required
]));
this.actionForm.setControl('messageTo',
new FormControl(this.action.messageTo || '', [
Validators.required
]));
this.actionForm.setControl('messageSubject',
new FormControl(this.action.messageSubject || '', [
Validators.required
]));
this.actionForm.setControl('messageBody',
new FormControl(this.action.messageBody || '', [
Validators.required
]));
}
}

2
src/Squidex/app/features/rules/pages/rules/rule-element.component.html

@ -25,7 +25,7 @@
</div>
<div class="large-link" *ngIf="element.readMore">
<a [href]="element.readMore" target="_blank" rel="noopener">Read More</a>
<a [href]="element.readMore" (click)="$event.stopPropagation()" target="_blank" rel="noopener">Read More</a>
</div>
</div>
</div>

7
src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html

@ -94,6 +94,13 @@
[actionFormSubmitted]="actionForm.submitted | async">
</sqx-elastic-search-action>
</ng-container>
<ng-container *ngSwitchCase="'Email'">
<sqx-email-action
[action]="action"
[actionForm]="actionForm.form"
[actionFormSubmitted]="actionForm.submitted | async">
</sqx-email-action>
</ng-container>
<ng-container *ngSwitchCase="'Fastly'">
<sqx-fastly-action
[action]="action"

2
src/Squidex/app/framework/angular/forms/toggle.component.html

@ -1,4 +1,4 @@
<div class="toggle-container" (click)="changeState()"
<div class="toggle-container" (click)="changeState($event)"
[class.disabled]="isDisabled"
[class.checked]="isChecked === true"
[class.unchecked]="isChecked === false">

19
src/Squidex/app/framework/angular/forms/toggle.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Types } from '@app/framework/internal';
@ -25,6 +25,9 @@ export class ToggleComponent implements ControlValueAccessor {
private callChange = (v: any) => { /* NOOP */ };
private callTouched = () => { /* NOOP */ };
@Input()
public threeStates = false;
public isChecked: boolean | null = null;
public isDisabled = false;
@ -53,12 +56,22 @@ export class ToggleComponent implements ControlValueAccessor {
this.callTouched = fn;
}
public changeState() {
public changeState(event: MouseEvent) {
if (this.isDisabled) {
return;
}
this.isChecked = !(this.isChecked === true);
if (this.threeStates && (event.ctrlKey || event.shiftKey)) {
if (this.isChecked) {
this.isChecked = null;
} else if (this.isChecked === null) {
this.isChecked = false;
} else {
this.isChecked = true;
}
} else {
this.isChecked = !(this.isChecked === true);
}
this.callChange(this.isChecked);
this.callTouched();

4
src/Squidex/app/framework/angular/http/caching.interceptor.ts

@ -21,13 +21,13 @@ export class CachingInterceptor implements HttpInterceptor {
const cacheEntry = this.cache[req.url];
if (cacheEntry) {
req = req.clone({ headers: req.headers.set('If-None-Match', cacheEntry.headers.get('Etag')!) });
req = req.clone({ headers: req.headers.set('If-None-Match', cacheEntry.headers.get('ETag')!) });
}
return next.handle(req).pipe(
tap(response => {
if (Types.is(response, HttpResponse)) {
if (response.headers.get('Etag')) {
if (response.headers.get('ETag')) {
this.cache[req.url] = response;
}
}

2
src/Squidex/app/shared/components/language-selector.component.html

@ -10,7 +10,7 @@
</button>
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="button" @fade>
<div class="dropdown-item" *ngFor="let language of languages" [class.active]="language == selectedLanguage" (click)="selectLanguage(language)">
<strong class="iso-code">{{language.iso2Code}}</strong> ({{language.englishName}})
<strong class="iso-code iso-code-dropdown">{{language.iso2Code}}</strong> ({{language.englishName}})
</div>
</div>
</div>

3
src/Squidex/app/shared/components/language-selector.component.scss

@ -7,6 +7,9 @@
.iso-code {
font-family: monospace;
}
.iso-code-dropdown {
display: inline-block;
min-width: 40px;
max-width: 60px;

7
src/Squidex/appsettings.json

@ -18,6 +18,13 @@
"enforceHttps": false
},
"etags": {
/*
* Set to true, to use strong etags.
*/
"strong": false
},
"ui": {
/*
* Regex suggestions for the UI

392
src/Squidex/package-lock.json

@ -136,7 +136,7 @@
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"dev": true,
"requires": {
@ -156,7 +156,7 @@
},
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"dev": true,
"requires": {
@ -928,9 +928,9 @@
"dev": true
},
"@types/jasmine": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.4.tgz",
"integrity": "sha512-543S+ZCJfN4jKWzRkptbJqTY2vc4h7+lPVqU2hXb1XFofDcUxNANAimdZPYaH6/yhezVAsNeujoZjAFU06bfmA==",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.5.tgz",
"integrity": "sha512-LJtc52O1PNUffMvH6Q3fS0BOhQWYlkh3SVu/Jc4GoPgJkUytk5Y6YPbw+6lZK2mWWvG62BtVyOFw0ih7r8STsw==",
"dev": true
},
"@types/json5": {
@ -964,9 +964,9 @@
"dev": true
},
"@types/react": {
"version": "16.7.17",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.7.17.tgz",
"integrity": "sha512-YcXcaoXaxo7A76mBCGlKlN2aZu3REQfF0DTrhiyXVJLA7PDdxVCr+wiQOrkVNn44D/zLlIyDSn3U918Ve0AaEA==",
"version": "16.7.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.7.18.tgz",
"integrity": "sha512-Tx4uu3ppK53/iHk6VpamMP3f3ahfDLEVt3ZQc8TFm30a1H3v9lMsCntBREswZIW/SKrvJjkb3Hq8UwO6GREBng==",
"dev": true,
"requires": {
"@types/prop-types": "*",
@ -983,9 +983,9 @@
}
},
"@types/sortablejs": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.7.1.tgz",
"integrity": "sha512-nNHmeP9dXTLxd9OmH6oci2P0SoDnobjC5Tilf+IUi0+TGFuHYnkHyH6TrWvP9r2ZJdkrWMtoA6X4J556Ao9i4w==",
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.7.2.tgz",
"integrity": "sha512-yIxpbtlfhaFi2QyuUK54XcmzDWZf5i11CgTrMO4Vh+sKKZthonizkTcqhADeHdngDNTDVUCYfIcfIvpZRAZY+A==",
"dev": true
},
"@webassemblyjs/ast": {
@ -1314,9 +1314,9 @@
}
},
"ansi-colors": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.1.tgz",
"integrity": "sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ==",
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
"integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
"dev": true
},
"ansi-escapes": {
@ -1446,9 +1446,9 @@
"dev": true
},
"array-flatten": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz",
"integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
"dev": true
},
"array-map": {
@ -2003,7 +2003,7 @@
"dependencies": {
"jsesc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true
},
@ -2338,9 +2338,9 @@
"dev": true
},
"bootstrap": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz",
"integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w=="
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.2.1.tgz",
"integrity": "sha512-tt/7vIv3Gm2mnd/WeDx36nfGGHleil0Wg8IeB7eMrVkY0fZ5iTaBisSh8oNANc2IBsCc6vCgCNTIM/IEN0+50Q=="
},
"brace-expansion": {
"version": "1.1.11",
@ -2520,35 +2520,60 @@
"dev": true
},
"cacache": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-11.2.0.tgz",
"integrity": "sha512-IFWl6lfK6wSeYCHUXh+N1lY72UDrpyrYQJNIVQf48paDuWbv5RbAtJYf/4gUQFObTCHZwdZ5sI8Iw7nqwP6nlQ==",
"version": "11.3.2",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
"integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
"dev": true,
"requires": {
"bluebird": "^3.5.1",
"chownr": "^1.0.1",
"figgy-pudding": "^3.1.0",
"glob": "^7.1.2",
"graceful-fs": "^4.1.11",
"lru-cache": "^4.1.3",
"bluebird": "^3.5.3",
"chownr": "^1.1.1",
"figgy-pudding": "^3.5.1",
"glob": "^7.1.3",
"graceful-fs": "^4.1.15",
"lru-cache": "^5.1.1",
"mississippi": "^3.0.0",
"mkdirp": "^0.5.1",
"move-concurrently": "^1.0.1",
"promise-inflight": "^1.0.1",
"rimraf": "^2.6.2",
"ssri": "^6.0.0",
"unique-filename": "^1.1.0",
"ssri": "^6.0.1",
"unique-filename": "^1.1.1",
"y18n": "^4.0.0"
},
"dependencies": {
"bluebird": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
"integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==",
"dev": true
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
"dev": true
},
"lru-cache": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz",
"integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
"yallist": "^3.0.2"
}
},
"y18n": {
@ -2556,6 +2581,12 @@
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true
}
}
},
@ -3371,23 +3402,49 @@
}
},
"css-loader": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.0.1.tgz",
"integrity": "sha512-XIVwoIOzSFRVsafOKa060GJ/A70c0IP/C1oVPHEX4eHIFF39z0Jl7j8Kua1SUTiqWDupUnbY3/yQx9r7EUB35w==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.0.tgz",
"integrity": "sha512-MoOu+CStsGrSt5K2OeZ89q3Snf+IkxRfAIt9aAKg4piioTrhtP1iEFPu+OVn3Ohz24FO6L+rw9UJxBILiSBw5Q==",
"dev": true,
"requires": {
"icss-utils": "^4.0.0",
"loader-utils": "^1.0.2",
"loader-utils": "^1.2.1",
"lodash": "^4.17.11",
"postcss": "^7.0.6",
"postcss-modules-extract-imports": "^2.0.0",
"postcss-modules-local-by-default": "^2.0.2",
"postcss-modules-local-by-default": "^2.0.3",
"postcss-modules-scope": "^2.0.0",
"postcss-modules-values": "^2.0.0",
"postcss-value-parser": "^3.3.0",
"schema-utils": "^1.0.0"
},
"dependencies": {
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
"json5": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^2.0.0",
"json5": "^1.0.1"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
@ -3647,9 +3704,9 @@
}
},
"csstype": {
"version": "2.5.8",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.8.tgz",
"integrity": "sha512-r4DbsyNJ7slwBSKoGesxDubRWJ71ghG8W2+1HcsDlAo12KGca9dDLv0u98tfdFw7ldBdoA7XmCnI6Q8LpAJXaQ==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.0.tgz",
"integrity": "sha512-by8hi8BlLbowQq0qtkx54d9aN73R9oUW20HISpka5kmgsR9F7nnxgfsemuR2sdCKZh+CDNf5egW9UZMm4mgJRg==",
"dev": true
},
"currently-unhandled": {
@ -4454,14 +4511,6 @@
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
},
"dependencies": {
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"dev": true
}
}
},
"espree": {
@ -4496,6 +4545,12 @@
}
}
},
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"dev": true
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
@ -5111,14 +5166,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -5133,14 +5186,12 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
@ -5262,8 +5313,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@ -5275,7 +5325,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -5290,7 +5339,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -5402,8 +5450,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@ -5536,7 +5583,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -5821,7 +5867,7 @@
},
"graphql-config": {
"version": "2.0.1",
"resolved": "http://registry.npmjs.org/graphql-config/-/graphql-config-2.0.1.tgz",
"resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.0.1.tgz",
"integrity": "sha512-eb4FzlODifHE/Q+91QptAmkGw39wL5ToinJ2556UUsGt2drPc4tzifL+HSnHSaxiIbH8EUhc/Fa6+neinF04qA==",
"requires": {
"graphql-import": "^0.4.4",
@ -5833,7 +5879,7 @@
},
"graphql-import": {
"version": "0.4.5",
"resolved": "http://registry.npmjs.org/graphql-import/-/graphql-import-0.4.5.tgz",
"resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.4.5.tgz",
"integrity": "sha512-G/+I08Qp6/QGTb9qapknCm3yPHV0ZL7wbaalWFpxsfR8ZhZoTBe//LsbsCKlbALQpcMegchpJhpTSKiJjhaVqQ==",
"requires": {
"lodash": "^4.17.4"
@ -5885,9 +5931,9 @@
}
},
"handle-thing": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
"integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
"integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==",
"dev": true
},
"handlebars": {
@ -9128,9 +9174,9 @@
"dev": true
},
"oidc-client": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/oidc-client/-/oidc-client-1.6.0.tgz",
"integrity": "sha512-u0cs487mv5x7rcS5fLfYgH6h8DZySRU8HDQlyrICt6zR/d4zYKzdjr4pmqEROqvkOA1YYdlK19A9eMi3xlmnlQ==",
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/oidc-client/-/oidc-client-1.6.1.tgz",
"integrity": "sha512-buA9G0hlFjUwxoL/xuHunBtTgoICtJSojnZtATqMliUTKxPYAzHprOQ85Lj0hgF+Zv8lI/ViqaNFDG0Z5KCUKA==",
"requires": {
"babel-polyfill": ">=6.9.1",
"jsrsasign": "^8.0.12"
@ -9283,7 +9329,7 @@
},
"p-is-promise": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
"resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
"integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
"dev": true
},
@ -9541,9 +9587,9 @@
"dev": true
},
"portfinder": {
"version": "1.0.19",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.19.tgz",
"integrity": "sha512-23aeQKW9KgHe6citUrG3r9HjeX6vls0h713TAa+CwTKZwNIr/pD2ApaxYF4Um3ZZyq4ar+Siv3+fhoHaIwSOSw==",
"version": "1.0.20",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
"integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==",
"dev": true,
"requires": {
"async": "^1.5.2",
@ -10048,9 +10094,9 @@
}
},
"postcss-modules-local-by-default": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.2.tgz",
"integrity": "sha512-qghHvHeydUBQ3EQic5NjYryZ5jzXzAYxHR7lZQlCNmjGpJtINRyX/ELnh/7fxBBmHNkEzNkq2l5cV6trfidYng==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.3.tgz",
"integrity": "sha512-jv4CQ8IQ0+TkaAIP7H4kgu/jQbrjte8xU61SYJAIOby+o3H0MGWX6eN1WXUKHccK6/EEjcAERjyIP8MXzAWAbQ==",
"dev": true,
"requires": {
"css-selector-tokenizer": "^0.7.0",
@ -11522,9 +11568,9 @@
}
},
"serialize-javascript": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz",
"integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==",
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz",
"integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==",
"dev": true
},
"serve-index": {
@ -12049,32 +12095,75 @@
"dev": true
},
"spdy": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz",
"integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz",
"integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"handle-thing": "^1.2.5",
"debug": "^4.1.0",
"handle-thing": "^2.0.0",
"http-deceiver": "^1.2.7",
"safe-buffer": "^5.0.1",
"select-hose": "^2.0.0",
"spdy-transport": "^2.0.18"
"spdy-transport": "^3.0.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
}
}
},
"spdy-transport": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz",
"integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
"integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"detect-node": "^2.0.3",
"debug": "^4.1.0",
"detect-node": "^2.0.4",
"hpack.js": "^2.1.6",
"obuf": "^1.1.1",
"readable-stream": "^2.2.9",
"safe-buffer": "^5.0.1",
"wbuf": "^1.7.2"
"obuf": "^1.1.2",
"readable-stream": "^3.0.6",
"wbuf": "^1.7.3"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"readable-stream": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
"integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"split-string": {
@ -12552,9 +12641,9 @@
}
},
"terser-webpack-plugin": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz",
"integrity": "sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz",
"integrity": "sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw==",
"dev": true,
"requires": {
"cacache": "^11.0.2",
@ -12580,12 +12669,12 @@
"dev": true
},
"through2": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
"integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
"dev": true,
"requires": {
"readable-stream": "^2.1.5",
"readable-stream": "~2.3.6",
"xtend": "~4.0.1"
}
},
@ -12745,7 +12834,7 @@
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
@ -12797,9 +12886,9 @@
}
},
"tslint-webpack-plugin": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/tslint-webpack-plugin/-/tslint-webpack-plugin-1.3.0.tgz",
"integrity": "sha512-/lGWgmz9TQ7+XdFOrmzRdrpCh96oUt8JmqSzg6RQrjJs+Ga9Fa+pTCWJKlCh7M0ArAyaIKMR5jzLGndqku1m2w==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tslint-webpack-plugin/-/tslint-webpack-plugin-2.0.0.tgz",
"integrity": "sha512-9fcU4TC2O31nhLhAHUqMIeSgsnnGW6QReW+aTcpdJtN6SkiFPANzIqvyhqZzkvMxhk2M2UQy/9BuujxzpfvWMg==",
"dev": true,
"requires": {
"chalk": "^2.1.0"
@ -12908,9 +12997,9 @@
"optional": true
},
"uglifyjs-webpack-plugin": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.0.1.tgz",
"integrity": "sha512-1HhCHkOB6wRCcv7htcz1QRPVbWPEY074RP9vzt/X0LF4xXm9l4YGd0qja7z88abDixQlnVwBjXsTBs+Xsn/eeQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.1.1.tgz",
"integrity": "sha512-TQEcyMNkObX/H+FfcKjiDgs5RcXX8vW2UUUrDTOfQgg3lrafztfeM5WAwXo+AzqozJK6NP9w98xNpG/dutzSsg==",
"dev": true,
"requires": {
"cacache": "^11.2.0",
@ -13116,9 +13205,9 @@
"dev": true
},
"url-parse": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
"integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz",
"integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
"dev": true,
"requires": {
"querystringify": "^2.0.0",
@ -13611,9 +13700,9 @@
}
},
"webpack": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.1.tgz",
"integrity": "sha512-qAS7BFyS5iuOZzGJxyDXmEI289h7tVNtJ5XMxf6Tz55J2riOyH42uaEsWF0F32TRaI+54SmI6qRgHM3GzsZ+sQ==",
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.2.tgz",
"integrity": "sha512-PK3uVg3/NuNVOjPfYleFI6JF7khO7c2kIlksH7mivQm+QDcwiqV1x6+q89dDeOioh5FNxJHr3LKbDu3oSAhl9g==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.7.11",
@ -14129,9 +14218,9 @@
}
},
"webpack-dev-server": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz",
"integrity": "sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww==",
"version": "3.1.14",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz",
"integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==",
"dev": true,
"requires": {
"ansi-html": "0.0.7",
@ -14153,12 +14242,14 @@
"portfinder": "^1.0.9",
"schema-utils": "^1.0.0",
"selfsigned": "^1.9.1",
"semver": "^5.6.0",
"serve-index": "^1.7.2",
"sockjs": "0.3.19",
"sockjs-client": "1.3.0",
"spdy": "^3.4.1",
"spdy": "^4.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^5.1.0",
"url": "^0.11.0",
"webpack-dev-middleware": "3.4.0",
"webpack-log": "^2.0.0",
"yargs": "12.0.2"
@ -14270,6 +14361,19 @@
}
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
@ -14310,6 +14414,21 @@
"rimraf": "^2.2.8"
}
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@ -14479,6 +14598,15 @@
"locate-path": "^3.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@ -14640,12 +14768,12 @@
}
},
"os-locale": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz",
"integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"execa": "^0.10.0",
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
@ -14656,6 +14784,12 @@
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",

31
src/Squidex/package.json

@ -5,15 +5,14 @@
"license": "MIT",
"repository": "https://github.com/SebastianStehle/Squidex",
"scripts": {
"copy": "cpx node_modules/oidc-client/dist/oidc-client.min.js wwwroot/scripts/",
"start": "npm run copy && webpack-dev-server --config app-config/webpack.run.dev.js --inline --port 3000 --hot",
"test": "karma start",
"test:coverage": "karma start karma.coverage.conf.js",
"test:clean": "rimraf _test-output",
"dev": "cpx node_modules/oidc-client/dist/oidc-client.min.js wwwroot/scripts/ && webpack-dev-server --config app-config/webpack.run.dev.js --inline --port 3000",
"start": "cpx node_modules/oidc-client/dist/oidc-client.min.js wwwroot/scripts/ && webpack-dev-server --config app-config/webpack.run.dev.js --inline --port 3000 --hot",
"build": "webpack --config app-config/webpack.run.prod.js",
"build:copy": "cpx node_modules/oidc-client/dist/oidc-client.min.js wwwroot/scripts/",
"build:clean": "rimraf wwwroot/build",
"tslint": "tslint -c tslint.json -p tsconfig.json app/**/*.ts"
"tslint": "tslint -c tslint.json -p tsconfig.json app/**/*.ts",
"build": "npm run copy && webpack --config app-config/webpack.run.prod.js",
"build:clean": "rimraf wwwroot/build"
},
"dependencies": {
"@angular/animations": "7.1.4",
@ -27,7 +26,7 @@
"@angular/router": "7.1.4",
"angular2-chartjs": "0.5.1",
"babel-polyfill": "6.26.0",
"bootstrap": "4.1.3",
"bootstrap": "4.2.1",
"core-js": "2.6.1",
"graphiql": "0.12.0",
"graphql": "14.0.2",
@ -35,7 +34,7 @@
"mousetrap": "1.6.2",
"ng2-dnd": "5.0.2",
"ngx-color-picker": "7.2.0",
"oidc-client": "1.6.0",
"oidc-client": "1.6.1",
"pikaday": "1.8.0",
"progressbar.js": "1.0.1",
"react": "16.7.0",
@ -51,19 +50,19 @@
"@angular/compiler-cli": "7.1.4",
"@ngtools/webpack": "7.1.4",
"@types/core-js": "2.5.0",
"@types/jasmine": "3.3.4",
"@types/jasmine": "3.3.5",
"@types/mousetrap": "1.6",
"@types/node": "10.12.18",
"@types/react": "16.7.17",
"@types/react": "16.7.18",
"@types/react-dom": "16.0.11",
"@types/sortablejs": "1.7.1",
"@types/sortablejs": "1.7.2",
"angular-router-loader": "0.8.5",
"angular2-template-loader": "0.6.2",
"awesome-typescript-loader": "5.2.1",
"babel-core": "6.26.3",
"codelyzer": "4.5.0",
"cpx": "1.5.0",
"css-loader": "2.0.1",
"css-loader": "2.1.0",
"file-loader": "3.0.1",
"html-loader": "0.5.5",
"html-webpack-plugin": "3.2.0",
@ -91,14 +90,14 @@
"style-loader": "0.23.1",
"tsconfig-paths-webpack-plugin": "3.2.0",
"tslint": "5.12.0",
"tslint-webpack-plugin": "1.3.0",
"tslint-webpack-plugin": "2.0.0",
"typemoq": "2.1.0",
"typescript": "3.1.1",
"uglifyjs-webpack-plugin": "2.0.1",
"uglifyjs-webpack-plugin": "2.1.1",
"underscore": "1.9.1",
"webpack": "4.28.1",
"webpack": "4.28.2",
"webpack-cli": "3.1.2",
"webpack-dev-server": "3.1.10",
"webpack-dev-server": "3.1.14",
"webpack-merge": "4.1.5"
}
}

10
src/Squidex/wwwroot/scripts/editor-sdk.js

@ -44,7 +44,7 @@ function SquidexFormField() {
}, 500);
var editor = {
/*
/**
* Notifies the control container that the editor has been touched.
*/
touched: function () {
@ -53,7 +53,7 @@ function SquidexFormField() {
}
},
/*
/**
* Notifies the control container that the value has been changed.
*/
valueChanged: function (value) {
@ -62,7 +62,7 @@ function SquidexFormField() {
}
},
/*
/**
* Register the disabled handler.
*/
onDisabled: function (callback) {
@ -73,7 +73,7 @@ function SquidexFormField() {
}
},
/*
/**
* Register the disabled handler.
*/
onValueChanged: function (callback) {
@ -84,7 +84,7 @@ function SquidexFormField() {
}
},
/*
/**
* Clean the editor SDK.
*/
clean: function () {

24
tests/Squidex.Tests/Pipeline/CommandMiddlewares/ETagCommandMiddlewareTests.cs

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure.Commands;
using Xunit;
@ -19,13 +20,13 @@ namespace Squidex.Pipeline.CommandMiddlewares
{
private readonly IHttpContextAccessor httpContextAccessor = A.Fake<IHttpContextAccessor>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly IHeaderDictionary requestHeaders = new HeaderDictionary();
private readonly HttpContext httpContext = new DefaultHttpContext();
private readonly ETagCommandMiddleware sut;
public ETagCommandMiddlewareTests()
{
A.CallTo(() => httpContextAccessor.HttpContext.Request.Headers)
.Returns(requestHeaders);
A.CallTo(() => httpContextAccessor.HttpContext)
.Returns(httpContext);
sut = new ETagCommandMiddleware(httpContextAccessor);
}
@ -47,7 +48,20 @@ namespace Squidex.Pipeline.CommandMiddlewares
[Fact]
public async Task Should_add_expected_version_to_command()
{
requestHeaders["If-Match"] = "13";
httpContext.Request.Headers[HeaderNames.IfMatch] = "13";
var command = new CreateContent();
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
Assert.Equal(13, context.Command.ExpectedVersion);
}
[Fact]
public async Task Should_add_weak_etag_as_expected_version_to_command()
{
httpContext.Request.Headers[HeaderNames.IfMatch] = "W/13";
var command = new CreateContent();
var context = new CommandContext(command, commandBus);
@ -67,7 +81,7 @@ namespace Squidex.Pipeline.CommandMiddlewares
await sut.HandleAsync(context);
Assert.Equal(new StringValues("17"), httpContextAccessor.HttpContext.Response.Headers["ETag"]);
Assert.Equal(new StringValues("17"), httpContextAccessor.HttpContext.Response.Headers[HeaderNames.ETag]);
}
}
}

87
tests/Squidex.Tests/Pipeline/ETagFilterTests.cs

@ -0,0 +1,87 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Squidex.Pipeline
{
public class ETagFilterTests
{
private readonly HttpContext httpContext = new DefaultHttpContext();
private readonly ActionExecutingContext executingContext;
private readonly ActionExecutedContext executedContext;
private readonly ETagFilter sut = new ETagFilter(Options.Create(new ETagOptions()));
public ETagFilterTests()
{
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var filters = new List<IFilterMetadata>();
executingContext = new ActionExecutingContext(actionContext, filters, new Dictionary<string, object>(), this);
executedContext = new ActionExecutedContext(actionContext, filters, this)
{
Result = new OkResult()
};
}
[Fact]
public async Task Should_convert_strong_to_weak_tag()
{
httpContext.Response.Headers[HeaderNames.ETag] = "13";
await sut.OnActionExecutionAsync(executingContext, () => Task.FromResult(executedContext));
Assert.Equal("W/13", httpContext.Response.Headers[HeaderNames.ETag]);
}
[Fact]
public async Task Should_not_convert_empty_strong_to_weak_tag()
{
httpContext.Response.Headers[HeaderNames.ETag] = string.Empty;
await sut.OnActionExecutionAsync(executingContext, () => Task.FromResult(executedContext));
Assert.Null((string)httpContext.Response.Headers[HeaderNames.ETag]);
}
[Fact]
public async Task Should_return_304_for_same_etags()
{
httpContext.Request.Method = HttpMethods.Get;
httpContext.Request.Headers[HeaderNames.IfNoneMatch] = "W/13";
httpContext.Response.Headers[HeaderNames.ETag] = "13";
await sut.OnActionExecutionAsync(executingContext, () => Task.FromResult(executedContext));
Assert.Equal(304, (executedContext.Result as StatusCodeResult).StatusCode);
}
[Fact]
public async Task Should_not_return_304_for_different_etags()
{
httpContext.Request.Method = HttpMethods.Get;
httpContext.Request.Headers[HeaderNames.IfNoneMatch] = "W/11";
httpContext.Response.Headers[HeaderNames.ETag] = "13";
await sut.OnActionExecutionAsync(executingContext, () => Task.FromResult(executedContext));
Assert.Equal(200, (executedContext.Result as StatusCodeResult).StatusCode);
}
}
}
Loading…
Cancel
Save