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 a way to Remove User from a Role (Issue #14632) #14652

Merged
merged 14 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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 @@ -25,6 +25,7 @@
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ResourceManagement\OrchardCore.ResourceManagement.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Roles.Core\OrchardCore.Roles.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Users.Core\OrchardCore.Users.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Workflows.Abstractions\OrchardCore.Workflows.Abstractions.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@using OrchardCore.Workflows.ViewModels
@using OrchardCore.Workflows.Helpers
@using OrchardCore.Roles.Workflows.Activities
@using OrchardCore.Roles.Workflows.ViewModels
@model ActivityViewModel<RemoveUserRoleTask>
<header>
<h4><i class="fa-solid fa-user" aria-hidden="true"></i>@Model.Activity.GetTitleOrDefault(() => T["Remove user from role"])</h4>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
</header>
<em>@T["{0} from role {1}", Model.Activity.UserName, Model.Activity.RoleName]</em>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@using OrchardCore.Roles.Workflows.ViewModels
@model RemoveUserRoleTaskViewModel

<div class="mb-3" asp-validation-class-for="UserName">
<label asp-for="UserName">@T["UserName"]</label>
<input type="text" asp-for="UserName" class="form-control code" />
<span asp-validation-for="UserName"></span>
<span class="hint">@T["The User to update. With Liquid support."]</span>
</div>

<div class="mb-3" asp-validation-class-for="RoleName">
<label asp-for="RoleName">@T["RoleName"]</label>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
<input type="text" asp-for="RoleName" class="form-control code" />
<span asp-validation-for="RoleName"></span>
<span class="hint">@T["The Role to remove. With Liquid support."]</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h4 class="card-title"><i class="fa-solid fa-user" aria-hidden="true"></i>@T["Remove user from role"]</h4>
<p>@T["Removes a user from a role."]</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@using OrchardCore.Workflows.ViewModels
@using OrchardCore.Workflows.Helpers
@using OrchardCore.Roles.Workflows.Activities
@using OrchardCore.Roles.Workflows.ViewModels
@model ActivityViewModel<SelectUsersInRoleTask>
<header>
<h4><i class="fa-solid fa-user" aria-hidden="true"></i>@Model.Activity.GetTitleOrDefault(() => T["Select users in roles"])</h4>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
</header>
<em>@T["Stores users of roles {0} to {1}", string.Join(",", Model.Activity.Roles), Model.Activity.OutputKeyName]</em>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@using OrchardCore.Roles.Workflows.ViewModels
@model SelectUsersInRoleTaskViewModel

<div class="mb-3" asp-validation-class-for="OutputKeyName">
<label asp-for="OutputKeyName">@T["OutputKeyName"]</label>
<input type="text" asp-for="OutputKeyName" class="form-control code" />
<span asp-validation-for="OutputKeyName"></span>
<span class="hint">@T["The Output Key Name to save list. With Liquid support."]</span>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
</div>

<div class="mb-3" asp-validation-class-for="Roles">
<label asp-for="Roles">@T["Roles"]</label>
@await Component.InvokeAsync("SelectRoles", new { selectedRoles = Model.Roles, htmlName = Html.NameFor(m => m.Roles), except = new[] { "Anonymous", "Authenticated" } })
<span asp-validation-for="Roles"></span>
<span class="hint">@T["The Roles to search for. With Liquid support."]</span>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h4 class="card-title"><i class="fa-solid fa-user" aria-hidden="true"></i>@T["Select users in role"]</h4>
<p>@T["Select users in role."]</p>
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
@model SelectRolesViewModel

<div class="d-flex flex-wrap">
<div class="mb-3">
@for (var i = 0; i < Model.RoleSelections.Count; i++)
{
var role = Model.RoleSelections[i];
<div class="checkbox pr-2">
<div class="form-check">
<label for="@Html.IdFor(m => m.RoleSelections[i])">
<input id="@Html.IdFor(m => m.RoleSelections[i])" name="@Model.HtmlName" type="checkbox" checked="@role.IsSelected" value="@role.Item" />
@role.Item
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using OrchardCore.Users;
using OrchardCore.Users.Models;
using OrchardCore.Users.Services;
using OrchardCore.Workflows.Abstractions.Models;
using OrchardCore.Workflows.Activities;
using OrchardCore.Workflows.Models;
using OrchardCore.Workflows.Services;

namespace OrchardCore.Roles.Workflows.Activities
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
public class RemoveUserRoleTask : TaskActivity
{
private readonly UserManager<IUser> _userManager;
private readonly IUserService _userService;
private readonly IWorkflowExpressionEvaluator _expressionEvaluator;
protected readonly IStringLocalizer S;

public RemoveUserRoleTask(UserManager<IUser> userManager, IUserService userService, IWorkflowExpressionEvaluator expressionvaluator, IStringLocalizer<RemoveUserRoleTask> localizer)
{
_userManager = userManager;
_userService = userService;
_expressionEvaluator = expressionvaluator;
S = localizer;
}

public override string Name => nameof(RemoveUserRoleTask);

public override LocalizedString DisplayText => S["Remove User Role Task"];

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

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

public WorkflowExpression<string> RoleName
{
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 userName = await _expressionEvaluator.EvaluateAsync(UserName, workflowContext, null);
var roleName = await _expressionEvaluator.EvaluateAsync(RoleName, workflowContext, null);

var user = (User)await _userService.GetUserAsync(userName);
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved

if (user != null)
{
if (user.RoleNames.Contains(roleName))
{
await _userManager.RemoveFromRoleAsync(user, roleName);
}

return Outcomes("Done");
}
else
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
return Outcomes("Failed");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using OrchardCore.Users;
using OrchardCore.Users.Models;
using OrchardCore.Workflows.Abstractions.Models;
using OrchardCore.Workflows.Activities;
using OrchardCore.Workflows.Models;
using OrchardCore.Workflows.Services;

namespace OrchardCore.Roles.Workflows.Activities
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
public class SelectUsersInRoleTask : TaskActivity
{
private readonly UserManager<IUser> _userManager;
private readonly IWorkflowExpressionEvaluator _expressionEvaluator;
protected readonly IStringLocalizer S;

public SelectUsersInRoleTask(UserManager<IUser> userManager, IWorkflowExpressionEvaluator expressionvaluator, IStringLocalizer<SelectUsersInRoleTask> localizer)
{
_userManager = userManager;
_expressionEvaluator = expressionvaluator;
S = localizer;
}

public override string Name => nameof(SelectUsersInRoleTask);

public override LocalizedString DisplayText => S["Select Users in Role Task"];

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

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

public IEnumerable<string> Roles
{
get => GetProperty(() => new List<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 propKeyName = await _expressionEvaluator.EvaluateAsync(OutputKeyName, workflowContext, null);

if (!string.IsNullOrEmpty(propKeyName))
{
if (Roles.Any())
{
HashSet<IUser> usersInRole = new HashSet<IUser>();
foreach (var roleName in Roles)
{
usersInRole.UnionWith(await _userManager.GetUsersInRoleAsync(roleName));
}
if (usersInRole.Any())
{
workflowContext.Output[propKeyName] = usersInRole.Select(u => (u as User).UserId).ToArray();
return Outcomes("Done");
}
}
}
return Outcomes("Failed");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using OrchardCore.Roles.Workflows.Activities;
using OrchardCore.Roles.Workflows.ViewModels;
using OrchardCore.Workflows.Display;
using OrchardCore.Workflows.Models;

namespace OrchardCore.Roles.Workflows.Drivers
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
public class RemoveUserRoleTaskDisplayDriver : ActivityDisplayDriver<RemoveUserRoleTask, RemoveUserRoleTaskViewModel>
{
protected override void EditActivity(RemoveUserRoleTask activity, RemoveUserRoleTaskViewModel model)
{
model.UserName = activity.UserName.Expression;
model.RoleName = activity.RoleName.Expression;
}

protected override void UpdateActivity(RemoveUserRoleTaskViewModel model, RemoveUserRoleTask activity)
{
activity.UserName = new WorkflowExpression<string>(model.UserName);
activity.RoleName = new WorkflowExpression<string>(model.RoleName);
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using OrchardCore.Roles.Workflows.Activities;
using OrchardCore.Roles.Workflows.ViewModels;
using OrchardCore.Workflows.Display;
using OrchardCore.Workflows.Models;

namespace OrchardCore.Roles.Workflows.Drivers
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
public class SelectUsersInRoleTaskDisplayDriver : ActivityDisplayDriver<SelectUsersInRoleTask, SelectUsersInRoleTaskViewModel>
{
protected override void EditActivity(SelectUsersInRoleTask activity, SelectUsersInRoleTaskViewModel model)
{
model.OutputKeyName = activity.OutputKeyName.Expression;
model.Roles = activity.Roles;
}

protected override void UpdateActivity(SelectUsersInRoleTaskViewModel model, SelectUsersInRoleTask activity)
{
activity.OutputKeyName = new WorkflowExpression<string>(model.OutputKeyName);
activity.Roles = model.Roles;
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Modules;
using OrchardCore.Roles.Workflows.Activities;
using OrchardCore.Roles.Workflows.Drivers;
using OrchardCore.Workflows.Helpers;

namespace OrchardCore.Roles.Workflows
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
[RequireFeatures("OrchardCore.Workflows")]
public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddActivity<RemoveUserRoleTask, RemoveUserRoleTaskDisplayDriver>();
services.AddActivity<SelectUsersInRoleTask, SelectUsersInRoleTaskDisplayDriver>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;

namespace OrchardCore.Roles.Workflows.ViewModels
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
public class RemoveUserRoleTaskViewModel
{
[Required]
public string UserName { get; set; }

[Required]
public string RoleName { get; set; }
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace OrchardCore.Roles.Workflows.ViewModels
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
public class SelectUsersInRoleTaskViewModel
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
[Required]
public string OutputKeyName { get; set; }

public IEnumerable<string> Roles { get; set; }
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading