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 8 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,11 @@
@using OrchardCore.Workflows.ViewModels
@using OrchardCore.Workflows.Helpers
@using OrchardCore.Roles.Workflows.Activities
@using OrchardCore.Roles.Workflows.ViewModels
@model ActivityViewModel<GetUsersByRoleTask>
<header>
<h4>
<i class="fa-solid fa-user" aria-hidden="true"></i>@Model.Activity.GetTitleOrDefault(() => T["Get users in roles"])
</h4>
</header>
<em>@T["Get users in 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 GetUsersByRoleTaskViewModel

<div class="mb-3" asp-validation-class-for="OutputKeyName">
<label asp-for="OutputKeyName">@T["Output Key Name"]</label>
<input type="text" asp-for="OutputKeyName" class="form-control code" />
<span asp-validation-for="OutputKeyName"></span>
<span class="hint">@T["The designated key name for storing the users' list in the workflow output. This key enables later access to the list within the workflow.With Liquid support."]</span>
</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["Choose the roles used to identify users during list generation."]</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<h4 class="card-title">
<i class="fa-solid fa-user" aria-hidden="true"></i>@T["Get users in roles"]
</h4>
<p>@T["Get users in roles."]</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@using OrchardCore.Workflows.ViewModels
@using OrchardCore.Workflows.Helpers
@using OrchardCore.Roles.Workflows.Activities
@using OrchardCore.Roles.Workflows.ViewModels
@model ActivityViewModel<UnassignUserRoleTask>
<header>
<h4>
<i class="fa-solid fa-user" aria-hidden="true"></i>@Model.Activity.GetTitleOrDefault(() => T["Unassign user from roles"])
</h4>
</header>
<em>@T["Unassign {0} from roles {1}", Model.Activity.UserName, string.Join(",", Model.Activity.Roles)]</em>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@using OrchardCore.Roles.Workflows.ViewModels

@model UnassignUserRoleTaskViewModel

<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="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 remove."]</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<h4 class="card-title">
<i class="fa-solid fa-user" aria-hidden="true"></i>@T["Unassign user from roles"]
</h4>
<p>@T["Unassign a user from roles."]</p>
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
@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">
<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
va htmlId = Html.IdFor(m => m.RoleSelections[i]);

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (windows-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (windows-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (windows-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (windows-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (windows-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in src/OrchardCore.Modules/OrchardCore.Roles/Views/Shared/Components/SelectRoles/Default.cshtml

View workflow job for this annotation

GitHub Actions / Build & Test (windows-latest)

The type or namespace name 'va' could not be found (are you missing a using directive or an assembly reference?)

<div class="checkbox form-check">
<input id="@htmlId" class="form-check-input" name="@Model.HtmlName" type="checkbox" checked="@role.IsSelected" value="@role.Item" />
<label for="@htmlId" class="form-check-label">
@role.Item
</label>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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;

public class GetUsersByRoleTask : TaskActivity
{
private readonly UserManager<IUser> _userManager;
private readonly IWorkflowExpressionEvaluator _expressionEvaluator;
protected readonly IStringLocalizer S;

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

public override string Name => nameof(GetUsersByRoleTask);

public override LocalizedString DisplayText => S["Get Users by 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)
=> Outcomes(S["Done"], S["Failed"]);

MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved

public override async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext)
{
var propKeyName = await _expressionEvaluator.EvaluateAsync(OutputKeyName, workflowContext, null);

if (!string.IsNullOrEmpty(propKeyName))
{
var usersInRole = new List<User>();
foreach (var role in Roles)
{
foreach(var u in await _userManager.GetUsersInRoleAsync(role))
{
if(u is not User user)
{
continue;
}

usersInRole.Add(user);
}
}
if (usersInRole.Count > 0)
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
{
workflowContext.Output[propKeyName] = usersInRole;

return Outcomes("Done");
}
}

return Outcomes("Failed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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.Users.Services;
using OrchardCore.Workflows.Abstractions.Models;
using OrchardCore.Workflows.Activities;
using OrchardCore.Workflows.Models;
using OrchardCore.Workflows.Services;

namespace OrchardCore.Roles.Workflows.Activities;

public class UnassignUserRoleTask : TaskActivity
{
private readonly UserManager<IUser> _userManager;
private readonly IUserService _userService;
private readonly IWorkflowExpressionEvaluator _expressionEvaluator;
protected readonly IStringLocalizer S;

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

public override string Name => nameof(UnassignUserRoleTask);

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

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

public WorkflowExpression<string> UserName
{
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)
=> Outcomes(S["Done"], S["Failed"]);

public override async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext)
{
var userName = await _expressionEvaluator.EvaluateAsync(UserName, workflowContext, null);

var u = await _userService.GetUserAsync(userName);

if (u is User user)
{
foreach(var role in Roles)
{
if (user.RoleNames.Contains(role))
{
await _userManager.RemoveFromRoleAsync(user, role);
MikeAlhayek marked this conversation as resolved.
Show resolved Hide resolved
}
}

return Outcomes("Done");
}

return Outcomes("Failed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using OrchardCore.Roles.Workflows.Activities;
using OrchardCore.Roles.Workflows.ViewModels;
using OrchardCore.Workflows.Display;
using OrchardCore.Workflows.Models;

namespace OrchardCore.Roles.Workflows.Drivers;

public class GetUsersByRoleTaskDisplayDriver : ActivityDisplayDriver<GetUsersByRoleTask, GetUsersByRoleTaskViewModel>
{
protected override void EditActivity(GetUsersByRoleTask activity, GetUsersByRoleTaskViewModel model)
{
model.OutputKeyName = activity.OutputKeyName.Expression;
model.Roles = activity.Roles;
}

protected override void UpdateActivity(GetUsersByRoleTaskViewModel model, GetUsersByRoleTask activity)
{
activity.OutputKeyName = new WorkflowExpression<string>(model.OutputKeyName);
activity.Roles = model.Roles;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using OrchardCore.Roles.Workflows.Activities;
using OrchardCore.Roles.Workflows.ViewModels;
using OrchardCore.Workflows.Display;
using OrchardCore.Workflows.Models;

namespace OrchardCore.Roles.Workflows.Drivers;

public class UnassignUserRoleTaskDisplayDriver : ActivityDisplayDriver<UnassignUserRoleTask, UnassignUserRoleTaskViewModel>
{
protected override void EditActivity(UnassignUserRoleTask activity, UnassignUserRoleTaskViewModel model)
{
model.UserName = activity.UserName.Expression;
model.Roles = activity.Roles;
}

protected override void UpdateActivity(UnassignUserRoleTaskViewModel model, UnassignUserRoleTask activity)
{
activity.UserName = new WorkflowExpression<string>(model.UserName);
activity.Roles = model.Roles;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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;

[RequireFeatures("OrchardCore.Workflows")]
public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddActivity<UnassignUserRoleTask, UnassignUserRoleTaskDisplayDriver>();
services.AddActivity<GetUsersByRoleTask, GetUsersByRoleTaskDisplayDriver>();
}
}
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;

public class GetUsersByRoleTaskViewModel
{
[Required]
public string OutputKeyName { get; set; }

[Required]
public IEnumerable<string> Roles { get; set; }
}
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;

public class UnassignUserRoleTaskViewModel
{
[Required]
public string UserName { get; set; }

[Required]
public IEnumerable<string> Roles { get; set; }
}
Loading