Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UserConfirmed workflow event for user email confirmation #16584

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
96689b3
Add UserConfirmedEvent Workflow Activity triggered when a user succes…
davidpuplava Apr 19, 2024
1e040cc
Change User Confirmed event thumbnail icon to something different
davidpuplava Apr 23, 2024
432710b
Merge tag 'before-formatting
davidpuplava Aug 19, 2024
5959d72
Format code
davidpuplava Aug 19, 2024
17137dc
Merge tag 'formatting' into davidpuplava/user-confirmed-activity
davidpuplava Aug 19, 2024
6678a72
Merge remote-tracking branch 'upstream/main' into davidpuplava/user-c…
davidpuplava Aug 19, 2024
f4913f2
Refactor activity display driver to be consistent with other activities
davidpuplava Aug 20, 2024
be3dc0b
Refactor confirmed email user event to EmailConfirmation controller
davidpuplava Aug 20, 2024
535379d
Remove accidental reference to Castle logging
davidpuplava Aug 20, 2024
2326d47
Improve code formatting for UserConfirmedEvent
davidpuplava Aug 20, 2024
60aaca5
Update src/OrchardCore.Modules/OrchardCore.Users/Workflows/Handlers/U…
davidpuplava Aug 20, 2024
38f0a1a
Remove references to specific files in SDK style project
davidpuplava Aug 20, 2024
d21cdf2
Add back accidental new line removal
davidpuplava Aug 20, 2024
b99f509
Simplify UserContext object creation
davidpuplava Aug 20, 2024
4993fde
Use more generic ILogger type in field definition for types
davidpuplava Aug 20, 2024
cf07b17
Merge branch 'davidpuplava/user-confirmed-activity' of github.com:dav…
davidpuplava Aug 20, 2024
415a6a8
Reorder constructor parameters, field definitions and assignment stat…
davidpuplava Aug 20, 2024
aa1274b
Merge remote-tracking branch 'upstream/main' into davidpuplava/user-c…
davidpuplava Aug 20, 2024
51d0d0c
Add UserConfirmedEvent to 2.0.0 release notes.
davidpuplava Aug 20, 2024
cf0958f
formatting
MikeAlhayek Aug 20, 2024
e36faa3
fix docs
MikeAlhayek Aug 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public Task DeletedAsync(UserDeleteContext context)
public Task CreatingAsync(UserCreateContext context) => Task.CompletedTask;
public Task UpdatingAsync(UserUpdateContext context) => Task.CompletedTask;
public Task DeletingAsync(UserDeleteContext context) => Task.CompletedTask;
public Task ConfirmedAsync(UserConfirmContext context) => Task.CompletedTask;

#endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.Modules;
using OrchardCore.Mvc.Core.Utilities;
using OrchardCore.Users.Handlers;
using OrchardCore.Users.Models;

namespace OrchardCore.Users.Controllers;
Expand All @@ -15,6 +18,8 @@ public sealed class EmailConfirmationController : Controller
private readonly UserManager<IUser> _userManager;
private readonly IAuthorizationService _authorizationService;
private readonly INotifier _notifier;
private readonly IEnumerable<IUserEventHandler> _userEventHandlers;
private readonly ILogger _logger;

internal readonly IHtmlLocalizer H;
internal readonly IStringLocalizer S;
Expand All @@ -23,12 +28,16 @@ public EmailConfirmationController(
UserManager<IUser> userManager,
IAuthorizationService authorizationService,
INotifier notifier,
IEnumerable<IUserEventHandler> userEventHandlers,
ILogger<EmailConfirmationController> logger,
IHtmlLocalizer<EmailConfirmationController> htmlLocalizer,
IStringLocalizer<EmailConfirmationController> stringLocalizer)
{
_userManager = userManager;
_authorizationService = authorizationService;
_notifier = notifier;
_userEventHandlers = userEventHandlers;
_logger = logger;
H = htmlLocalizer;
S = stringLocalizer;
}
Expand All @@ -52,6 +61,9 @@ public async Task<IActionResult> ConfirmEmail(string userId, string code)

if (result.Succeeded)
{
var userContext = new UserConfirmContext(user) { ConfirmationType = UserConfirmationType.Email };
await _userEventHandlers.InvokeAsync((handler, context) => handler.ConfirmedAsync(userContext), userContext, _logger);

return View();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@model OrchardCore.Users.Workflows.ViewModels.UserEventViewModel<OrchardCore.Users.Workflows.Activities.UserConfirmedEvent>

<header>
<h4><i class="fa-solid fa-envelope-circle-check" aria-hidden="true"></i>@Model.Activity.GetTitleOrDefault(() => T["User Confirmed"])</h4>
</header>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@using OrchardCore.Users.Workflows.ViewModels
@model UserConfirmedEventViewModel
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h4 class="card-title"><i class="fa-solid fa-envelope-circle-check" aria-hidden="true"></i>@T["User Confirmed"]</h4>
<p>@T["User Confirmed Email."]</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.Extensions.Localization;
using OrchardCore.Users.Services;
using OrchardCore.Workflows.Services;

namespace OrchardCore.Users.Workflows.Activities;

public class UserConfirmedEvent : UserEvent
{
public UserConfirmedEvent(
IUserService userService,
IWorkflowScriptEvaluator scriptEvaluator,
IStringLocalizer<UserUpdatedEvent> stringLocalizer)
: base(userService, scriptEvaluator, stringLocalizer)
{
}

public override string Name
=> nameof(UserConfirmedEvent);

public override LocalizedString DisplayText
=> S["User Confirmed Event"];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Users.Workflows.Activities;
using OrchardCore.Users.Workflows.ViewModels;
using OrchardCore.Workflows.Display;

namespace OrchardCore.Users.Workflows.Drivers;

public class UserConfirmedEventDisplayDriver : ActivityDisplayDriver<UserConfirmedEvent, UserConfirmedEventViewModel>
{
public override Task<IDisplayResult> DisplayAsync(UserConfirmedEvent activity, BuildDisplayContext context)
{
return CombineAsync(
Shape("UserConfirmedEvent_Fields_Thumbnail", new UserConfirmedEventViewModel(activity)).Location("Thumbnail", "Content"),
Factory("UserConfirmedEvent_Fields_Design", ctx =>
{
var shape = new UserConfirmedEventViewModel
{
Activity = activity,
};

return shape;
}).Location("Design", "Content")
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ private Task<IEnumerable<WorkflowExecutionContext>> TriggerWorkflowEventAsync(st
correlationId: user.UserId
);
}

public override Task ConfirmedAsync(UserConfirmContext context)
=> TriggerWorkflowEventAsync(nameof(UserConfirmedEvent), (User)context.User);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public override void ConfigureServices(IServiceCollection services)
services.AddScoped<IUserEventHandler, UserEventHandler>();
services.AddActivity<AssignUserRoleTask, AssignUserRoleTaskDisplayDriver>();
services.AddActivity<ValidateUserTask, ValidateUserTaskDisplayDriver>();
services.AddActivity<UserConfirmedEvent, UserConfirmedEventDisplayDriver>();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using OrchardCore.Users.Workflows.Activities;

namespace OrchardCore.Users.Workflows.ViewModels;

public class UserConfirmedEventViewModel : UserEventViewModel<UserConfirmedEvent>
{
public UserConfirmedEventViewModel()
{
}

public UserConfirmedEventViewModel(UserConfirmedEvent activity)
: base(activity)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,10 @@ public interface IUserEventHandler
/// </summary>
/// <param name="context">The <see cref="UserContext"/>.</param>
Task EnabledAsync(UserContext context);

/// <summary>
/// Occurs when a user is confirmed.
/// </summary>
/// <param name="context">The <see cref="UserContext"/>.</param>
Task ConfirmedAsync(UserConfirmContext context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace OrchardCore.Users.Handlers;

/// <summary>
/// Represents a context for confirming a user.
/// </summary>
public class UserConfirmContext : UserContext
{
/// <inheritdocs />
public UserConfirmContext(IUser user)
: base(user)
{
}

public UserConfirmationType ConfirmationType { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace OrchardCore.Users.Handlers;

public enum UserConfirmationType
{
Email,
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ public abstract class UserEventHandlerBase : IUserEventHandler

/// <inheritdocs />
public virtual Task EnabledAsync(UserContext context) => Task.CompletedTask;

/// <inheritdocs />
public virtual Task ConfirmedAsync(UserConfirmContext context) => Task.CompletedTask;
}
20 changes: 14 additions & 6 deletions src/docs/releases/2.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ public class RegisterUserFormDisplayDriver : DisplayDriver<RegisterUserForm>

- Two-factor authentication (2FA) is now available for all users. Previously, only users with permission to access the admin area could set up their own 2FA methods, although all users could still use 2FA if it was set up by an admin. To enable this change, the method `IsRequiredAsync()` in both the `ITwoFactorAuthenticationHandlerCoordinator` and `ITwoFactorAuthenticationHandler` interfaces has been updated to `IsRequiredAsync(IUser user)`.

- A new `UserConfirmedEvent` workflow event is now available. This event is triggered when a user successfully confirms their email address using the link sent during user registration.


### Notifications Module

The endpoint for marking notifications as read has been updated. Previously the route was `/api/notifications/read`, and now it is `/Notifications/MarkAsRead`. This change will only affect you if you have overridden the `UserNotificationNavbar` view.
Expand Down Expand Up @@ -388,6 +391,12 @@ AppContext.SetSwitch("OrchardCore.Media.EnableLegacyMediaFieldGraphQLFields", tr

Previously the `<resources type="..." />` Razor tag helper and the `{% resources type: "..." %}` Liquid tag were only capable of handling a hard-coded set of resource definition types (`script`, `stylesheet`, etc). Now both can be extended with `IResourcesTagHelperProcessor` to run custom rendering logic. To make this possible, the `OrchardCore.ResourceManagement.TagHelpers.ResourceType` and `OrchardCore.Resources.Liquid.ResourcesTag.ResourceType` enums have been replaced with a common `OrchardCore.ResourceManagement.ResourceTagType`. It was renamed to avoid confusion with `ResourceDefinition.Type`. This change does not affect the uses of the Razor tag helper or the Liquid tag in templates of user projects, but it affects published releases such as NuGet packages. Any themes and modules that contain `<resources type="..." />` in a Razor file (e.g. _Layout.cshtml_) must be re-released to generate the updated `.cshtml.cs` files, because the old pre-compiled templates would throw `MissingMethodException`.

### Workflows Module

Previously, the `CreateContentTask`, `RetrieveContentTask`, and `UpdateContentTask` in the workflow's CorrelationId was updated each time with the ContentItemId of the current content item. Now, the CorrelationId value will only be updated if the current workflow's CorrelationId is empty.

Additionally, a new workflow-scoped script function `setCorrelationId(id:string): void` was added, that you can use to update the workflow's CorrelationId.

## Change Logs

### Azure AI Search Module
Expand Down Expand Up @@ -420,14 +429,13 @@ Additionally, if an error occurs, a new custom exception, RecipeExecutionExcepti

The method `Task TriggerEventAsync(string name, IDictionary<string, object> input = null, string correlationId = null, bool isExclusive = false, bool isAlwaysCorrelated = false)`
was changed to return `Task<IEnumerable<WorkflowExecutionContext>>` instead.

#### CorrelationId

**Breaking change:**
`CreateContentTask`, `RetrieveContentTask`, and `UpdateContentTask`, in previous versions, the workflow's CorrelationId was updated each time with the ContentItemId of the current content item;
Now, the CorrelationId value will only be updated if the current workflow's CorrelationId is empty.
#### New Events

The following events were added:

- `UserConfirmedEvent`: this event triggers when a user successfully confirms their email address after registration.

Also added a workflow-scoped script function `setCorrelationId(id:string): void`, that you can use to update the workflow's CorrelationId.


### GraphQL Module
Expand Down