mirror of https://github.com/Squidex/squidex.git
34 changed files with 2353 additions and 1617 deletions
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Rules.Actions |
||||
|
{ |
||||
|
[TypeName(nameof(TweetAction))] |
||||
|
public sealed class TweetAction : RuleAction |
||||
|
{ |
||||
|
public string PinCode { get; set; } |
||||
|
|
||||
|
public string Text { get; set; } |
||||
|
|
||||
|
public override T Accept<T>(IRuleActionVisitor<T> visitor) |
||||
|
{ |
||||
|
return visitor.Visit(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Rules.Actions |
||||
|
{ |
||||
|
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,74 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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.EnrichedEvents; |
||||
|
using Squidex.Domain.Apps.Core.Rules.Actions; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
#pragma warning disable SA1649 // File name must match first type name
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.HandleRules.Actions |
||||
|
{ |
||||
|
public sealed class TweetJob |
||||
|
{ |
||||
|
public string PinCode { get; set; } |
||||
|
|
||||
|
public string Text { get; set; } |
||||
|
} |
||||
|
|
||||
|
public sealed class TweetActionHandler : RuleActionHandler<TweetAction, TweetJob> |
||||
|
{ |
||||
|
private const string Description = "Send a tweet"; |
||||
|
|
||||
|
private readonly RuleEventFormatter formatter; |
||||
|
private readonly ClientPool<string, Tokens> tokenPool; |
||||
|
|
||||
|
public TweetActionHandler(RuleEventFormatter formatter, IOptions<TwitterOptions> twitterOptions) |
||||
|
{ |
||||
|
Guard.NotNull(formatter, nameof(formatter)); |
||||
|
Guard.NotNull(twitterOptions, nameof(twitterOptions)); |
||||
|
|
||||
|
this.formatter = formatter; |
||||
|
|
||||
|
tokenPool = new ClientPool<string, Tokens>(async key => |
||||
|
{ |
||||
|
var session = await OAuth.AuthorizeAsync(twitterOptions.Value.ClientId, twitterOptions.Value.ClientSecret); |
||||
|
|
||||
|
return await session.GetTokensAsync(key); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected override (string Description, TweetJob Data) CreateJob(EnrichedEvent @event, TweetAction action) |
||||
|
{ |
||||
|
var text = formatter.Format(action.Text, @event); |
||||
|
|
||||
|
var ruleJob = new TweetJob { Text = text, PinCode = action.PinCode }; |
||||
|
|
||||
|
return (Description, ruleJob); |
||||
|
} |
||||
|
|
||||
|
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(TweetJob job) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var tokens = await tokenPool.GetClientAsync(job.PinCode); |
||||
|
|
||||
|
var response = await tokens.Statuses.UpdateAsync(x => job.Text); |
||||
|
|
||||
|
return ($"Tweeted: {job.Text}", null); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
return (ex.Message, ex); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using NJsonSchema.Annotations; |
||||
|
using Squidex.Domain.Apps.Core.Rules; |
||||
|
using Squidex.Domain.Apps.Core.Rules.Actions; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Rules.Models.Actions |
||||
|
{ |
||||
|
[JsonSchema("Tweet")] |
||||
|
public sealed class TweetActionDto : RuleActionDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The pin code.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string PinCode { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The text that is sent as tweet to twitter.
|
||||
|
/// </summary>
|
||||
|
public string Text { get; set; } |
||||
|
|
||||
|
public override RuleAction ToAction() |
||||
|
{ |
||||
|
return SimpleMapper.Map(this, new TweetAction()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using CoreTweet; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Squidex.Domain.Apps.Core.Rules.Actions; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Rules |
||||
|
{ |
||||
|
public sealed class TwitterController : Controller |
||||
|
{ |
||||
|
private readonly TwitterOptions twitterOptions; |
||||
|
|
||||
|
public TwitterController(IOptions<TwitterOptions> twitterOptions) |
||||
|
{ |
||||
|
this.twitterOptions = twitterOptions.Value; |
||||
|
} |
||||
|
|
||||
|
[Route("rules/twitter/auth")] |
||||
|
public async Task<IActionResult> Auth() |
||||
|
{ |
||||
|
var session = await OAuth.AuthorizeAsync(twitterOptions.ClientId, twitterOptions.ClientSecret); |
||||
|
|
||||
|
return Redirect(session.AuthorizeUri.ToString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
<h3 class="wizard-title">Tweet a status update to your twitter feed</h3> |
||||
|
|
||||
|
<form [formGroup]="actionForm" class="form-horizontal"> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-label" for="pinCode">Pin Code</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<sqx-control-errors for="pinCode" [submitted]="actionFormSubmitted"></sqx-control-errors> |
||||
|
|
||||
|
<input type="pinCode" class="form-control" id="pinCode" formControlName="pinCode" /> |
||||
|
|
||||
|
<small class="form-text text-muted"> |
||||
|
The pin code to authorize. Follow this link and add the pin code: <a href="/api/rules/twitter/auth" target="_blank">Login to twitter</a>. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-label" for="text">Text</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors> |
||||
|
|
||||
|
<textarea class="form-control" id="text" formControlName="text"></textarea> |
||||
|
|
||||
|
<small class="form-text text-muted"> |
||||
|
The text to tweet. Read the <a routerLink="help">help</a> section for information about advanced formatting. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,2 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
@ -0,0 +1,37 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { Component, Input, OnInit } from '@angular/core'; |
||||
|
import { FormControl, FormGroup, Validators } from '@angular/forms'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-tweet-action', |
||||
|
styleUrls: ['./tweet-action.component.scss'], |
||||
|
templateUrl: './tweet-action.component.html' |
||||
|
}) |
||||
|
export class TweetActionComponent implements OnInit { |
||||
|
@Input() |
||||
|
public action: any; |
||||
|
|
||||
|
@Input() |
||||
|
public actionForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public actionFormSubmitted = false; |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.actionForm.setControl('pinCode', |
||||
|
new FormControl(this.action.pinCode || '', [ |
||||
|
Validators.required |
||||
|
])); |
||||
|
|
||||
|
this.actionForm.setControl('text', |
||||
|
new FormControl(this.action.text || '', [ |
||||
|
Validators.required |
||||
|
])); |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
@ -0,0 +1,43 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using FluentAssertions; |
||||
|
using Squidex.Domain.Apps.Core.Rules.Actions; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Rules.Guards.Actions |
||||
|
{ |
||||
|
public class TweetActionTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_pin_code_is_null() |
||||
|
{ |
||||
|
var action = new TweetAction { PinCode = null }; |
||||
|
|
||||
|
var errors = await RuleActionValidator.ValidateAsync(action); |
||||
|
|
||||
|
errors.Should().BeEquivalentTo( |
||||
|
new List<ValidationError> |
||||
|
{ |
||||
|
new ValidationError("Pin Code is required.", "PinCode") |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_pin_code_is_absolute() |
||||
|
{ |
||||
|
var action = new TweetAction { PinCode = "123" }; |
||||
|
|
||||
|
var errors = await RuleActionValidator.ValidateAsync(action); |
||||
|
|
||||
|
Assert.Empty(errors); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue