Skip to content

Commit

Permalink
Suuport Azure Communication SMS
Browse files Browse the repository at this point in the history
  • Loading branch information
hishamco committed Sep 13, 2024
1 parent 47d5e90 commit d98c12e
Show file tree
Hide file tree
Showing 28 changed files with 1,003 additions and 0 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageVersion Include="AWSSDK.S3" Version="3.7.400.4" />
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.301" />
<PackageVersion Include="Azure.Communication.Email" Version="1.0.1" />
<PackageManagement Include="Azure.Communication.Sms" Version="1.0.1" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.1" />
<PackageVersion Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.3.4" />
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
Expand Down
7 changes: 7 additions & 0 deletions OrchardCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Rules.Core", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Queries.Core", "src\OrchardCore\OrchardCore.Queries.Core\OrchardCore.Queries.Core.csproj", "{61B358F2-702C-40AA-9DF7-7121248FE6DE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Sms.Azure", "src\OrchardCore.Modules\OrchardCore.Sms.Azure\OrchardCore.Sms.Azure.csproj", "{013C8BBF-6879-4B47-80C9-A466923E45E5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1389,6 +1391,10 @@ Global
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.Build.0 = Release|Any CPU
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1629,6 +1635,7 @@ Global
{E8A1097D-A65A-4B17-A3A2-F50D79552732} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{013C8BBF-6879-4B47-80C9-A466923E45E5} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using OrchardCore.Workflows.Abstractions.Models;
using OrchardCore.Workflows.Activities;
using OrchardCore.Workflows.Models;
using OrchardCore.Workflows.Services;

namespace OrchardCore.Sms.Azure.Activities;

public class SmsTask : TaskActivity<SmsTask>
{
private readonly ISmsService _smsService;
private readonly IWorkflowExpressionEvaluator _expressionEvaluator;
protected readonly IStringLocalizer S;

public SmsTask(
ISmsService smsService,
IWorkflowExpressionEvaluator expressionEvaluator,
IStringLocalizer<SmsTask> stringLocalizer
)
{
_smsService = smsService;
_expressionEvaluator = expressionEvaluator;
S = stringLocalizer;
}

public override LocalizedString DisplayText => S["SMS Task"];

public override LocalizedString Category => S["Messaging"];

public WorkflowExpression<string> PhoneNumber
{
get => GetProperty(() => new WorkflowExpression<string>());
set => SetProperty(value);
}

public WorkflowExpression<string> Body
{
get => GetProperty(() => new WorkflowExpression<string>());
set => SetProperty(value);
}

public override IEnumerable<Outcome> GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext)
{
return Outcomes(S["Done"], S["Failed"]);
}

public override async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext)
{
var message = new SmsMessage
{
To = await _expressionEvaluator.EvaluateAsync(PhoneNumber, workflowContext, null),
Body = await _expressionEvaluator.EvaluateAsync(Body, workflowContext, null),
};

var result = await _smsService.SendAsync(message);

workflowContext.LastResult = result;

if (result.Succeeded)
{
return Outcomes("Done");
}

return Outcomes("Failed");
}
}
54 changes: 54 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.Sms.Azure/AdminMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Localization;
using OrchardCore.Mvc.Core.Utilities;
using OrchardCore.Navigation;
using OrchardCore.Sms.Azure.Controllers;

namespace OrchardCore.Sms.Azure;

public class AdminMenu : INavigationProvider
{
private static readonly RouteValueDictionary _routeValues = new()
{
{ "area", "OrchardCore.Settings" },
{ "groupId", SmsSettings.GroupId },
};

protected readonly IStringLocalizer S;

public AdminMenu(IStringLocalizer<AdminMenu> stringLocalizer)
{
S = stringLocalizer;
}

public Task BuildNavigationAsync(string name, NavigationBuilder builder)
{
if (!NavigationHelper.IsAdminMenu(name))
{
return Task.CompletedTask;
}

builder
.Add(S["Configuration"], configuration => configuration
.Add(S["Settings"], settings => settings
.Add(S["SMS"], S["SMS"].PrefixPosition(), sms => sms
.AddClass("sms")
.Id("sms")
.Action("Index", "Admin", _routeValues)
.Permission(SmsPermissions.ManageSmsSettings)
.LocalNav()
)
.Add(S["SMS Test"], S["SMS Test"].PrefixPosition(), sms => sms
.AddClass("smstest")
.Id("smstest")
.Action(nameof(AdminController.Test), typeof(AdminController).ControllerName(), "OrchardCore.Sms")
.Permission(SmsPermissions.ManageSmsSettings)
.LocalNav()
)
)
);

return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using OrchardCore.Admin;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.Sms.Azure.ViewModels;

namespace OrchardCore.Sms.Azure.Controllers;

public class AdminController : Controller
{
private readonly SmsProviderOptions _smsProviderOptions;
private readonly IPhoneFormatValidator _phoneFormatValidator;
private readonly INotifier _notifier;
private readonly IAuthorizationService _authorizationService;
private readonly ISmsProviderResolver _smsProviderResolver;

protected readonly IHtmlLocalizer H;
protected readonly IStringLocalizer S;

public AdminController(
IOptions<SmsProviderOptions> smsProviderOptions,
IPhoneFormatValidator phoneFormatValidator,
ISmsProviderResolver smsProviderResolver,
INotifier notifier,
IAuthorizationService authorizationService,
IHtmlLocalizer<AdminController> htmlLocalizer,
IStringLocalizer<AdminController> stringLocalizer)
{
_smsProviderOptions = smsProviderOptions.Value;
_phoneFormatValidator = phoneFormatValidator;
_smsProviderResolver = smsProviderResolver;
_notifier = notifier;
_authorizationService = authorizationService;
H = htmlLocalizer;
S = stringLocalizer;
}

[Admin("sms/test", "SmsProviderTest")]
public async Task<IActionResult> Test()
{
if (!await _authorizationService.AuthorizeAsync(User, SmsPermissions.ManageSmsSettings))
{
return Forbid();
}

var model = new SmsTestViewModel();

PopulateModel(model);

return View(model);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Test(SmsTestViewModel model)
{
if (!await _authorizationService.AuthorizeAsync(User, SmsPermissions.ManageSmsSettings))
{
return Forbid();
}

if (ModelState.IsValid)
{
var provider = await _smsProviderResolver.GetAsync(model.Provider);

if (provider is null)
{
ModelState.AddModelError(nameof(model.Provider), S["Please select a valid provider."]);
}
else if (!_phoneFormatValidator.IsValid(model.PhoneNumber))
{
ModelState.AddModelError(nameof(model.PhoneNumber), S["Please provide a valid phone number."]);
}
else
{
var result = await provider.SendAsync(new SmsMessage()
{
To = model.PhoneNumber,
Body = S["This is a test SMS message."]
});

if (result.Succeeded)
{
await _notifier.SuccessAsync(H["The test SMS message has been successfully sent."]);

return RedirectToAction(nameof(Test));
}
else
{
await _notifier.ErrorAsync(H["The test SMS message failed to send."]);
}
}
}

PopulateModel(model);

return View(model);
}

private void PopulateModel(SmsTestViewModel model)
{
model.Providers = _smsProviderOptions.Providers
.Where(entry => entry.Value.IsEnabled)
.Select(entry => new SelectListItem(entry.Key, entry.Key))
.OrderBy(item => item.Text)
.ToArray();
}
}
Loading

0 comments on commit d98c12e

Please sign in to comment.