mirror of https://github.com/Squidex/squidex.git
1690 changed files with 61757 additions and 38440 deletions
@ -0,0 +1,21 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2017 Qaisar Ahmad & Sebastian Stehle |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -1,21 +0,0 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2017 Qaisar Ahmad & Sebastian Stehle |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -1,7 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<configuration> |
|||
<packageSources> |
|||
<add key="localfeed" value="libs" /> |
|||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> |
|||
</packageSources> |
|||
</configuration> |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<configuration> |
|||
<packageSources> |
|||
<add key="myget.org" value="https://dotnet.myget.org/F/orleans-ci/api/v3/index.json" protocolVersion="3" /> |
|||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> |
|||
</packageSources> |
|||
</configuration> |
|||
@ -1,50 +0,0 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Djs/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Djs/@EntryIndexRemoved">True</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecshtml/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecshtml/@EntryIndexRemoved">True</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecss/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecss/@EntryIndexRemoved">True</s:Boolean> |
|||
|
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ehtml/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ehtml/@EntryIndexRemoved">True</s:Boolean> |
|||
|
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejs/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejs/@EntryIndexRemoved">True</s:Boolean> |
|||
|
|||
|
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejson/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejson/@EntryIndexRemoved">True</s:Boolean> |
|||
|
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Escss/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Escss/@EntryIndexRemoved">True</s:Boolean> |
|||
|
|||
|
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ets/@EntryIndexedValue">False</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ets/@EntryIndexRemoved">True</s:Boolean> |
|||
|
|||
|
|||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002E_002A/@EntryIndexedValue">True</s:Boolean> |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Header/@EntryIndexedValue"><?xml version="1.0" encoding="utf-16"?><Profile name="Header"><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile></s:String> |
|||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Namespaces/@EntryIndexedValue"><?xml version="1.0" encoding="utf-16"?><Profile name="Namespaces"><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile></s:String> |
|||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Typescript/@EntryIndexedValue"><?xml version="1.0" encoding="utf-16"?><Profile name="Typescript"><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs></Profile></s:String> |
|||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean> |
|||
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">==========================================================================
 |
|||
Squidex Headless CMS
 |
|||
==========================================================================
 |
|||
Copyright (c) Squidex UG (haftungsbeschraenkt)
 |
|||
All rights reserved. Licensed under the MIT license.
 |
|||
==========================================================================
 |
|||
</s:String> |
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean> |
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean> |
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean> |
|||
</wpf:ResourceDictionary> |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// 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.Algolia |
|||
{ |
|||
[RuleActionHandler(typeof(AlgoliaActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M16 .842C7.633.842.842 7.625.842 16S7.625 31.158 16 31.158c8.374 0 15.158-6.791 15.158-15.166S24.375.842 16 .842zm0 25.83c-5.898 0-10.68-4.781-10.68-10.68S10.101 5.313 16 5.313s10.68 4.781 10.68 10.679-4.781 10.68-10.68 10.68zm0-19.156v7.956c0 .233.249.388.458.279l7.055-3.663a.312.312 0 0 0 .124-.434 8.807 8.807 0 0 0-7.319-4.447z'/></svg>", |
|||
IconColor = "#0d9bf9", |
|||
Display = "Populate Algolia index", |
|||
Description = "Populate and synchronize indices in Algolia for full text search.", |
|||
ReadMore = "https://www.algolia.com/")] |
|||
public sealed class AlgoliaAction : RuleAction |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Application Id", Description = "The application ID.")] |
|||
public string AppId { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Api Key", Description = "The API key to grant access to Squidex.")] |
|||
public string ApiKey { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Index Name", Description = "THe name of the index.")] |
|||
public string IndexName { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Algolia.Search; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
|
|||
namespace Squidex.Extensions.Actions.Algolia |
|||
{ |
|||
public sealed class AlgoliaActionHandler : RuleActionHandler<AlgoliaAction, AlgoliaJob> |
|||
{ |
|||
private const string DescriptionIgnore = "Ignore"; |
|||
|
|||
private readonly ClientPool<(string AppId, string ApiKey, string IndexName), Index> clients; |
|||
|
|||
public AlgoliaActionHandler(RuleEventFormatter formatter) |
|||
: base(formatter) |
|||
{ |
|||
clients = new ClientPool<(string AppId, string ApiKey, string IndexName), Index>(key => |
|||
{ |
|||
var client = new AlgoliaClient(key.AppId, key.ApiKey); |
|||
|
|||
return client.InitIndex(key.IndexName); |
|||
}); |
|||
} |
|||
|
|||
protected override (string Description, AlgoliaJob Data) CreateJob(EnrichedEvent @event, AlgoliaAction action) |
|||
{ |
|||
if (@event is EnrichedContentEvent contentEvent) |
|||
{ |
|||
var contentId = contentEvent.Id.ToString(); |
|||
|
|||
var ruleDescription = string.Empty; |
|||
var ruleJob = new AlgoliaJob |
|||
{ |
|||
AppId = action.AppId, |
|||
ApiKey = action.ApiKey, |
|||
ContentId = contentId, |
|||
IndexName = Format(action.IndexName, @event) |
|||
}; |
|||
|
|||
if (contentEvent.Type == EnrichedContentEventType.Deleted || |
|||
contentEvent.Type == EnrichedContentEventType.Unpublished) |
|||
{ |
|||
ruleDescription = $"Delete entry from Algolia index: {action.IndexName}"; |
|||
} |
|||
else |
|||
{ |
|||
ruleDescription = $"Add entry to Algolia index: {action.IndexName}"; |
|||
|
|||
ruleJob.Content = ToPayload(contentEvent); |
|||
ruleJob.Content["objectID"] = contentId; |
|||
} |
|||
|
|||
return (ruleDescription, ruleJob); |
|||
} |
|||
|
|||
return (DescriptionIgnore, new AlgoliaJob()); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(AlgoliaJob job) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(job.AppId)) |
|||
{ |
|||
return (DescriptionIgnore, null); |
|||
} |
|||
|
|||
var index = clients.GetClient((job.AppId, job.ApiKey, job.IndexName)); |
|||
|
|||
try |
|||
{ |
|||
if (job.Content != null) |
|||
{ |
|||
var response = await index.PartialUpdateObjectAsync(job.Content); |
|||
|
|||
return (response.ToString(Formatting.Indented), null); |
|||
} |
|||
else |
|||
{ |
|||
var response = await index.DeleteObjectAsync(job.ContentId); |
|||
|
|||
return (response.ToString(Formatting.Indented), null); |
|||
} |
|||
} |
|||
catch (AlgoliaException ex) |
|||
{ |
|||
return (ex.Message, ex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class AlgoliaJob |
|||
{ |
|||
public string AppId { get; set; } |
|||
|
|||
public string ApiKey { get; set; } |
|||
|
|||
public string ContentId { get; set; } |
|||
|
|||
public string IndexName { get; set; } |
|||
|
|||
public JObject Content { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Text.RegularExpressions; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.AzureQueue |
|||
{ |
|||
[RuleActionHandler(typeof(AzureQueueActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M.011 16L0 6.248l12-1.63V16zM14 4.328L29.996 2v14H14zM30 18l-.004 14L14 29.75V18zM12 29.495L.01 27.851.009 18H12z'/></svg>", |
|||
IconColor = "#0d9bf9", |
|||
Display = "Send to Azure Queue", |
|||
Description = "Send an event to azure queue storage.", |
|||
ReadMore = "https://azure.microsoft.com/en-us/services/storage/queues/")] |
|||
public sealed class AzureQueueAction : RuleAction |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Connection String", Description = "The connection string to the storage account.")] |
|||
public string ConnectionString { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Queue", Description = "The name of the queue.")] |
|||
public string Queue { get; set; } |
|||
|
|||
protected override IEnumerable<ValidationError> CustomValidate() |
|||
{ |
|||
if (!string.IsNullOrWhiteSpace(Queue) && !Regex.IsMatch(Queue, "^[a-z][a-z0-9]{2,}(\\-[a-z0-9]+)*$")) |
|||
{ |
|||
yield return new ValidationError("Queue must be valid azure queue name.", nameof(Queue)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.WindowsAzure.Storage; |
|||
using Microsoft.WindowsAzure.Storage.Queue; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
|
|||
namespace Squidex.Extensions.Actions.AzureQueue |
|||
{ |
|||
public sealed class AzureQueueActionHandler : RuleActionHandler<AzureQueueAction, AzureQueueJob> |
|||
{ |
|||
private readonly ClientPool<(string ConnectionString, string QueueName), CloudQueue> clients; |
|||
|
|||
public AzureQueueActionHandler(RuleEventFormatter formatter) |
|||
: base(formatter) |
|||
{ |
|||
clients = new ClientPool<(string ConnectionString, string QueueName), CloudQueue>(key => |
|||
{ |
|||
var storageAccount = CloudStorageAccount.Parse(key.ConnectionString); |
|||
|
|||
var queueClient = storageAccount.CreateCloudQueueClient(); |
|||
var queueRef = queueClient.GetQueueReference(key.QueueName); |
|||
|
|||
return queueRef; |
|||
}); |
|||
} |
|||
|
|||
protected override (string Description, AzureQueueJob Data) CreateJob(EnrichedEvent @event, AzureQueueAction action) |
|||
{ |
|||
var queueName = Format(action.Queue, @event); |
|||
|
|||
var ruleDescription = $"Send AzureQueueJob to azure queue '{queueName}'"; |
|||
var ruleJob = new AzureQueueJob |
|||
{ |
|||
QueueConnectionString = action.ConnectionString, |
|||
QueueName = queueName, |
|||
MessageBodyV2 = ToEnvelopeJson(@event) |
|||
}; |
|||
|
|||
return (ruleDescription, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(AzureQueueJob job) |
|||
{ |
|||
var queue = clients.GetClient((job.QueueConnectionString, job.QueueName)); |
|||
|
|||
await queue.AddMessageAsync(new CloudQueueMessage(job.MessageBodyV2)); |
|||
|
|||
return ("Completed", null); |
|||
} |
|||
} |
|||
|
|||
public sealed class AzureQueueJob |
|||
{ |
|||
public string QueueConnectionString { get; set; } |
|||
|
|||
public string QueueName { get; set; } |
|||
|
|||
public string MessageBodyV2 { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
#pragma warning disable RECS0108 // Warns about static fields in generic types
|
|||
|
|||
namespace Squidex.Extensions.Actions |
|||
{ |
|||
internal sealed class ClientPool<TKey, TClient> |
|||
{ |
|||
private static readonly TimeSpan TTL = TimeSpan.FromMinutes(30); |
|||
private readonly MemoryCache memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); |
|||
private readonly Func<TKey, Task<TClient>> factory; |
|||
|
|||
public ClientPool(Func<TKey, TClient> factory) |
|||
{ |
|||
this.factory = x => Task.FromResult(factory(x)); |
|||
} |
|||
|
|||
public ClientPool(Func<TKey, Task<TClient>> factory) |
|||
{ |
|||
this.factory = factory; |
|||
} |
|||
|
|||
public TClient GetClient(TKey key) |
|||
{ |
|||
return GetClientAsync(key).Result; |
|||
} |
|||
|
|||
public async Task<TClient> GetClientAsync(TKey key) |
|||
{ |
|||
if (!memoryCache.TryGetValue<TClient>(key, out var client)) |
|||
{ |
|||
client = await factory(key); |
|||
|
|||
memoryCache.Set(key, client, TTL); |
|||
} |
|||
|
|||
return client; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Discourse |
|||
{ |
|||
[RuleActionHandler(typeof(DiscourseActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M16.137 0C7.376 0 0 7.037 0 15.721V32l16.134-.016C24.895 31.984 32 24.676 32 15.995S24.888 0 16.137 0zm.336 6.062a9.862 9.862 0 0 1 5.119 1.555l-.038-.023a.747.747 0 0 1 .05.033l-.033-.021c.288.183.529.353.762.534l-.022-.016c.058.044.094.073.131.103l-.018-.014c.218.174.411.34.597.514l-.005-.005a9.48 9.48 0 0 1 .639.655l.009.01c.073.082.154.176.233.272l.014.018c.053.06.116.133.177.206l.013.017-.052-.047-.008-.007c.104.126.218.273.328.423l.02.028.001.001-.001-.001c-.01-.018.005.005.019.028l.024.042c.145.206.301.451.445.704l.025.048c.131.226.273.51.402.801l.025.063a9.504 9.504 0 0 1 .802 3.853c0 5.38-4.401 9.741-9.831 9.741a9.866 9.866 0 0 1-4.106-.888l.061.025-6.39 1.43 1.78-5.672a7.888 7.888 0 0 1-.293-.584l-.025-.061a8.226 8.226 0 0 1-.254-.617l-.022-.068A1.043 1.043 0 0 1 7 19.017l-.022-.067a8.428 8.428 0 0 1-.246-.829l-.014-.067a9.402 9.402 0 0 1-.265-2.248c0-5.381 4.403-9.744 9.834-9.744l.194.002h-.01z'/></svg>", |
|||
IconColor = "#eB6121", |
|||
Display = "Post to discourse", |
|||
Description = "Create a post or topic at discourse.", |
|||
ReadMore = "https://www.discourse.org/")] |
|||
public sealed class DiscourseAction : RuleAction |
|||
{ |
|||
[AbsoluteUrl] |
|||
[Required] |
|||
[Display(Name = "Url", Description = "he url to the discourse server.")] |
|||
public Uri Url { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Api Key", Description = "The api key to authenticate to your discourse server.")] |
|||
public string ApiKey { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Api Username", Description = "The api username to authenticate to your discourse server.")] |
|||
public string ApiUsername { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Text", Description = "The text as markdown.")] |
|||
public string Text { get; set; } |
|||
|
|||
[Display(Name = "Title", Description = "The optional title when creating new topics.")] |
|||
public string Title { get; set; } |
|||
|
|||
[Display(Name = "Topic", Description = "The optional topic id.")] |
|||
public int? Topic { get; set; } |
|||
|
|||
[Display(Name = "Category", Description = "The optional category id.")] |
|||
public int? Category { get; set; } |
|||
} |
|||
} |
|||
@ -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.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
|
|||
namespace Squidex.Extensions.Actions.Discourse |
|||
{ |
|||
public sealed class DiscourseActionHandler : RuleActionHandler<DiscourseAction, DiscourseJob> |
|||
{ |
|||
private const string DescriptionCreatePost = "Create discourse Post"; |
|||
private const string DescriptionCreateTopic = "Create discourse Topic"; |
|||
|
|||
private readonly IHttpClientFactory httpClientFactory; |
|||
|
|||
public DiscourseActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) |
|||
: base(formatter) |
|||
{ |
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
protected override (string Description, DiscourseJob Data) CreateJob(EnrichedEvent @event, DiscourseAction action) |
|||
{ |
|||
var url = $"{action.Url.ToString().TrimEnd('/')}/posts.json?api_key={action.ApiKey}&api_username={action.ApiUsername}"; |
|||
|
|||
var json = |
|||
new JObject( |
|||
new JProperty("raw", Format(action.Text, @event)), |
|||
new JProperty("title", Format(action.Title, @event))); |
|||
|
|||
if (action.Topic.HasValue) |
|||
{ |
|||
json.Add(new JProperty("topic_id", action.Topic.Value)); |
|||
} |
|||
|
|||
if (action.Category.HasValue) |
|||
{ |
|||
json.Add(new JProperty("category", action.Category.Value)); |
|||
} |
|||
|
|||
var ruleJob = new DiscourseJob |
|||
{ |
|||
RequestUrl = url, |
|||
RequestBody = json.ToString() |
|||
}; |
|||
|
|||
var description = |
|||
action.Topic.HasValue ? |
|||
DescriptionCreateTopic : |
|||
DescriptionCreatePost; |
|||
|
|||
return (description, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(DiscourseJob job) |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl) |
|||
{ |
|||
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") |
|||
}; |
|||
|
|||
return await httpClient.OneWayRequestAsync(request, job.RequestBody); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class DiscourseJob |
|||
{ |
|||
public string RequestUrl { get; set; } |
|||
|
|||
public string RequestBody { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.ElasticSearch |
|||
{ |
|||
[RuleActionHandler(typeof(ElasticSearchActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 28'><path d='M13.427 17.436H4.163C3.827 16.354 3.636 15.2 3.636 14s.182-2.355.527-3.436h15.245c1.891 0 3.418 1.545 3.418 3.445a3.421 3.421 0 0 1-3.418 3.427h-5.982zm-.436 1.146H4.6a11.508 11.508 0 0 0 4.2 4.982 11.443 11.443 0 0 0 15.827-3.209 5.793 5.793 0 0 0-4.173-1.773H12.99zm7.464-9.164a5.794 5.794 0 0 0 4.173-1.773 11.45 11.45 0 0 0-9.536-5.1c-2.327 0-4.491.7-6.3 1.891a11.554 11.554 0 0 0-4.2 4.982h15.864z'/></svg>", |
|||
IconColor = "#1e5470", |
|||
Display = "Populate ElasticSearch index", |
|||
Description = "Populate and synchronize indices in ElasticSearch for full text search.", |
|||
ReadMore = "https://www.elastic.co/")] |
|||
public sealed class ElasticSearchAction : RuleAction |
|||
{ |
|||
[AbsoluteUrl] |
|||
[Required] |
|||
[Display(Name = "Host", Description = "The hostname of the elastic search instance or cluster.")] |
|||
public Uri Host { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Index Name", Description = "The name of the index.")] |
|||
public string IndexName { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Index Type", Description = "The name of the index type.")] |
|||
public string IndexType { get; set; } |
|||
|
|||
[Display(Name = "Username", Description = "The optional username.")] |
|||
public string Username { get; set; } |
|||
|
|||
[Display(Name = "Password", Description = "The optional password.")] |
|||
public string Password { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Elasticsearch.Net; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
|
|||
namespace Squidex.Extensions.Actions.ElasticSearch |
|||
{ |
|||
public sealed class ElasticSearchActionHandler : RuleActionHandler<ElasticSearchAction, ElasticSearchJob> |
|||
{ |
|||
private const string DescriptionIgnore = "Ignore"; |
|||
|
|||
private readonly ClientPool<(Uri Host, string Username, string Password), ElasticLowLevelClient> clients; |
|||
|
|||
public ElasticSearchActionHandler(RuleEventFormatter formatter) |
|||
: base(formatter) |
|||
{ |
|||
clients = new ClientPool<(Uri Host, string Username, string Password), ElasticLowLevelClient>(key => |
|||
{ |
|||
var config = new ConnectionConfiguration(key.Host); |
|||
|
|||
if (!string.IsNullOrEmpty(key.Username) && !string.IsNullOrWhiteSpace(key.Password)) |
|||
{ |
|||
config = config.BasicAuthentication(key.Username, key.Password); |
|||
} |
|||
|
|||
return new ElasticLowLevelClient(config); |
|||
}); |
|||
} |
|||
|
|||
protected override (string Description, ElasticSearchJob Data) CreateJob(EnrichedEvent @event, ElasticSearchAction action) |
|||
{ |
|||
if (@event is EnrichedContentEvent contentEvent) |
|||
{ |
|||
var contentId = contentEvent.Id.ToString(); |
|||
|
|||
var ruleDescription = string.Empty; |
|||
var ruleJob = new ElasticSearchJob |
|||
{ |
|||
Host = action.Host.ToString(), |
|||
ContentId = contentId, |
|||
IndexName = Format(action.IndexName, @event), |
|||
IndexType = Format(action.IndexType, @event) |
|||
}; |
|||
|
|||
if (contentEvent.Type == EnrichedContentEventType.Deleted || |
|||
contentEvent.Type == EnrichedContentEventType.Unpublished) |
|||
{ |
|||
ruleDescription = $"Delete entry index: {action.IndexName}"; |
|||
} |
|||
else |
|||
{ |
|||
ruleDescription = $"Upsert to index: {action.IndexName}"; |
|||
|
|||
ruleJob.Content = ToPayload(contentEvent); |
|||
ruleJob.Content["objectID"] = contentId; |
|||
} |
|||
|
|||
ruleJob.Username = action.Username; |
|||
ruleJob.Password = action.Password; |
|||
|
|||
return (ruleDescription, ruleJob); |
|||
} |
|||
|
|||
return (DescriptionIgnore, new ElasticSearchJob()); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(ElasticSearchJob job) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(job.Host)) |
|||
{ |
|||
return (DescriptionIgnore, null); |
|||
} |
|||
|
|||
var client = clients.GetClient((new Uri(job.Host, UriKind.Absolute), job.Username, job.Password)); |
|||
|
|||
try |
|||
{ |
|||
if (job.Content != null) |
|||
{ |
|||
var doc = job.Content.ToString(); |
|||
|
|||
var response = await client.IndexAsync<StringResponse>(job.IndexName, job.IndexType, job.ContentId, doc); |
|||
|
|||
return (response.Body, response.OriginalException); |
|||
} |
|||
else |
|||
{ |
|||
var response = await client.DeleteAsync<StringResponse>(job.IndexName, job.IndexType, job.ContentId); |
|||
|
|||
return (response.Body, response.OriginalException); |
|||
} |
|||
} |
|||
catch (ElasticsearchClientException ex) |
|||
{ |
|||
return (ex.Message, ex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class ElasticSearchJob |
|||
{ |
|||
public string Host { get; set; } |
|||
|
|||
public string Username { get; set; } |
|||
|
|||
public string Password { get; set; } |
|||
|
|||
public string ContentId { get; set; } |
|||
|
|||
public string IndexName { get; set; } |
|||
|
|||
public string IndexType { get; set; } |
|||
|
|||
public JObject Content { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// ==========================================================================
|
|||
// 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.Fastly |
|||
{ |
|||
[RuleActionHandler(typeof(FastlyActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 32'><path d='M10.68.948v1.736h.806v2.6A12.992 12.992 0 0 0 .951 18.051c0 7.178 5.775 12.996 12.9 12.996 7.124 0 12.9-5.819 12.9-12.996-.004-6.332-4.502-11.605-10.455-12.755l-.081-.013V2.684h.807V.948H10.68zm3.53 10.605c3.218.173 5.81 2.713 6.09 5.922v.211h-.734v.737h.734v.201c-.279 3.21-2.871 5.752-6.09 5.925v-.723h-.733v.721c-3.281-.192-5.905-2.845-6.077-6.152h.728v-.737h-.724c.195-3.284 2.808-5.911 6.073-6.103v.725h.733v-.727zm2.513 3.051l-2.462 2.282a1.13 1.13 0 0 0-.41-.078c-.633 0-1.147.517-1.147 1.155a1.15 1.15 0 0 0 1.147 1.155c.633 0 1.147-.517 1.147-1.155 0-.117-.018-.23-.05-.337l.002.008 2.223-2.505-.449-.526z'/></svg>", |
|||
IconColor = "#e23335", |
|||
Display = "Purge fastly cache", |
|||
Description = "Remove entries from the fastly CDN cache.", |
|||
ReadMore = "https://www.fastly.com/")] |
|||
public sealed class FastlyAction : RuleAction |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Api Key", Description = "The API key to grant access to Squidex.")] |
|||
public string ApiKey { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Service Id", Description = "The ID of the fastly service.")] |
|||
public string ServiceId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Fastly |
|||
{ |
|||
public sealed class FastlyActionHandler : RuleActionHandler<FastlyAction, FastlyJob> |
|||
{ |
|||
private const string Description = "Purge key in fastly"; |
|||
|
|||
private readonly IHttpClientFactory httpClientFactory; |
|||
|
|||
public FastlyActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) |
|||
: base(formatter) |
|||
{ |
|||
Guard.NotNull(httpClientFactory, nameof(httpClientFactory)); |
|||
|
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
protected override (string Description, FastlyJob Data) CreateJob(EnrichedEvent @event, FastlyAction action) |
|||
{ |
|||
var ruleJob = new FastlyJob |
|||
{ |
|||
Key = @event.AggregateId.ToString(), |
|||
FastlyApiKey = action.ApiKey, |
|||
FastlyServiceID = action.ServiceId |
|||
}; |
|||
|
|||
return (Description, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(FastlyJob job) |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
httpClient.Timeout = TimeSpan.FromSeconds(2); |
|||
|
|||
var requestUrl = $"https://api.fastly.com/service/{job.FastlyServiceID}/purge/{job.Key}"; |
|||
var request = new HttpRequestMessage(HttpMethod.Post, requestUrl); |
|||
|
|||
request.Headers.Add("Fastly-Key", job.FastlyApiKey); |
|||
|
|||
return await httpClient.OneWayRequestAsync(request); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class FastlyJob |
|||
{ |
|||
public string FastlyApiKey { get; set; } |
|||
|
|||
public string FastlyServiceID { get; set; } |
|||
|
|||
public string Key { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.Http; |
|||
|
|||
namespace Squidex.Extensions.Actions |
|||
{ |
|||
public static class HttpHelper |
|||
{ |
|||
public static async Task<(string Dump, Exception Exception)> OneWayRequestAsync(this HttpClient client, HttpRequestMessage request, string requestBody = null) |
|||
{ |
|||
HttpResponseMessage response = null; |
|||
try |
|||
{ |
|||
response = await client.SendAsync(request); |
|||
|
|||
var responseString = await response.Content.ReadAsStringAsync(); |
|||
|
|||
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, responseString); |
|||
|
|||
Exception ex = null; |
|||
|
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
ex = new HttpRequestException($"Response code does not indicate success: {(int)response.StatusCode} ({response.StatusCode})."); |
|||
} |
|||
|
|||
return (requestDump, ex); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
var requestDump = DumpFormatter.BuildDump(request, response, requestBody, ex.ToString()); |
|||
|
|||
return (requestDump, ex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// ==========================================================================
|
|||
// 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.Medium |
|||
{ |
|||
[RuleActionHandler(typeof(MediumActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M3.795 8.48a1.239 1.239 0 0 0-.404-1.045l-2.987-3.6v-.537H9.68l7.171 15.727 6.304-15.727H32v.537l-2.556 2.449a.749.749 0 0 0-.284.717v18a.749.749 0 0 0 .284.716l2.493 2.449v.537H19.39v-.537l2.583-2.509c.253-.253.253-.328.253-.717V10.392l-7.187 18.251h-.969L5.703 10.392v12.232a1.69 1.69 0 0 0 .463 1.404l3.36 4.08v.536H-.001v-.537l3.36-4.08c.36-.371.52-.893.435-1.403V8.48z'/></svg>", |
|||
IconColor = "#00ab6c", |
|||
Display = "Post to Medium", |
|||
Description = "Create a new story or post at medium.", |
|||
ReadMore = "https://medium.com/")] |
|||
public sealed class MediumAction : RuleAction |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Access Token", Description = "The self issued access token.")] |
|||
public string AccessToken { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Title", Description = "The title, used for the url.")] |
|||
public string Title { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Content", Description = "The content, either html or markdown.")] |
|||
public string Content { get; set; } |
|||
|
|||
[Display(Name = "Canonical Url", Description = "The original home of this content, if it was originally published elsewhere.")] |
|||
public string CanonicalUrl { get; set; } |
|||
|
|||
[Display(Name = "Tags", Description = "The optional comma separated list of tags.")] |
|||
public string Tags { get; set; } |
|||
|
|||
[Display(Name = "Is Html", Description = "Indicates whether the content is markdown or html.")] |
|||
public bool IsHtml { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
using Squidex.Infrastructure.Http; |
|||
|
|||
namespace Squidex.Extensions.Actions.Medium |
|||
{ |
|||
public sealed class MediumActionHandler : RuleActionHandler<MediumAction, MediumJob> |
|||
{ |
|||
private const string Description = "Post to medium"; |
|||
|
|||
private readonly IHttpClientFactory httpClientFactory; |
|||
|
|||
public MediumActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) |
|||
: base(formatter) |
|||
{ |
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
protected override (string Description, MediumJob Data) CreateJob(EnrichedEvent @event, MediumAction action) |
|||
{ |
|||
var requestBody = |
|||
new JObject( |
|||
new JProperty("title", Format(action.Title, @event)), |
|||
new JProperty("contentFormat", action.IsHtml ? "html" : "markdown"), |
|||
new JProperty("content", Format(action.Content, @event)), |
|||
new JProperty("canonicalUrl", Format(action.CanonicalUrl, @event)), |
|||
new JProperty("tags", ParseTags(@event, action))); |
|||
|
|||
var ruleJob = new MediumJob { AccessToken = action.AccessToken, RequestBody = requestBody.ToString(Formatting.Indented) }; |
|||
|
|||
return (Description, ruleJob); |
|||
} |
|||
|
|||
private JArray ParseTags(EnrichedEvent @event, MediumAction action) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(action.Tags)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
string[] tags; |
|||
try |
|||
{ |
|||
var jsonTags = Format(action.Tags, @event); |
|||
|
|||
tags = JsonConvert.DeserializeObject<string[]>(jsonTags); |
|||
} |
|||
catch |
|||
{ |
|||
tags = action.Tags.Split(','); |
|||
} |
|||
|
|||
return new JArray(tags); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(MediumJob job) |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
httpClient.Timeout = TimeSpan.FromSeconds(4); |
|||
httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); |
|||
httpClient.DefaultRequestHeaders.Add("Accept-Charset", "utf-8"); |
|||
httpClient.DefaultRequestHeaders.Add("User-Agent", "Squidex Headless CMS"); |
|||
|
|||
string id; |
|||
|
|||
HttpResponseMessage response = null; |
|||
|
|||
var meRequest = BuildMeRequest(job); |
|||
try |
|||
{ |
|||
response = await httpClient.SendAsync(meRequest); |
|||
|
|||
var responseString = await response.Content.ReadAsStringAsync(); |
|||
var responseJson = JToken.Parse(responseString); |
|||
|
|||
id = responseJson["data"]["id"].ToString(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
var requestDump = DumpFormatter.BuildDump(meRequest, response, ex.ToString()); |
|||
|
|||
return (requestDump, ex); |
|||
} |
|||
|
|||
return await httpClient.OneWayRequestAsync(BuildPostRequest(job, id), job.RequestBody); |
|||
} |
|||
} |
|||
|
|||
private static HttpRequestMessage BuildPostRequest(MediumJob job, string id) |
|||
{ |
|||
var request = new HttpRequestMessage(HttpMethod.Post, $"https://api.medium.com/v1/users/{id}/posts") |
|||
{ |
|||
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") |
|||
}; |
|||
|
|||
request.Headers.Add("Authorization", $"Bearer {job.AccessToken}"); |
|||
|
|||
return request; |
|||
} |
|||
|
|||
private static HttpRequestMessage BuildMeRequest(MediumJob job) |
|||
{ |
|||
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.medium.com/v1/me"); |
|||
|
|||
request.Headers.Add("Authorization", $"Bearer {job.AccessToken}"); |
|||
|
|||
return request; |
|||
} |
|||
} |
|||
|
|||
public sealed class MediumJob |
|||
{ |
|||
public string RequestBody { get; set; } |
|||
|
|||
public string AccessToken { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
|
|||
namespace Squidex.Extensions.Actions.Prerender |
|||
{ |
|||
[RuleActionHandler(typeof(PrerenderActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M2.073 17.984l8.646-5.36v-1.787L.356 17.325v1.318l10.363 6.488v-1.787zM29.927 17.984l-8.646-5.36v-1.787l10.363 6.488v1.318l-10.363 6.488v-1.787zM18.228 6.693l-6.276 19.426 1.656.548 6.276-19.426z'/></svg>", |
|||
IconColor = "#2c3e50", |
|||
Display = "Recache URL", |
|||
Description = "Recache a certain URL or cache a URL for the first time.", |
|||
ReadMore = "https://prerender.io")] |
|||
public sealed class PrerenderAction : RuleAction |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Token", Description = "The prerender token from your account.")] |
|||
public string Token { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Url", Description = "The url to recache.")] |
|||
public string Url { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
|
|||
namespace Squidex.Extensions.Actions.Prerender |
|||
{ |
|||
public sealed class PrerenderActionHandler : RuleActionHandler<PrerenderAction, PrerenderJob> |
|||
{ |
|||
private readonly IHttpClientFactory httpClientFactory; |
|||
|
|||
public PrerenderActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) |
|||
: base(formatter) |
|||
{ |
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
protected override (string Description, PrerenderJob Data) CreateJob(EnrichedEvent @event, PrerenderAction action) |
|||
{ |
|||
var url = Format(action.Url, @event); |
|||
|
|||
var request = |
|||
new JObject( |
|||
new JProperty("prerenderToken", action.Token), |
|||
new JProperty("url", url)); |
|||
|
|||
return ($"Recache {url}", new PrerenderJob { RequestBody = request.ToString() }); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(PrerenderJob job) |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.prerender.io/recache") |
|||
{ |
|||
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") |
|||
}; |
|||
|
|||
return await httpClient.OneWayRequestAsync(request, job.RequestBody); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class PrerenderJob |
|||
{ |
|||
public string RequestBody { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Extensions.Actions |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class, Inherited = false)] |
|||
public sealed class RuleActionAttribute : Attribute |
|||
{ |
|||
public string ReadMore { get; set; } |
|||
|
|||
public string IconImage { get; set; } |
|||
|
|||
public string IconColor { get; set; } |
|||
|
|||
public string Display { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class, Inherited = false)] |
|||
public sealed class RuleActionHandlerAttribute : Attribute |
|||
{ |
|||
public Type HandlerType { get; } |
|||
|
|||
public RuleActionHandlerAttribute(Type handlerType) |
|||
{ |
|||
Guard.NotNull(handlerType, nameof(handlerType)); |
|||
|
|||
HandlerType = handlerType; |
|||
|
|||
if (!typeof(IRuleActionHandler).IsAssignableFrom(handlerType)) |
|||
{ |
|||
throw new ArgumentException($"Handler type must implement {typeof(IRuleActionHandler)}.", nameof(handlerType)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Extensions.Actions |
|||
{ |
|||
public sealed class RuleElement |
|||
{ |
|||
public Type Type { get; set; } |
|||
|
|||
public string ReadMore { get; set; } |
|||
|
|||
public string IconImage { get; set; } |
|||
|
|||
public string IconColor { get; set; } |
|||
|
|||
public string Display { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions |
|||
{ |
|||
public static class RuleElementRegistry |
|||
{ |
|||
private const string Suffix = "Action"; |
|||
private static readonly HashSet<Type> ActionHandlerTypes = new HashSet<Type>(); |
|||
private static readonly Dictionary<string, RuleElement> ActionTypes = new Dictionary<string, RuleElement>(); |
|||
private static readonly Dictionary<string, RuleElement> TriggerTypes = new Dictionary<string, RuleElement> |
|||
{ |
|||
["ContentChanged"] = new RuleElement |
|||
{ |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'><path d='M21.875 28H6.125A6.087 6.087 0 0 1 0 21.875V6.125A6.087 6.087 0 0 1 6.125 0h15.75A6.087 6.087 0 0 1 28 6.125v15.75A6.088 6.088 0 0 1 21.875 28zM6.125 1.75A4.333 4.333 0 0 0 1.75 6.125v15.75a4.333 4.333 0 0 0 4.375 4.375h15.75a4.333 4.333 0 0 0 4.375-4.375V6.125a4.333 4.333 0 0 0-4.375-4.375H6.125z'/><path d='M13.125 12.25H7.35c-1.575 0-2.888-1.313-2.888-2.888V7.349c0-1.575 1.313-2.888 2.888-2.888h5.775c1.575 0 2.887 1.313 2.887 2.888v2.013c0 1.575-1.312 2.888-2.887 2.888zM7.35 6.212c-.613 0-1.138.525-1.138 1.138v2.012A1.16 1.16 0 0 0 7.35 10.5h5.775a1.16 1.16 0 0 0 1.138-1.138V7.349a1.16 1.16 0 0 0-1.138-1.138H7.35zM22.662 16.713H5.337c-.525 0-.875-.35-.875-.875s.35-.875.875-.875h17.237c.525 0 .875.35.875.875s-.35.875-.787.875zM15.138 21.262h-9.8c-.525 0-.875-.35-.875-.875s.35-.875.875-.875h9.713c.525 0 .875.35.875.875s-.35.875-.787.875z'/></svg>", |
|||
IconColor = "#3389ff", |
|||
Display = "Content changed", |
|||
Description = "For content changes like created, updated, published, unpublished..." |
|||
}, |
|||
|
|||
["AssetChanged"] = new RuleElement |
|||
{ |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'><path d='M21.875 28H6.125A6.087 6.087 0 0 1 0 21.875V6.125A6.087 6.087 0 0 1 6.125 0h15.75A6.087 6.087 0 0 1 28 6.125v15.75A6.088 6.088 0 0 1 21.875 28zM6.125 1.75A4.333 4.333 0 0 0 1.75 6.125v15.75a4.333 4.333 0 0 0 4.375 4.375h15.75a4.333 4.333 0 0 0 4.375-4.375V6.125a4.333 4.333 0 0 0-4.375-4.375H6.125z'/><path d='M21.088 23.537H9.1c-.35 0-.612-.175-.787-.525s-.088-.7.088-.962l8.225-9.713c.175-.175.438-.35.7-.35s.525.175.7.35l5.25 7.525c.088.087.088.175.088.262.438 1.225.087 2.012-.175 2.45-.613.875-1.925.963-2.1.963zm-10.063-1.75h10.15c.175 0 .612-.088.7-.262.088-.088.088-.35 0-.7l-4.55-6.475-6.3 7.438zM9.1 13.737c-2.1 0-3.85-1.75-3.85-3.85S7 6.037 9.1 6.037s3.85 1.75 3.85 3.85-1.663 3.85-3.85 3.85zm0-5.949c-1.138 0-2.1.875-2.1 2.1s.962 2.1 2.1 2.1 2.1-.962 2.1-2.1-.875-2.1-2.1-2.1z'/></svg>", |
|||
IconColor = "#3389ff", |
|||
Display = "Asset changed", |
|||
Description = "For asset changes like uploaded, updated, renamed, deleted..." |
|||
} |
|||
}; |
|||
|
|||
public static IReadOnlyDictionary<string, RuleElement> Triggers |
|||
{ |
|||
get { return TriggerTypes; } |
|||
} |
|||
|
|||
public static IReadOnlyDictionary<string, RuleElement> Actions |
|||
{ |
|||
get { return ActionTypes; } |
|||
} |
|||
|
|||
public static IReadOnlyCollection<Type> ActionHandlers |
|||
{ |
|||
get { return ActionHandlerTypes; } |
|||
} |
|||
|
|||
static RuleElementRegistry() |
|||
{ |
|||
var actionTypes = |
|||
typeof(RuleElementRegistry).Assembly |
|||
.GetTypes() |
|||
.Where(x => typeof(RuleAction).IsAssignableFrom(x)) |
|||
.Where(x => x.GetCustomAttribute<RuleActionAttribute>() != null) |
|||
.Where(x => x.GetCustomAttribute<RuleActionHandlerAttribute>() != null) |
|||
.ToList(); |
|||
|
|||
foreach (var actionType in actionTypes) |
|||
{ |
|||
var name = actionType.Name; |
|||
|
|||
if (name.EndsWith(Suffix, StringComparison.Ordinal)) |
|||
{ |
|||
name = name.Substring(0, name.Length - Suffix.Length); |
|||
} |
|||
|
|||
var metadata = actionType.GetCustomAttribute<RuleActionAttribute>(); |
|||
|
|||
ActionTypes[name] = |
|||
new RuleElement |
|||
{ |
|||
Type = actionType, |
|||
Display = metadata.Display, |
|||
Description = metadata.Description, |
|||
IconColor = metadata.IconColor, |
|||
IconImage = metadata.IconImage, |
|||
ReadMore = metadata.ReadMore |
|||
}; |
|||
|
|||
ActionHandlerTypes.Add(actionType.GetCustomAttribute<RuleActionHandlerAttribute>().HandlerType); |
|||
} |
|||
} |
|||
|
|||
public static void RegisterTypes(TypeNameRegistry typeNameRegistry) |
|||
{ |
|||
foreach (var actionType in ActionTypes.Values) |
|||
{ |
|||
typeNameRegistry.Map(actionType.Type, actionType.Type.Name); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Slack |
|||
{ |
|||
[RuleActionHandler(typeof(SlackActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 26 28'><path d='M23.734 12.125c1.281 0 2.266.938 2.266 2.219 0 1-.516 1.703-1.453 2.031l-2.688.922.875 2.609c.078.234.109.484.109.734 0 1.234-1 2.266-2.234 2.266a2.271 2.271 0 0 1-2.172-1.547l-.859-2.578-4.844 1.656.859 2.562c.078.234.125.484.125.734 0 1.219-1 2.266-2.25 2.266a2.25 2.25 0 0 1-2.156-1.547l-.859-2.547-2.391.828c-.25.078-.516.141-.781.141-1.266 0-2.219-.938-2.219-2.203 0-.969.625-1.844 1.547-2.156l2.438-.828-1.641-4.891-2.438.844c-.25.078-.5.125-.75.125-1.25 0-2.219-.953-2.219-2.203 0-.969.625-1.844 1.547-2.156l2.453-.828-.828-2.484a2.337 2.337 0 0 1-.125-.734c0-1.234 1-2.266 2.25-2.266a2.25 2.25 0 0 1 2.156 1.547l.844 2.5L13.14 5.5 12.296 3a2.337 2.337 0 0 1-.125-.734c0-1.234 1.016-2.266 2.25-2.266.984 0 1.859.625 2.172 1.547l.828 2.516 2.531-.859c.219-.063.438-.094.672-.094 1.219 0 2.266.906 2.266 2.156 0 .969-.75 1.781-1.625 2.078l-2.453.844 1.641 4.937 2.562-.875a2.32 2.32 0 0 1 .719-.125zm-12.406 4.094l4.844-1.641-1.641-4.922-4.844 1.672z'/></svg>", |
|||
IconColor = "#5c3a58", |
|||
Display = "Send to Slack", |
|||
Description = "Create a status update at slack to a channel you define.", |
|||
ReadMore = "https://slack.com")] |
|||
public sealed class SlackAction : RuleAction |
|||
{ |
|||
[AbsoluteUrl] |
|||
[Required] |
|||
[Display(Name = "Webhook Url", Description = "The slack webhook url.")] |
|||
public Uri WebhookUrl { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Text", Description = "The text that is sent as message to slack.")] |
|||
public string Text { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Slack |
|||
{ |
|||
public sealed class SlackActionHandler : RuleActionHandler<SlackAction, SlackJob> |
|||
{ |
|||
private const string Description = "Send message to slack"; |
|||
|
|||
private readonly IHttpClientFactory httpClientFactory; |
|||
|
|||
public SlackActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) |
|||
: base(formatter) |
|||
{ |
|||
Guard.NotNull(httpClientFactory, nameof(httpClientFactory)); |
|||
|
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
protected override (string Description, SlackJob Data) CreateJob(EnrichedEvent @event, SlackAction action) |
|||
{ |
|||
var body = |
|||
new JObject( |
|||
new JProperty("text", Format(action.Text, @event))); |
|||
|
|||
var ruleJob = new SlackJob |
|||
{ |
|||
RequestUrl = action.WebhookUrl.ToString(), |
|||
RequestBody = body.ToString(Formatting.Indented) |
|||
}; |
|||
|
|||
return (Description, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(SlackJob job) |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
httpClient.Timeout = TimeSpan.FromSeconds(2); |
|||
|
|||
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl) |
|||
{ |
|||
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") |
|||
}; |
|||
|
|||
return await httpClient.OneWayRequestAsync(request, job.RequestBody); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class SlackJob |
|||
{ |
|||
public string RequestUrl { get; set; } |
|||
|
|||
public string RequestBody { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
|
|||
namespace Squidex.Extensions.Actions.Twitter |
|||
{ |
|||
[RuleActionHandler(typeof(TweetActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M32 7.075a12.941 12.941 0 0 1-3.769 1.031 6.601 6.601 0 0 0 2.887-3.631 13.21 13.21 0 0 1-4.169 1.594A6.565 6.565 0 0 0 22.155 4a6.563 6.563 0 0 0-6.563 6.563c0 .512.056 1.012.169 1.494A18.635 18.635 0 0 1 2.23 5.195a6.56 6.56 0 0 0-.887 3.3 6.557 6.557 0 0 0 2.919 5.463 6.565 6.565 0 0 1-2.975-.819v.081a6.565 6.565 0 0 0 5.269 6.437 6.574 6.574 0 0 1-2.968.112 6.588 6.588 0 0 0 6.131 4.563 13.17 13.17 0 0 1-9.725 2.719 18.568 18.568 0 0 0 10.069 2.95c12.075 0 18.681-10.006 18.681-18.681 0-.287-.006-.569-.019-.85A13.216 13.216 0 0 0 32 7.076z'/></svg>", |
|||
IconColor = "#1da1f2", |
|||
Display = "Tweet", |
|||
Description = "Create a status update at Tweet to a your user account.", |
|||
ReadMore = "https://twitter.com")] |
|||
public sealed class TweetAction : RuleAction |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Access Token", Description = " The generated access token.")] |
|||
public string AccessToken { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Access Secret", Description = " The generated access secret.")] |
|||
public string AccessSecret { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Text", Description = "The text that is sent as tweet to twitter.")] |
|||
public string Text { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using CoreTweet; |
|||
using Microsoft.Extensions.Options; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Twitter |
|||
{ |
|||
public sealed class TweetActionHandler : RuleActionHandler<TweetAction, TweetJob> |
|||
{ |
|||
private const string Description = "Send a tweet"; |
|||
|
|||
private readonly TwitterOptions twitterOptions; |
|||
|
|||
public TweetActionHandler(RuleEventFormatter formatter, IOptions<TwitterOptions> twitterOptions) |
|||
: base(formatter) |
|||
{ |
|||
Guard.NotNull(twitterOptions, nameof(twitterOptions)); |
|||
|
|||
this.twitterOptions = twitterOptions.Value; |
|||
} |
|||
|
|||
protected override (string Description, TweetJob Data) CreateJob(EnrichedEvent @event, TweetAction action) |
|||
{ |
|||
var ruleJob = new TweetJob |
|||
{ |
|||
Text = Format(action.Text, @event), |
|||
AccessToken = action.AccessToken, |
|||
AccessSecret = action.AccessSecret |
|||
}; |
|||
|
|||
return (Description, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(TweetJob job) |
|||
{ |
|||
var tokens = Tokens.Create( |
|||
twitterOptions.ClientId, |
|||
twitterOptions.ClientSecret, |
|||
job.AccessToken, |
|||
job.AccessSecret); |
|||
|
|||
await tokens.Statuses.UpdateAsync(status => job.Text); |
|||
|
|||
return ($"Tweeted: {job.Text}", null); |
|||
} |
|||
} |
|||
|
|||
public sealed class TweetJob |
|||
{ |
|||
public string AccessToken { get; set; } |
|||
|
|||
public string AccessSecret { get; set; } |
|||
|
|||
public string Text { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Extensions.Actions.Twitter |
|||
{ |
|||
public sealed class TwitterOptions |
|||
{ |
|||
public string ClientId { get; set; } |
|||
|
|||
public string ClientSecret { get; set; } |
|||
|
|||
public bool IsConfigured() |
|||
{ |
|||
return !string.IsNullOrWhiteSpace(ClientId) && !string.IsNullOrWhiteSpace(ClientSecret); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Webhook |
|||
{ |
|||
[RuleActionHandler(typeof(WebhookActionHandler))] |
|||
[RuleAction( |
|||
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'><path d='M5.95 27.125h-.262C1.75 26.425 0 23.187 0 20.3c0-2.713 1.575-5.688 5.075-6.563V9.712c0-.525.35-.875.875-.875s.875.35.875.875v4.725c0 .438-.35.787-.7.875-2.975.438-4.375 2.8-4.375 4.988s1.313 4.55 4.2 5.075h.175a.907.907 0 0 1 .7 1.05c-.088.438-.438.7-.875.7zM21.175 27.387c-2.8 0-5.775-1.662-6.65-5.075H9.712c-.525 0-.875-.35-.875-.875s.35-.875.875-.875h5.512c.438 0 .787.35.875.7.438 2.975 2.8 4.288 4.988 4.375 2.188 0 4.55-1.313 5.075-4.2v-.088a.908.908 0 0 1 1.05-.7.908.908 0 0 1 .7 1.05v.088c-.612 3.85-3.85 5.6-6.737 5.6zM21.525 18.55c-.525 0-.875-.35-.875-.875v-4.813c0-.438.35-.787.7-.875 2.975-.438 4.288-2.8 4.375-4.987 0-2.188-1.313-4.55-4.2-5.075h-.088c-.525-.175-.875-.613-.787-1.05s.525-.788 1.05-.7h.088c3.938.7 5.688 3.937 5.688 6.825 0 2.713-1.662 5.688-5.075 6.563v4.113c0 .438-.438.875-.875.875zM1.137 6.737H.962c-.438-.087-.788-.525-.7-.963v-.087c.7-3.938 3.85-5.688 6.737-5.688h.087c2.712 0 5.688 1.662 6.563 5.075h4.025c.525 0 .875.35.875.875s-.35.875-.875.875h-4.725c-.438 0-.788-.35-.875-.7-.438-2.975-2.8-4.288-4.988-4.375-2.188 0-4.55 1.313-5.075 4.2v.087c-.088.438-.438.7-.875.7z'/><path d='M7 10.588c-.875 0-1.837-.35-2.538-1.05a3.591 3.591 0 0 1 0-5.075C5.162 3.851 6.037 3.5 7 3.5s1.838.35 2.537 1.05c.7.7 1.05 1.575 1.05 2.537s-.35 1.837-1.05 2.538c-.7.612-1.575.963-2.537.963zM7 5.25c-.438 0-.875.175-1.225.525a1.795 1.795 0 0 0 2.538 2.538c.35-.35.525-.788.525-1.313s-.175-.875-.525-1.225S7.525 5.25 7 5.25zM21.088 23.887a3.65 3.65 0 0 1-2.537-1.05 3.591 3.591 0 0 1 0-5.075c.7-.7 1.575-1.05 2.537-1.05s1.838.35 2.537 1.05c.7.7 1.05 1.575 1.05 2.538s-.35 1.837-1.05 2.537c-.787.7-1.662 1.05-2.537 1.05zm0-5.337c-.525 0-.963.175-1.313.525a1.795 1.795 0 0 0 2.537 2.538c.35-.35.525-.788.525-1.313s-.175-.963-.525-1.313-.787-.438-1.225-.438zM20.387 10.588c-.875 0-1.837-.35-2.537-1.05S16.8 7.963 16.8 7.001s.35-1.837 1.05-2.538c.7-.612 1.662-.962 2.537-.962s1.838.35 2.538 1.05c1.4 1.4 1.4 3.675 0 5.075-.7.612-1.575.963-2.538.963zm0-5.338c-.525 0-.962.175-1.313.525s-.525.788-.525 1.313.175.962.525 1.313c.7.7 1.838.7 2.538 0s.7-1.838 0-2.538c-.263-.438-.7-.612-1.225-.612zM7.087 23.887c-.875 0-1.837-.35-2.538-1.05s-1.05-1.575-1.05-2.537.35-1.838 1.05-2.538c.7-.612 1.575-.962 2.538-.962s1.837.35 2.538 1.05c1.4 1.4 1.4 3.675 0 5.075-.7.612-1.575.962-2.538.962zm0-5.337c-.525 0-.962.175-1.313.525s-.525.788-.525 1.313.175.963.525 1.313a1.794 1.794 0 1 0 2.538-2.537c-.263-.438-.7-.612-1.225-.612z'/></svg>", |
|||
IconColor = "#4bb958", |
|||
Display = "Send webhook", |
|||
Description = "Send events like ContentPublished to your webhook.", |
|||
ReadMore = "https://en.wikipedia.org/wiki/Webhook")] |
|||
public sealed class WebhookAction : RuleAction |
|||
{ |
|||
[AbsoluteUrl] |
|||
[Required] |
|||
[Display(Name = "Url", Description = "he url to the webhook.")] |
|||
public Uri Url { get; set; } |
|||
|
|||
[Display(Name = "Shared Secret", Description = "The shared secret that is used to calculate the signature.")] |
|||
public string SharedSecret { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.HandleRules; |
|||
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Actions.Webhook |
|||
{ |
|||
public sealed class WebhookActionHandler : RuleActionHandler<WebhookAction, WebhookJob> |
|||
{ |
|||
private readonly IHttpClientFactory httpClientFactory; |
|||
|
|||
public WebhookActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) |
|||
: base(formatter) |
|||
{ |
|||
Guard.NotNull(httpClientFactory, nameof(httpClientFactory)); |
|||
|
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
protected override (string Description, WebhookJob Data) CreateJob(EnrichedEvent @event, WebhookAction action) |
|||
{ |
|||
var requestBody = ToEnvelopeJson(@event); |
|||
var requestUrl = Format(action.Url, @event); |
|||
|
|||
var ruleDescription = $"Send event to webhook '{requestUrl}'"; |
|||
var ruleJob = new WebhookJob |
|||
{ |
|||
RequestUrl = Format(action.Url.ToString(), @event), |
|||
RequestSignature = $"{requestBody}{action.SharedSecret}".Sha256Base64(), |
|||
RequestBody = requestBody |
|||
}; |
|||
|
|||
return (ruleDescription, ruleJob); |
|||
} |
|||
|
|||
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(WebhookJob job) |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl) |
|||
{ |
|||
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") |
|||
}; |
|||
|
|||
request.Headers.Add("X-Signature", job.RequestSignature); |
|||
request.Headers.Add("X-Application", "Squidex Webhook"); |
|||
request.Headers.Add("User-Agent", "Squidex Webhook"); |
|||
|
|||
return await httpClient.OneWayRequestAsync(request, job.RequestBody); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public sealed class WebhookJob |
|||
{ |
|||
public string RequestUrl { get; set; } |
|||
|
|||
public string RequestSignature { get; set; } |
|||
|
|||
public string RequestBody { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<DebugType>full</DebugType> |
|||
<DebugSymbols>True</DebugSymbols> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Algolia.Search" Version="5.1.0" /> |
|||
<PackageReference Include="CoreTweet" Version="0.9.0.415" /> |
|||
<PackageReference Include="Elasticsearch.Net" Version="6.3.1" /> |
|||
<PackageReference Include="Microsoft.Extensions.Http" Version="2.1.1" /> |
|||
<PackageReference Include="Microsoft.OData.Core" Version="7.5.1" /> |
|||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> |
|||
<PackageReference Include="NodaTime" Version="2.4.0" /> |
|||
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> |
|||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" PrivateAssets="all" /> |
|||
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" /> |
|||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" /> |
|||
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> |
|||
<PackageReference Include="WindowsAzure.Storage" Version="9.3.2" /> |
|||
</ItemGroup> |
|||
<PropertyGroup> |
|||
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -1,25 +0,0 @@ |
|||
FROM microsoft/aspnetcore-build:2.0.3-jessie |
|||
|
|||
# Install runtime dependencies |
|||
RUN apt-get update \ |
|||
&& apt-get install -y --no-install-recommends ca-certificates bzip2 libfontconfig \ |
|||
&& apt-get clean \ |
|||
&& rm -rf /var/lib/apt/lists/* |
|||
|
|||
# Install official PhantomJS release |
|||
RUN set -x \ |
|||
&& apt-get update \ |
|||
&& apt-get install -y --no-install-recommends \ |
|||
&& mkdir /srv/var \ |
|||
&& mkdir /tmp/phantomjs \ |
|||
# Download Phantom JS |
|||
&& curl -L https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 | tar -xj --strip-components=1 -C /tmp/phantomjs \ |
|||
# Copy binaries only |
|||
&& mv /tmp/phantomjs/bin/phantomjs /usr/local/bin \ |
|||
# Create symbol link |
|||
# Clean up |
|||
&& apt-get autoremove -y \ |
|||
&& apt-get clean all \ |
|||
&& rm -rf /tmp/* /var/lib/apt/lists/* |
|||
|
|||
RUN phantomjs --version |
|||
@ -0,0 +1,17 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Contents |
|||
{ |
|||
public enum StatusChange |
|||
{ |
|||
Archived, |
|||
Published, |
|||
Restored, |
|||
Unpublished |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules.Actions |
|||
{ |
|||
[TypeName(nameof(AlgoliaAction))] |
|||
public sealed class AlgoliaAction : RuleAction |
|||
{ |
|||
public string AppId { get; set; } |
|||
|
|||
public string ApiKey { get; set; } |
|||
|
|||
public string IndexName { get; set; } |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules.Actions |
|||
{ |
|||
[TypeName(nameof(AzureQueueAction))] |
|||
public sealed class AzureQueueAction : RuleAction |
|||
{ |
|||
public string ConnectionString { get; set; } |
|||
|
|||
public string Queue { get; set; } |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,31 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules.Actions |
|||
{ |
|||
[TypeName(nameof(ElasticSearchAction))] |
|||
public sealed class ElasticSearchAction : RuleAction |
|||
{ |
|||
public Uri Host { get; set; } |
|||
|
|||
public string Username { get; set; } |
|||
|
|||
public string Password { get; set; } |
|||
|
|||
public string IndexName { get; set; } |
|||
|
|||
public string IndexType { get; set; } |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules.Actions |
|||
{ |
|||
[TypeName(nameof(FastlyAction))] |
|||
public sealed class FastlyAction : RuleAction |
|||
{ |
|||
public string ApiKey { get; set; } |
|||
|
|||
public string ServiceId { get; set; } |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules.Actions |
|||
{ |
|||
[TypeName(nameof(SlackAction))] |
|||
public sealed class SlackAction : RuleAction |
|||
{ |
|||
public Uri WebhookUrl { get; set; } |
|||
|
|||
public string Text { get; set; } |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules.Actions |
|||
{ |
|||
[TypeName(nameof(WebhookAction))] |
|||
public sealed class WebhookAction : RuleAction |
|||
{ |
|||
public Uri Url { get; set; } |
|||
|
|||
public string SharedSecret { get; set; } |
|||
|
|||
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Rules.Actions; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules |
|||
{ |
|||
public interface IRuleActionVisitor<out T> |
|||
{ |
|||
T Visit(AlgoliaAction action); |
|||
|
|||
T Visit(AzureQueueAction action); |
|||
|
|||
T Visit(ElasticSearchAction action); |
|||
|
|||
T Visit(FastlyAction action); |
|||
|
|||
T Visit(SlackAction action); |
|||
|
|||
T Visit(WebhookAction action); |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Rules |
|||
{ |
|||
public sealed class RuleJobData : Dictionary<string, JToken> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.Contracts; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class ArrayField : RootField<ArrayFieldProperties>, IArrayField |
|||
{ |
|||
private FieldCollection<NestedField> fields = FieldCollection<NestedField>.Empty; |
|||
|
|||
public IReadOnlyList<NestedField> Fields |
|||
{ |
|||
get { return fields.Ordered; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<long, NestedField> FieldsById |
|||
{ |
|||
get { return fields.ById; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<string, NestedField> FieldsByName |
|||
{ |
|||
get { return fields.ByName; } |
|||
} |
|||
|
|||
public ArrayField(long id, string name, Partitioning partitioning, ArrayFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField DeleteField(long fieldId) |
|||
{ |
|||
return Updatefields(f => f.Remove(fieldId)); |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField ReorderFields(List<long> ids) |
|||
{ |
|||
return Updatefields(f => f.Reorder(ids)); |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField AddField(NestedField field) |
|||
{ |
|||
return Updatefields(f => f.Add(field)); |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField UpdateField(long fieldId, Func<NestedField, NestedField> updater) |
|||
{ |
|||
return Updatefields(f => f.Update(fieldId, updater)); |
|||
} |
|||
|
|||
private ArrayField Updatefields(Func<FieldCollection<NestedField>, FieldCollection<NestedField>> updater) |
|||
{ |
|||
var newFields = updater(fields); |
|||
|
|||
if (ReferenceEquals(newFields, fields)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
return Clone<ArrayField>(clone => |
|||
{ |
|||
clone.fields = newFields; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
[TypeName("ArrayField")] |
|||
public sealed class ArrayFieldProperties : FieldProperties |
|||
{ |
|||
public int? MinItems { get; set; } |
|||
|
|||
public int? MaxItems { get; set; } |
|||
|
|||
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor, IField field) |
|||
{ |
|||
return visitor.Visit((IArrayField)field); |
|||
} |
|||
|
|||
public override RootField CreateRootField(long id, string name, Partitioning partitioning) |
|||
{ |
|||
return Fields.Array(id, name, partitioning, this); |
|||
} |
|||
|
|||
public override NestedField CreateNestedField(long id, string name) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class AssetsField : Field<AssetsFieldProperties> |
|||
{ |
|||
public AssetsField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new AssetsFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public AssetsField(long id, string name, Partitioning partitioning, AssetsFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class BooleanField : Field<BooleanFieldProperties> |
|||
{ |
|||
public BooleanField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new BooleanFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public BooleanField(long id, string name, Partitioning partitioning, BooleanFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class DateTimeField : Field<DateTimeFieldProperties> |
|||
{ |
|||
public DateTimeField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new DateTimeFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public DateTimeField(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,115 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public abstract class Field : Cloneable<Field> |
|||
{ |
|||
private readonly long fieldId; |
|||
private readonly Partitioning partitioning; |
|||
private readonly string fieldName; |
|||
private bool isDisabled; |
|||
private bool isHidden; |
|||
private bool isLocked; |
|||
|
|||
public long Id |
|||
{ |
|||
get { return fieldId; } |
|||
} |
|||
|
|||
public string Name |
|||
{ |
|||
get { return fieldName; } |
|||
} |
|||
|
|||
public bool IsLocked |
|||
{ |
|||
get { return isLocked; } |
|||
} |
|||
|
|||
public bool IsHidden |
|||
{ |
|||
get { return isHidden; } |
|||
} |
|||
|
|||
public bool IsDisabled |
|||
{ |
|||
get { return isDisabled; } |
|||
} |
|||
|
|||
public Partitioning Partitioning |
|||
{ |
|||
get { return partitioning; } |
|||
} |
|||
|
|||
public abstract FieldProperties RawProperties { get; } |
|||
|
|||
protected Field(long id, string name, Partitioning partitioning) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
Guard.NotNull(partitioning, nameof(partitioning)); |
|||
Guard.GreaterThan(id, 0, nameof(id)); |
|||
|
|||
fieldId = id; |
|||
fieldName = name; |
|||
|
|||
this.partitioning = partitioning; |
|||
} |
|||
|
|||
[Pure] |
|||
public Field Lock() |
|||
{ |
|||
return Clone<Field>(clone => |
|||
{ |
|||
clone.isLocked = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public Field Hide() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public Field Show() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = false; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public Field Disable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public Field Enable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = false; |
|||
}); |
|||
} |
|||
|
|||
public abstract Field Update(FieldProperties newProperties); |
|||
|
|||
public abstract T Accept<T>(IFieldVisitor<T> visitor); |
|||
} |
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Diagnostics.Contracts; |
|||
using System.Linq; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class FieldCollection<T> : Cloneable<FieldCollection<T>> where T : IField |
|||
{ |
|||
public static readonly FieldCollection<T> Empty = new FieldCollection<T>(); |
|||
|
|||
private ImmutableArray<T> fieldsOrdered = ImmutableArray<T>.Empty; |
|||
private ImmutableDictionary<long, T> fieldsById; |
|||
private ImmutableDictionary<string, T> fieldsByName; |
|||
|
|||
public IReadOnlyList<T> Ordered |
|||
{ |
|||
get { return fieldsOrdered; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<long, T> ById |
|||
{ |
|||
get |
|||
{ |
|||
if (fieldsById == null) |
|||
{ |
|||
if (fieldsOrdered.Length == 0) |
|||
{ |
|||
fieldsById = ImmutableDictionary<long, T>.Empty; |
|||
} |
|||
else |
|||
{ |
|||
fieldsById = fieldsOrdered.ToImmutableDictionary(x => x.Id); |
|||
} |
|||
} |
|||
|
|||
return fieldsById; |
|||
} |
|||
} |
|||
|
|||
public IReadOnlyDictionary<string, T> ByName |
|||
{ |
|||
get |
|||
{ |
|||
if (fieldsByName == null) |
|||
{ |
|||
if (fieldsOrdered.Length == 0) |
|||
{ |
|||
fieldsByName = ImmutableDictionary<string, T>.Empty; |
|||
} |
|||
else |
|||
{ |
|||
fieldsByName = fieldsOrdered.ToImmutableDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); |
|||
} |
|||
} |
|||
|
|||
return fieldsByName; |
|||
} |
|||
} |
|||
|
|||
private FieldCollection() |
|||
{ |
|||
} |
|||
|
|||
public FieldCollection(T[] fields) |
|||
{ |
|||
Guard.NotNull(fields, nameof(fields)); |
|||
|
|||
fieldsOrdered = ImmutableArray.Create(fields); |
|||
} |
|||
|
|||
protected override void OnCloned() |
|||
{ |
|||
fieldsById = null; |
|||
fieldsByName = null; |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Remove(long fieldId) |
|||
{ |
|||
if (!ById.TryGetValue(fieldId, out var field)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = fieldsOrdered.Remove(field); |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Reorder(List<long> ids) |
|||
{ |
|||
Guard.NotNull(ids, nameof(ids)); |
|||
|
|||
if (ids.Count != fieldsOrdered.Length || ids.Any(x => !ById.ContainsKey(x))) |
|||
{ |
|||
throw new ArgumentException("Ids must cover all fields.", nameof(ids)); |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id)).ToImmutableArray(); |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Add(T field) |
|||
{ |
|||
Guard.NotNull(field, nameof(field)); |
|||
|
|||
if (ByName.ContainsKey(field.Name) || ById.ContainsKey(field.Id)) |
|||
{ |
|||
throw new ArgumentException($"A field with name '{field.Name}' and id {field.Id} already exists.", nameof(field)); |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = clone.fieldsOrdered.Add(field); |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Update(long fieldId, Func<T, T> updater) |
|||
{ |
|||
Guard.NotNull(updater, nameof(updater)); |
|||
|
|||
if (!ById.TryGetValue(fieldId, out var field)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
var newField = updater(field); |
|||
|
|||
if (ReferenceEquals(newField, field)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (!(newField is T typedField)) |
|||
{ |
|||
throw new InvalidOperationException($"Field must be of type {typeof(T)}"); |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = clone.fieldsOrdered.Replace(field, typedField); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public static class FieldExtensions |
|||
{ |
|||
public static Schema ReorderFields(this Schema schema, List<long> ids, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.ReorderFields(ids); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.ReorderFields(ids); |
|||
} |
|||
|
|||
public static Schema DeleteField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.DeleteField(fieldId); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.DeleteField(fieldId); |
|||
} |
|||
|
|||
public static Schema LockField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Lock()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Lock()); |
|||
} |
|||
|
|||
public static Schema HideField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Hide()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Hide()); |
|||
} |
|||
|
|||
public static Schema ShowField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Show()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Show()); |
|||
} |
|||
|
|||
public static Schema EnableField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Enable()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Enable()); |
|||
} |
|||
|
|||
public static Schema DisableField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Disable()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Disable()); |
|||
} |
|||
|
|||
public static Schema UpdateField(this Schema schema, long fieldId, FieldProperties properties, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Update(properties)); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Update(properties)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,226 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public static class Fields |
|||
{ |
|||
public static RootField<ArrayFieldProperties> Array(long id, string name, Partitioning partitioning, params NestedField[] fields) |
|||
{ |
|||
var result = new ArrayField(id, name, partitioning, new ArrayFieldProperties()); |
|||
|
|||
if (fields != null) |
|||
{ |
|||
foreach (var field in fields) |
|||
{ |
|||
result = result.AddField(field); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static ArrayField Array(long id, string name, Partitioning partitioning, ArrayFieldProperties properties = null) |
|||
{ |
|||
return new ArrayField(id, name, partitioning, properties ?? new ArrayFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<AssetsFieldProperties> Assets(long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null) |
|||
{ |
|||
return new RootField<AssetsFieldProperties>(id, name, partitioning, properties ?? new AssetsFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<BooleanFieldProperties> Boolean(long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null) |
|||
{ |
|||
return new RootField<BooleanFieldProperties>(id, name, partitioning, properties ?? new BooleanFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<DateTimeFieldProperties> DateTime(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null) |
|||
{ |
|||
return new RootField<DateTimeFieldProperties>(id, name, partitioning, properties ?? new DateTimeFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<GeolocationFieldProperties> Geolocation(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null) |
|||
{ |
|||
return new RootField<GeolocationFieldProperties>(id, name, partitioning, properties ?? new GeolocationFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<JsonFieldProperties> Json(long id, string name, Partitioning partitioning, JsonFieldProperties properties = null) |
|||
{ |
|||
return new RootField<JsonFieldProperties>(id, name, partitioning, properties ?? new JsonFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<NumberFieldProperties> Number(long id, string name, Partitioning partitioning, NumberFieldProperties properties = null) |
|||
{ |
|||
return new RootField<NumberFieldProperties>(id, name, partitioning, properties ?? new NumberFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<ReferencesFieldProperties> References(long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null) |
|||
{ |
|||
return new RootField<ReferencesFieldProperties>(id, name, partitioning, properties ?? new ReferencesFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<StringFieldProperties> String(long id, string name, Partitioning partitioning, StringFieldProperties properties = null) |
|||
{ |
|||
return new RootField<StringFieldProperties>(id, name, partitioning, properties ?? new StringFieldProperties()); |
|||
} |
|||
|
|||
public static RootField<TagsFieldProperties> Tags(long id, string name, Partitioning partitioning, TagsFieldProperties properties = null) |
|||
{ |
|||
return new RootField<TagsFieldProperties>(id, name, partitioning, properties ?? new TagsFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<AssetsFieldProperties> Assets(long id, string name, AssetsFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<AssetsFieldProperties>(id, name, properties ?? new AssetsFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<BooleanFieldProperties> Boolean(long id, string name, BooleanFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<BooleanFieldProperties>(id, name, properties ?? new BooleanFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<DateTimeFieldProperties> DateTime(long id, string name, DateTimeFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<DateTimeFieldProperties>(id, name, properties ?? new DateTimeFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<GeolocationFieldProperties> Geolocation(long id, string name, GeolocationFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<GeolocationFieldProperties>(id, name, properties ?? new GeolocationFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<JsonFieldProperties> Json(long id, string name, JsonFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<JsonFieldProperties>(id, name, properties ?? new JsonFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<NumberFieldProperties> Number(long id, string name, NumberFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<NumberFieldProperties>(id, name, properties ?? new NumberFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<ReferencesFieldProperties> References(long id, string name, ReferencesFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<ReferencesFieldProperties>(id, name, properties ?? new ReferencesFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<StringFieldProperties> String(long id, string name, StringFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<StringFieldProperties>(id, name, properties ?? new StringFieldProperties()); |
|||
} |
|||
|
|||
public static NestedField<TagsFieldProperties> Tags(long id, string name, TagsFieldProperties properties = null) |
|||
{ |
|||
return new NestedField<TagsFieldProperties>(id, name, properties ?? new TagsFieldProperties()); |
|||
} |
|||
|
|||
public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func<ArrayField, ArrayField> handler, ArrayFieldProperties properties = null) |
|||
{ |
|||
var field = Array(id, name, partitioning, properties); |
|||
|
|||
if (handler != null) |
|||
{ |
|||
field = handler(field); |
|||
} |
|||
|
|||
return schema.AddField(field); |
|||
} |
|||
|
|||
public static Schema AddAssets(this Schema schema, long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(Assets(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddBoolean(this Schema schema, long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(Boolean(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddDateTime(this Schema schema, long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(DateTime(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddGeolocation(this Schema schema, long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(Geolocation(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddJson(this Schema schema, long id, string name, Partitioning partitioning, JsonFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(Json(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddNumber(this Schema schema, long id, string name, Partitioning partitioning, NumberFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(Number(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddReferences(this Schema schema, long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(References(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddString(this Schema schema, long id, string name, Partitioning partitioning, StringFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(String(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static Schema AddTags(this Schema schema, long id, string name, Partitioning partitioning, TagsFieldProperties properties = null) |
|||
{ |
|||
return schema.AddField(Tags(id, name, partitioning, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddAssets(this ArrayField field, long id, string name, AssetsFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(Assets(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddBoolean(this ArrayField field, long id, string name, BooleanFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(Boolean(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddDateTime(this ArrayField field, long id, string name, DateTimeFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(DateTime(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddGeolocation(this ArrayField field, long id, string name, GeolocationFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(Geolocation(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddJson(this ArrayField field, long id, string name, JsonFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(Json(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddNumber(this ArrayField field, long id, string name, NumberFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(Number(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddReferences(this ArrayField field, long id, string name, ReferencesFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(References(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddString(this ArrayField field, long id, string name, StringFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(String(id, name, properties)); |
|||
} |
|||
|
|||
public static ArrayField AddTags(this ArrayField field, long id, string name, TagsFieldProperties properties = null) |
|||
{ |
|||
return field.AddField(Tags(id, name, properties)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,61 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public abstract class Field<T> : Field where T : FieldProperties, new() |
|||
{ |
|||
private T properties; |
|||
|
|||
public T Properties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public override FieldProperties RawProperties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
protected Field(long id, string name, Partitioning partitioning, T properties) |
|||
: base(id, name, partitioning) |
|||
{ |
|||
Guard.NotNull(properties, nameof(properties)); |
|||
|
|||
this.properties = properties; |
|||
this.properties.Freeze(); |
|||
} |
|||
|
|||
[Pure] |
|||
public override Field Update(FieldProperties newProperties) |
|||
{ |
|||
var typedProperties = ValidateProperties(newProperties); |
|||
|
|||
return Clone<Field<T>>(clone => |
|||
{ |
|||
clone.properties = typedProperties; |
|||
clone.properties.Freeze(); |
|||
}); |
|||
} |
|||
|
|||
private T ValidateProperties(FieldProperties newProperties) |
|||
{ |
|||
Guard.NotNull(newProperties, nameof(newProperties)); |
|||
|
|||
if (!(newProperties is T typedProperties)) |
|||
{ |
|||
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); |
|||
} |
|||
|
|||
return typedProperties; |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class GeolocationField : Field<GeolocationFieldProperties> |
|||
{ |
|||
public GeolocationField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new GeolocationFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public GeolocationField(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface IArrayField : IField<ArrayFieldProperties> |
|||
{ |
|||
IReadOnlyList<NestedField> Fields { get; } |
|||
|
|||
IReadOnlyDictionary<long, NestedField> FieldsById { get; } |
|||
|
|||
IReadOnlyDictionary<string, NestedField> FieldsByName { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface IField |
|||
{ |
|||
long Id { get; } |
|||
|
|||
string Name { get; } |
|||
|
|||
bool IsLocked { get; } |
|||
|
|||
bool IsDisabled { get; } |
|||
|
|||
bool IsHidden { get; } |
|||
|
|||
FieldProperties RawProperties { get; } |
|||
|
|||
T Accept<T>(IFieldVisitor<T> visitor); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface IField<out T> : IField |
|||
{ |
|||
T Properties { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface INestedField : IField |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface IRootField : IField |
|||
{ |
|||
Partitioning Partitioning { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas.Json |
|||
{ |
|||
public sealed class JsonNestedFieldModel |
|||
{ |
|||
[JsonProperty] |
|||
public long Id { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public bool IsHidden { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public bool IsDisabled { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public FieldProperties Properties { get; set; } |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class JsonField : Field<JsonFieldProperties> |
|||
{ |
|||
public JsonField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new JsonFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public JsonField(long id, string name, Partitioning partitioning, JsonFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public abstract class NestedField : Cloneable<NestedField>, INestedField |
|||
{ |
|||
private readonly long fieldId; |
|||
private readonly string fieldName; |
|||
private bool isDisabled; |
|||
private bool isHidden; |
|||
private bool isLocked; |
|||
|
|||
public long Id |
|||
{ |
|||
get { return fieldId; } |
|||
} |
|||
|
|||
public string Name |
|||
{ |
|||
get { return fieldName; } |
|||
} |
|||
|
|||
public bool IsLocked |
|||
{ |
|||
get { return isLocked; } |
|||
} |
|||
|
|||
public bool IsHidden |
|||
{ |
|||
get { return isHidden; } |
|||
} |
|||
|
|||
public bool IsDisabled |
|||
{ |
|||
get { return isDisabled; } |
|||
} |
|||
|
|||
public abstract FieldProperties RawProperties { get; } |
|||
|
|||
protected NestedField(long id, string name) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
Guard.GreaterThan(id, 0, nameof(id)); |
|||
|
|||
fieldId = id; |
|||
fieldName = name; |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Lock() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isLocked = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Hide() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Show() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = false; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Disable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Enable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = false; |
|||
}); |
|||
} |
|||
|
|||
public abstract T Accept<T>(IFieldVisitor<T> visitor); |
|||
|
|||
public abstract NestedField Update(FieldProperties newProperties); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public class NestedField<T> : NestedField, IField<T> where T : FieldProperties, new() |
|||
{ |
|||
private T properties; |
|||
|
|||
public T Properties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public override FieldProperties RawProperties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public NestedField(long id, string name, T properties) |
|||
: base(id, name) |
|||
{ |
|||
Guard.NotNull(properties, nameof(properties)); |
|||
|
|||
SetProperties(properties); |
|||
} |
|||
|
|||
[Pure] |
|||
public override NestedField Update(FieldProperties newProperties) |
|||
{ |
|||
var typedProperties = ValidateProperties(newProperties); |
|||
|
|||
return Clone<NestedField<T>>(clone => |
|||
{ |
|||
clone.SetProperties(typedProperties); |
|||
}); |
|||
} |
|||
|
|||
private void SetProperties(T newProperties) |
|||
{ |
|||
properties = newProperties; |
|||
properties.Freeze(); |
|||
} |
|||
|
|||
private T ValidateProperties(FieldProperties newProperties) |
|||
{ |
|||
Guard.NotNull(newProperties, nameof(newProperties)); |
|||
|
|||
if (!(newProperties is T typedProperties)) |
|||
{ |
|||
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); |
|||
} |
|||
|
|||
return typedProperties; |
|||
} |
|||
|
|||
public override TResult Accept<TResult>(IFieldVisitor<TResult> visitor) |
|||
{ |
|||
return properties.Accept(visitor, this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class NumberField : Field<NumberFieldProperties> |
|||
{ |
|||
public NumberField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new NumberFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public NumberField(long id, string name, Partitioning partitioning, NumberFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class ReferencesField : Field<ReferencesFieldProperties> |
|||
{ |
|||
public ReferencesField(long id, string name, Partitioning partitioning) |
|||
: base(id, name, partitioning, new ReferencesFieldProperties()) |
|||
{ |
|||
} |
|||
|
|||
public ReferencesField(long id, string name, Partitioning partitioning, ReferencesFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public abstract class RootField : Cloneable<RootField>, IRootField |
|||
{ |
|||
private readonly long fieldId; |
|||
private readonly string fieldName; |
|||
private readonly Partitioning partitioning; |
|||
private bool isDisabled; |
|||
private bool isHidden; |
|||
private bool isLocked; |
|||
|
|||
public long Id |
|||
{ |
|||
get { return fieldId; } |
|||
} |
|||
|
|||
public string Name |
|||
{ |
|||
get { return fieldName; } |
|||
} |
|||
|
|||
public bool IsLocked |
|||
{ |
|||
get { return isLocked; } |
|||
} |
|||
|
|||
public bool IsHidden |
|||
{ |
|||
get { return isHidden; } |
|||
} |
|||
|
|||
public bool IsDisabled |
|||
{ |
|||
get { return isDisabled; } |
|||
} |
|||
|
|||
public Partitioning Partitioning |
|||
{ |
|||
get { return partitioning; } |
|||
} |
|||
|
|||
public abstract FieldProperties RawProperties { get; } |
|||
|
|||
protected RootField(long id, string name, Partitioning partitioning) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
Guard.GreaterThan(id, 0, nameof(id)); |
|||
Guard.NotNull(partitioning, nameof(partitioning)); |
|||
|
|||
fieldId = id; |
|||
fieldName = name; |
|||
|
|||
this.partitioning = partitioning; |
|||
} |
|||
|
|||
[Pure] |
|||
public RootField Lock() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isLocked = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public RootField Hide() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public RootField Show() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = false; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public RootField Disable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public RootField Enable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = false; |
|||
}); |
|||
} |
|||
|
|||
public abstract T Accept<T>(IFieldVisitor<T> visitor); |
|||
|
|||
public abstract RootField Update(FieldProperties newProperties); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public class RootField<T> : RootField, IField<T> where T : FieldProperties, new() |
|||
{ |
|||
private T properties; |
|||
|
|||
public T Properties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public override FieldProperties RawProperties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public RootField(long id, string name, Partitioning partitioning, T properties) |
|||
: base(id, name, partitioning) |
|||
{ |
|||
Guard.NotNull(properties, nameof(properties)); |
|||
|
|||
SetProperties(properties); |
|||
} |
|||
|
|||
[Pure] |
|||
public override RootField Update(FieldProperties newProperties) |
|||
{ |
|||
var typedProperties = ValidateProperties(newProperties); |
|||
|
|||
return Clone<RootField<T>>(clone => |
|||
{ |
|||
clone.SetProperties(typedProperties); |
|||
}); |
|||
} |
|||
|
|||
private void SetProperties(T newProperties) |
|||
{ |
|||
properties = newProperties; |
|||
properties.Freeze(); |
|||
} |
|||
|
|||
private T ValidateProperties(FieldProperties newProperties) |
|||
{ |
|||
Guard.NotNull(newProperties, nameof(newProperties)); |
|||
|
|||
if (!(newProperties is T typedProperties)) |
|||
{ |
|||
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); |
|||
} |
|||
|
|||
return typedProperties; |
|||
} |
|||
|
|||
public override TResult Accept<TResult>(IFieldVisitor<TResult> visitor) |
|||
{ |
|||
return properties.Accept(visitor, this); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue