user/role management
This commit is contained in:
parent
8295a03fef
commit
7415b343c1
183
GanjooRazor/Areas/Admin/Pages/Roles.cshtml
Normal file
183
GanjooRazor/Areas/Admin/Pages/Roles.cshtml
Normal file
@ -0,0 +1,183 @@
|
||||
@page
|
||||
@model GanjooRazor.Areas.Admin.Pages.RolesModel
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@{
|
||||
Layout = "_AdminLayout";
|
||||
ViewData["Title"] = "مدیریت نقشها";
|
||||
}
|
||||
|
||||
<div class="up-page-header">
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.LastError))
|
||||
{
|
||||
<div class="up-alert up-alert--danger">@Model.LastError</div>
|
||||
}
|
||||
|
||||
<div class="up-card">
|
||||
<h2 class="up-card__title">نقش جدید</h2>
|
||||
<div class="up-form-actions">
|
||||
<input type="text" id="new-role-name" placeholder="نام نقش" style="flex:1;" />
|
||||
<input type="text" id="new-role-description" placeholder="توضیحات" style="flex:1;" />
|
||||
<a role="button" onclick="createRole()" class="up-btn up-btn--success">➕ ایجاد</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function createRole() {
|
||||
var name = document.getElementById('new-role-name').value.trim();
|
||||
var description = document.getElementById('new-role-description').value.trim();
|
||||
if (name === '') {
|
||||
upToast('نام نقش خالی است.', 'error');
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '?handler=NewRole',
|
||||
data: { name: name, description: description },
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function () {
|
||||
location.reload(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteRole(roleName) {
|
||||
var ok = await upConfirm('آیا از حذف نقش «' + roleName + '» اطمینان دارید؟');
|
||||
if (!ok) return;
|
||||
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: '?handler=Role',
|
||||
data: { roleName: roleName },
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function () {
|
||||
var row = document.getElementById('tr-role-' + roleName);
|
||||
row.parentNode.removeChild(row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleRolePermissions(roleName) {
|
||||
var row = document.getElementById('perm-row-' + roleName);
|
||||
if (row.style.display === 'none' || row.style.display === '') {
|
||||
row.style.display = 'table-row';
|
||||
loadRolePermissions(roleName);
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function loadRolePermissions(roleName) {
|
||||
var container = document.getElementById('perm-container-' + roleName);
|
||||
container.innerHTML = 'در حال بارگذاری...';
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: '?handler=Permissions&roleName=' + encodeURIComponent(roleName),
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function (items) {
|
||||
renderPermissionTree(roleName, items);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderPermissionTree(roleName, items) {
|
||||
var container = document.getElementById('perm-container-' + roleName);
|
||||
container.innerHTML = '';
|
||||
|
||||
var tree = document.createElement('div');
|
||||
tree.className = 'up-perm-tree';
|
||||
|
||||
items.forEach(function (item, itemIndex) {
|
||||
var itemRow = document.createElement('div');
|
||||
itemRow.className = 'up-perm-tree__item';
|
||||
|
||||
var itemLabel = document.createElement('strong');
|
||||
itemLabel.textContent = item.description;
|
||||
itemRow.appendChild(itemLabel);
|
||||
tree.appendChild(itemRow);
|
||||
|
||||
var opsWrap = document.createElement('div');
|
||||
opsWrap.className = 'up-perm-tree__ops';
|
||||
item.operations.forEach(function (op, opIndex) {
|
||||
var opLabel = document.createElement('label');
|
||||
opLabel.className = 'up-check-row';
|
||||
var cb = document.createElement('input');
|
||||
cb.type = 'checkbox';
|
||||
cb.checked = op.status;
|
||||
cb.dataset.itemIndex = itemIndex;
|
||||
cb.dataset.opIndex = opIndex;
|
||||
opLabel.appendChild(cb);
|
||||
opLabel.appendChild(document.createTextNode(op.description));
|
||||
opsWrap.appendChild(opLabel);
|
||||
});
|
||||
tree.appendChild(opsWrap);
|
||||
});
|
||||
|
||||
container.appendChild(tree);
|
||||
|
||||
var saveBtn = document.createElement('a');
|
||||
saveBtn.setAttribute('role', 'button');
|
||||
saveBtn.className = 'up-btn up-btn--success';
|
||||
saveBtn.textContent = '💾 ذخیرهٔ دسترسیها';
|
||||
saveBtn.onclick = function () { saveRolePermissions(roleName, items, tree); };
|
||||
container.appendChild(saveBtn);
|
||||
}
|
||||
|
||||
function saveRolePermissions(roleName, items, tree) {
|
||||
tree.querySelectorAll('input[type=checkbox]').forEach(function (cb) {
|
||||
items[cb.dataset.itemIndex].operations[cb.dataset.opIndex].status = cb.checked;
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: '?handler=Permissions&roleName=' + encodeURIComponent(roleName),
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(items),
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function () {
|
||||
upToast('دسترسیهای نقش ذخیره شد.');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@if (Model.Roles != null && Model.Roles.Length > 0)
|
||||
{
|
||||
<table>
|
||||
<tr>
|
||||
<th>نام</th>
|
||||
<th>توضیحات</th>
|
||||
<th>#</th>
|
||||
</tr>
|
||||
@foreach (var role in Model.Roles)
|
||||
{
|
||||
<tr id="tr-role-@role.Name">
|
||||
<td>@role.Name</td>
|
||||
<td>@role.Description</td>
|
||||
<td>
|
||||
<a role="button" onclick="toggleRolePermissions('@role.Name')" class="up-icon-btn" title="دسترسیها" aria-label="مدیریت دسترسیهای این نقش">🔑</a>
|
||||
<a role="button" onclick="deleteRole('@role.Name')" class="up-icon-btn" title="حذف" aria-label="حذف این نقش">🗑</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="perm-row-@role.Name" style="display:none">
|
||||
<td colspan="3">
|
||||
<div id="perm-container-@role.Name"></div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<partial name="_EmptyState" model="@("نقشی تعریف نشده است.")" />
|
||||
}
|
||||
132
GanjooRazor/Areas/Admin/Pages/Roles.cshtml.cs
Normal file
132
GanjooRazor/Areas/Admin/Pages/Roles.cshtml.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using GanjooRazor.Utils;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Newtonsoft.Json;
|
||||
using RSecurityBackend.Models.Auth.Db;
|
||||
using RSecurityBackend.Models.Auth.Memory;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GanjooRazor.Areas.Admin.Pages
|
||||
{
|
||||
[IgnoreAntiforgeryToken(Order = 1001)]
|
||||
public class RolesModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Last Error
|
||||
/// </summary>
|
||||
public string LastError { get; set; }
|
||||
|
||||
public RAppRole[] Roles { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Request.Cookies["Token"]))
|
||||
return Redirect("/");
|
||||
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
{
|
||||
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/roles");
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Roles = JsonConvert.DeserializeObject<RAppRole[]>(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
else
|
||||
{
|
||||
LastError = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LastError = "لطفاً از گنجور خارج و مجددا به آن وارد شوید.";
|
||||
}
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// create a new role - ?handler=NewRole
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnPostNewRoleAsync(string name, string description)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var newRole = new { name = name, description = description };
|
||||
var response = await secureClient.PostAsync($"{APIRoot.Url}/api/roles", new StringContent(JsonConvert.SerializeObject(newRole), Encoding.UTF8, "application/json"));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return new JsonResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// delete a role - ?handler=Role (DELETE)
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnDeleteRoleAsync(string roleName)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/roles/{roleName}");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return new JsonResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// securable items tree annotated with this role's current permission status - ?handler=Permissions&roleName=...
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnGetPermissionsAsync(string roleName)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/roles/permissions/{roleName}");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var items = JsonConvert.DeserializeObject<SecurableItem[]>(await response.Content.ReadAsStringAsync());
|
||||
return new JsonResult(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// save this role's permissions - ?handler=Permissions&roleName=... (PUT)
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnPutPermissionsAsync(string roleName)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
string body;
|
||||
using (var reader = new StreamReader(Request.Body))
|
||||
{
|
||||
body = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/roles/permissions/{roleName}", new StringContent(body, Encoding.UTF8, "application/json"));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return new JsonResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
216
GanjooRazor/Areas/Admin/Pages/Users.cshtml
Normal file
216
GanjooRazor/Areas/Admin/Pages/Users.cshtml
Normal file
@ -0,0 +1,216 @@
|
||||
@page
|
||||
@model GanjooRazor.Areas.Admin.Pages.UsersModel
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@{
|
||||
Layout = "_AdminLayout";
|
||||
ViewData["Title"] = "مدیریت کاربران";
|
||||
ViewData["WideShell"] = true;
|
||||
}
|
||||
|
||||
<div class="up-page-header">
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.LastError))
|
||||
{
|
||||
<div class="up-alert up-alert--danger">@Model.LastError</div>
|
||||
}
|
||||
|
||||
<div class="up-card">
|
||||
<form method="get" class="up-form-actions">
|
||||
<input type="text" name="filterByEmail" value="@Model.FilterByEmail" placeholder="جستجوی ایمیل" style="flex:1;" />
|
||||
<input type="hidden" name="pageSize" value="@Model.PageSize" />
|
||||
<input type="submit" value="🔎 جستجو" class="up-btn up-btn--ghost" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var allRolesCache = null;
|
||||
|
||||
function toggleUserRow(userId) {
|
||||
var row = document.getElementById('roles-row-' + userId);
|
||||
if (row.style.display === 'none' || row.style.display === '') {
|
||||
row.style.display = 'table-row';
|
||||
loadUserRoles(userId);
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function loadUserRoles(userId) {
|
||||
var container = document.getElementById('roles-container-' + userId);
|
||||
container.innerHTML = 'در حال بارگذاری...';
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: '?handler=Roles&id=' + userId,
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function (roles) {
|
||||
renderUserRoles(userId, roles);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderUserRoles(userId, roles) {
|
||||
var container = document.getElementById('roles-container-' + userId);
|
||||
container.innerHTML = '';
|
||||
roles.forEach(function (role) {
|
||||
var chip = document.createElement('span');
|
||||
chip.className = 'up-role-chip';
|
||||
chip.textContent = role;
|
||||
var removeBtn = document.createElement('a');
|
||||
removeBtn.setAttribute('role', 'button');
|
||||
removeBtn.className = 'up-role-chip__remove';
|
||||
removeBtn.title = 'حذف نقش';
|
||||
removeBtn.textContent = '✕';
|
||||
removeBtn.onclick = function () { removeUserRole(userId, role); };
|
||||
chip.appendChild(removeBtn);
|
||||
container.appendChild(chip);
|
||||
});
|
||||
|
||||
var addBtn = document.createElement('a');
|
||||
addBtn.setAttribute('role', 'button');
|
||||
addBtn.className = 'up-btn up-btn--sm up-btn--ghost';
|
||||
addBtn.textContent = '➕ نقش جدید';
|
||||
addBtn.onclick = function () { showAddRole(userId, roles); };
|
||||
container.appendChild(addBtn);
|
||||
}
|
||||
|
||||
function showAddRole(userId, currentRoles) {
|
||||
function withAllRoles(all) {
|
||||
var unassigned = all.filter(function (r) { return currentRoles.indexOf(r) === -1; });
|
||||
if (unassigned.length === 0) {
|
||||
upToast('این کاربر همهٔ نقشهای موجود را دارد.', 'error');
|
||||
return;
|
||||
}
|
||||
var select = document.createElement('select');
|
||||
unassigned.forEach(function (r) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = r;
|
||||
opt.text = r;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
var confirmBtn = document.createElement('a');
|
||||
confirmBtn.setAttribute('role', 'button');
|
||||
confirmBtn.className = 'up-btn up-btn--sm up-btn--success';
|
||||
confirmBtn.textContent = 'تأیید';
|
||||
confirmBtn.onclick = function () { addUserRole(userId, select.value); };
|
||||
|
||||
var container = document.getElementById('roles-container-' + userId);
|
||||
var wrap = document.createElement('span');
|
||||
wrap.appendChild(select);
|
||||
wrap.appendChild(confirmBtn);
|
||||
container.appendChild(wrap);
|
||||
}
|
||||
|
||||
if (allRolesCache != null) {
|
||||
withAllRoles(allRolesCache);
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: '?handler=AllRoles',
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function (all) {
|
||||
allRolesCache = all;
|
||||
withAllRoles(all);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addUserRole(userId, role) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '?handler=AddRole',
|
||||
data: { id: userId, role: role },
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function () {
|
||||
loadUserRoles(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeUserRole(userId, role) {
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: '?handler=Role',
|
||||
data: { id: userId, role: role },
|
||||
error: function (e) {
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function () {
|
||||
loadUserRoles(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleUserStatus(userId, checkbox) {
|
||||
var active = checkbox.checked;
|
||||
checkbox.disabled = true;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '?handler=ToggleStatus',
|
||||
data: { id: userId, active: active },
|
||||
error: function (e) {
|
||||
checkbox.checked = !active;
|
||||
checkbox.disabled = false;
|
||||
upToast(e.responseText || String(e), 'error');
|
||||
},
|
||||
success: function () {
|
||||
checkbox.disabled = false;
|
||||
upToast('وضعیت کاربر بهروز شد.');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@if (Model.Users != null && Model.Users.Length > 0)
|
||||
{
|
||||
<table>
|
||||
<tr>
|
||||
<th>پست الکترونیکی</th>
|
||||
<th>نام</th>
|
||||
<th>نام خانوادگی</th>
|
||||
<th>فعال</th>
|
||||
<th>#</th>
|
||||
</tr>
|
||||
@foreach (var user in Model.Users)
|
||||
{
|
||||
<tr id="tr-@user.Id">
|
||||
<td>@user.Email</td>
|
||||
<td>@user.FirstName</td>
|
||||
<td>@user.SurName</td>
|
||||
<td>
|
||||
@if (user.Status == RSecurityBackend.Models.Auth.Db.RAppUserStatus.Active)
|
||||
{
|
||||
<input type="checkbox" checked onchange="toggleUserStatus('@user.Id', this)" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" onchange="toggleUserStatus('@user.Id', this)" />
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<a role="button" onclick="toggleUserRow('@user.Id')" class="up-icon-btn" title="نقشها" aria-label="مدیریت نقشهای این کاربر">🎭</a>
|
||||
<a role="button" href="/Admin/ExamineUser?id=@user.Id" class="up-icon-btn" title="مشاهده" aria-label="مشاهدهٔ این کاربر">👁️</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="roles-row-@user.Id" style="display:none">
|
||||
<td colspan="5">
|
||||
<div id="roles-container-@user.Id" class="up-role-chip-row"></div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<partial name="_EmptyState" model="@("کاربری یافت نشد.")" />
|
||||
}
|
||||
|
||||
<partial name="_Pagination" model="Model.PaginationLinks" />
|
||||
216
GanjooRazor/Areas/Admin/Pages/Users.cshtml.cs
Normal file
216
GanjooRazor/Areas/Admin/Pages/Users.cshtml.cs
Normal file
@ -0,0 +1,216 @@
|
||||
using GanjooRazor.Utils;
|
||||
using GSpotifyProxy.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Newtonsoft.Json;
|
||||
using RSecurityBackend.Models.Auth.Db;
|
||||
using RSecurityBackend.Models.Auth.ViewModels;
|
||||
using RSecurityBackend.Models.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GanjooRazor.Areas.Admin.Pages
|
||||
{
|
||||
[IgnoreAntiforgeryToken(Order = 1001)]
|
||||
public class UsersModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Last Error
|
||||
/// </summary>
|
||||
public string LastError { get; set; }
|
||||
|
||||
public PublicRAppUser[] Users { get; set; }
|
||||
|
||||
public PaginationMetadata Paging { get; set; }
|
||||
|
||||
public List<NameIdUrlImage> PaginationLinks { get; set; } = new List<NameIdUrlImage>();
|
||||
|
||||
public int PageNumber { get; set; } = 1;
|
||||
|
||||
public int PageSize { get; set; } = 15;
|
||||
|
||||
public string FilterByEmail { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(int pageNumber = 1, int pageSize = 15, string filterByEmail = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Request.Cookies["Token"]))
|
||||
return Redirect("/");
|
||||
|
||||
PageNumber = pageNumber < 1 ? 1 : pageNumber;
|
||||
PageSize = pageSize;
|
||||
FilterByEmail = filterByEmail;
|
||||
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
{
|
||||
var url = $"{APIRoot.Url}/api/users?PageNumber={PageNumber}&PageSize={PageSize}";
|
||||
if (!string.IsNullOrEmpty(FilterByEmail))
|
||||
url += $"&filterByEmail={Uri.EscapeDataString(FilterByEmail)}";
|
||||
|
||||
var response = await secureClient.GetAsync(url);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Users = JsonConvert.DeserializeObject<PublicRAppUser[]>(await response.Content.ReadAsStringAsync());
|
||||
if (response.Headers.TryGetValues("paging-headers", out var values))
|
||||
{
|
||||
Paging = JsonConvert.DeserializeObject<PaginationMetadata>(values.FirstOrDefault());
|
||||
if (Paging != null)
|
||||
{
|
||||
for (int p = 1; p <= Paging.totalPages; p++)
|
||||
{
|
||||
var pageUrl = $"?pageNumber={p}&pageSize={PageSize}";
|
||||
if (!string.IsNullOrEmpty(FilterByEmail))
|
||||
pageUrl += $"&filterByEmail={Uri.EscapeDataString(FilterByEmail)}";
|
||||
PaginationLinks.Add(new NameIdUrlImage()
|
||||
{
|
||||
Name = p.ToString(),
|
||||
Url = p == PageNumber ? "" : pageUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LastError = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LastError = "لطفاً از گنجور خارج و مجددا به آن وارد شوید.";
|
||||
}
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// role names for a specific user - ?handler=Roles&id=...
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnGetRolesAsync(Guid id)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/users/{id}/roles");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var roles = JsonConvert.DeserializeObject<string[]>(await response.Content.ReadAsStringAsync());
|
||||
return new JsonResult(roles);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// all defined role names, for populating the "add role" picker - ?handler=AllRoles
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnGetAllRolesAsync()
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/roles");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var roles = JsonConvert.DeserializeObject<RAppRole[]>(await response.Content.ReadAsStringAsync());
|
||||
return new JsonResult(roles.Select(r => r.Name).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// add a role to a user - ?handler=AddRole
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnPostAddRoleAsync(Guid id, string role)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var response = await secureClient.PostAsync($"{APIRoot.Url}/api/users/{id}/roles/{Uri.EscapeDataString(role)}", null);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return new JsonResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// remove a role from a user - ?handler=Role (DELETE)
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnDeleteRoleAsync(Guid id, string role)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/users/{id}/roles/{Uri.EscapeDataString(role)}");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return new JsonResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// toggle a user's active/inactive status - ?handler=ToggleStatus
|
||||
/// </summary>
|
||||
public async Task<IActionResult> OnPostToggleStatusAsync(Guid id, bool active)
|
||||
{
|
||||
using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response)))
|
||||
{
|
||||
if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response))
|
||||
return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید.");
|
||||
|
||||
var userInfoResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/users/{id}");
|
||||
if (!userInfoResponse.IsSuccessStatusCode)
|
||||
return BadRequest(await userInfoResponse.Content.ReadAsStringAsync());
|
||||
|
||||
var userInfo = JsonConvert.DeserializeObject<PublicRAppUser>(await userInfoResponse.Content.ReadAsStringAsync());
|
||||
|
||||
// ModifyUser rejects the request unless IsAdmin exactly matches the user's actual
|
||||
// current admin-role membership (this endpoint cannot itself change admin status),
|
||||
// so we need their real membership, not a guess.
|
||||
var rolesResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/users/{id}/roles");
|
||||
if (!rolesResponse.IsSuccessStatusCode)
|
||||
return BadRequest(await rolesResponse.Content.ReadAsStringAsync());
|
||||
var currentRoles = JsonConvert.DeserializeObject<string[]>(await rolesResponse.Content.ReadAsStringAsync());
|
||||
bool isCurrentlyAdmin = currentRoles.Contains("Administrator");
|
||||
|
||||
var updateModel = new RegisterRAppUser()
|
||||
{
|
||||
Password = "",
|
||||
IsAdmin = isCurrentlyAdmin,
|
||||
Id = userInfo.Id,
|
||||
Username = userInfo.Username,
|
||||
Email = userInfo.Email,
|
||||
PhoneNumber = userInfo.PhoneNumber,
|
||||
FirstName = userInfo.FirstName,
|
||||
SurName = userInfo.SurName,
|
||||
Status = active ? RAppUserStatus.Active : RAppUserStatus.Inactive,
|
||||
RImageId = userInfo.RImageId,
|
||||
NickName = userInfo.NickName,
|
||||
Bio = userInfo.Bio,
|
||||
Website = userInfo.Website
|
||||
};
|
||||
|
||||
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/users/{id}", new StringContent(JsonConvert.SerializeObject(updateModel), Encoding.UTF8, "application/json"));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return BadRequest(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return new JsonResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -498,6 +498,14 @@
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link text-dark dropdown-toggle" href="#" id="adminUsersDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">کاربران و نقشها</a>
|
||||
<div class="dropdown-menu" aria-labelledby="adminUsersDropdown">
|
||||
<a class="dropdown-item" asp-area="Admin" asp-page="Users">کاربران</a>
|
||||
<a class="dropdown-item" asp-area="Admin" asp-page="Roles">نقشها</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" href="javascript:toggleColorScheme();" title="تغییر پوسته">
|
||||
<span id="icon-sun"><svg height="24px" viewBox="0 -960 960 960" width="24px"><path d="M440-760v-160h80v160h-80Zm266 110-55-55 112-115 56 57-113 113Zm54 210v-80h160v80H760ZM440-40v-160h80v160h-80ZM254-652 140-763l57-56 113 113-56 54Zm508 512L651-255l54-54 114 110-57 59ZM40-440v-80h160v80H40Zm157 300-56-57 112-112 29 27 29 28-114 114Zm283-100q-100 0-170-70t-70-170q0-100 70-170t170-70q100 0 170 70t70 170q0 100-70 170t-170 70Z" /></svg></span>
|
||||
|
||||
@ -713,7 +713,6 @@ body.up-modal-open {
|
||||
}
|
||||
|
||||
.up-verse-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 26px;
|
||||
@ -788,3 +787,57 @@ body.up-modal-open {
|
||||
color: var(--up-link-hover, #0056b3);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ---------- role chips (Users.cshtml) ---------- */
|
||||
|
||||
.up-role-chip-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--up-space-2);
|
||||
flex-wrap: wrap;
|
||||
padding: var(--up-space-2) 0;
|
||||
}
|
||||
|
||||
.up-role-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--up-surface-alt);
|
||||
border: 1px solid var(--up-border);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.up-role-chip__remove {
|
||||
cursor: pointer;
|
||||
color: var(--up-text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.up-role-chip__remove:hover {
|
||||
color: var(--up-danger, #c0392b);
|
||||
}
|
||||
|
||||
/* ---------- permission tree (Roles.cshtml) ---------- */
|
||||
|
||||
.up-perm-tree {
|
||||
text-align: right;
|
||||
margin-bottom: var(--up-space-3);
|
||||
}
|
||||
|
||||
.up-perm-tree__item {
|
||||
margin-top: var(--up-space-3);
|
||||
}
|
||||
|
||||
.up-perm-tree__item:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.up-perm-tree__ops {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--up-space-3);
|
||||
margin-top: var(--up-space-1);
|
||||
padding-right: var(--up-space-3);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user