From d33eb7c8965737db468804d3705a886e7db08dc5 Mon Sep 17 00:00:00 2001
From: Rory&
Date: Thu, 11 Jan 2024 07:31:54 +0100
Subject: Policy list editor rewrite
---
MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor | 421 +++++++++++------------
1 file changed, 199 insertions(+), 222 deletions(-)
(limited to 'MatrixRoomUtils.Web/Pages/Rooms')
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
index dbe0648..b89d59c 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
@@ -1,192 +1,124 @@
@page "/Rooms/{RoomId}/Policies"
@using LibMatrix
-@using LibMatrix.Homeservers
@using ArcaneLibs.Extensions
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.EventTypes.Spec.State.Policy
@using System.Diagnostics
-@using System.Diagnostics.CodeAnalysis
-@using LibMatrix.Extensions
-@using LibMatrix.Responses
-Policy list editor - Editing @RoomId
-
+@using LibMatrix.RoomTypes
+@using System.Collections.Frozen
+@using System.Reflection
+@using ArcaneLibs.Attributes
+@using LibMatrix.EventTypes
-
- This policy list contains @GetPolicyCount(typeof(ServerPolicyRuleEventContent)) server bans,
- @GetPolicyCount(typeof(RoomPolicyRuleEventContent)) room bans and
- @GetPolicyCount(typeof(UserPolicyRuleEventContent)) user bans.
- @foreach (var (key, value) in PolicyEventsByType) {
-
@key.Name: @value.Count
- }
-
-Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)
+@using MatrixRoomUtils.Web.Shared.PolicyEditorComponents
-Server policies
+Policy list editor - Editing @RoomId
-@if (!GetPolicyEventsByType(typeof(ServerPolicyRuleEventContent)).Any()) {
- No server policies
-}
-else {
-
-
-
- Server
- Reason
- Expires
- Actions
-
-
-
- @foreach (var policyEvent in GetValidPolicyEventsByType(typeof(ServerPolicyRuleEventContent))) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
-
-
- Entity: @policyData.Entity
- State: @policyEvent.StateKey
-
- @policyData.Reason
-
- @policyData.ExpiryDateTime
-
-
- await RemovePolicyAsync(policyEvent)" *@>Edit
- @* await RemovePolicyAsync(policyEvent)" #1#>Remove *@
-
-
- }
-
-
-
- Redacted or invalid events
-
-
-
- State key
- Serialised Contents
-
-
-
- @foreach (var policyEvent in GetInvalidPolicyEventsByType(typeof(ServerPolicyRuleEventContent))) {
-
- @policyEvent.StateKey
- @policyEvent.RawContent.ToJson(false, true)
-
- }
-
-
-
+@* Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!) *@
+Create new policy
+
+@if (Loading) {
+ Loading...
}
-Room policies
-
-@if (!GetPolicyEventsByType(typeof(RoomPolicyRuleEventContent)).Any()) {
- No room policies
+else if (PolicyEventsByType is not { Count: > 0 }) {
+ No policies yet
}
else {
-
-
-
- Room
- Reason
- Expires
- Actions
-
-
-
- @foreach (var policyEvent in GetValidPolicyEventsByType(typeof(RoomPolicyRuleEventContent))) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
-
- Entity: @policyData.Entity State: @policyEvent.StateKey
- @policyData.Reason
-
- @policyData.ExpiryDateTime
-
-
- await RemovePolicyAsync(policyEvent)" *@>Remove
-
-
- }
-
-
-
- Redacted or invalid events
-
-
-
- State key
- Serialised Contents
-
-
-
- @foreach (var policyEvent in GetInvalidPolicyEventsByType(typeof(RoomPolicyRuleEventContent))) {
+ @foreach (var (type, value) in PolicyEventsByType) {
+
+ @(GetValidPolicyEventsByType(type).Count) active,
+ @(GetInvalidPolicyEventsByType(type).Count) invalid
+ (@value.Count total)
+ @(GetPolicyTypeName(type).ToLower())
+
+ }
+
+ @foreach (var type in KnownPolicyTypes.OrderByDescending(t => GetPolicyEventsByType(t).Count)) {
+
+
+
+ @($"{GetPolicyTypeName(type)}: {GetPolicyEventsByType(type).Count} policies")
+
+
+
+
+ @{
+ var policies = GetValidPolicyEventsByType(type);
+ var invalidPolicies = GetInvalidPolicyEventsByType(type);
+ // enumerate all properties with friendly name
+ var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(x => (x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyNameOrNull()) is not null)
+ .Where(x => x.GetCustomAttribute() is null)
+ .ToFrozenSet();
+ var propNames = props.Select(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName()!).ToFrozenSet();
+ }
+
- @policyEvent.StateKey
- @policyEvent.RawContent!.ToJson(false, true)
+ @foreach (var name in propNames) {
+ @name
+ }
+ Actions
- }
-
-
-
-}
-User policies
-
-@if (!GetPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Any()) {
- No user policies
-}
-else {
-
-
-
- @if (EnableAvatars) {
-
- }
- User
- Reason
- Expires
- Actions
-
-
-
- @foreach (var policyEvent in GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent))) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
-
- @if (EnableAvatars) {
-
- @if (Avatars.ContainsKey(policyData.Entity)) {
-
+
+
+ @foreach (var policy in policies.OrderBy(x => x.RawContent?["entity"]?.GetValue())) {
+
+ @{
+ var typedContent = policy.TypedContent!;
+ var proxySafeProps = typedContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(x => props.Any(y => y.Name == x.Name))
+ .ToFrozenSet();
+ Console.WriteLine($"{proxySafeProps?.Count} proxy safe props found in {policies.FirstOrDefault()?.TypedContent?.GetType()}");
}
-
+ @foreach (var prop in proxySafeProps ?? Enumerable.Empty()) {
+ @prop.GetGetMethod()?.Invoke(typedContent, null)
+ }
+
+
+ @if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, policy.Type)) {
+ Edit
+ Remove
+ @if (policy.IsLegacyType) {
+ Update policy type
+ }
+ }
+
+
+
}
- Entity: @string.Join("", policyData.Entity.Take(64)) State: @string.Join("", policyEvent.StateKey.Take(64))
- @policyData.Reason
-
- @policyData.ExpiryDateTime
-
-
- await RemovePolicyAsync(policyEvent)" *@>Remove
-
-
- }
-
-
-
- Redacted or invalid events
-
-
-
- State key
- Serialised Contents
-
-
-
- @foreach (var policyEvent in GetInvalidPolicyEventsByType(typeof(UserPolicyRuleEventContent))) {
-
- @policyEvent.StateKey
- @policyEvent.RawContent.ToJson(false, true)
-
- }
-
-
-
+
+
+
+
+
+ @("Invalid " + GetPolicyTypeName(type).ToLower())
+
+
+
+
+
+ State key
+ Json contents
+
+
+
+ @foreach (var policy in invalidPolicies) {
+
+ @policy.StateKey
+
+ @policy.RawContent.ToJson(true, false)
+
+
+ }
+
+
+
+
+ }
+}
+
+@if (CurrentlyEditingEvent is not null) {
+
}
@code {
@@ -197,47 +129,59 @@ else {
private const bool Debug = false;
#endif
+ private bool Loading { get; set; } = true;
//get room list
// - sync withroom list filter
// Type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
[Parameter]
- public string? RoomId { get; set; }
+ public string RoomId { get; set; } = null!;
private bool _enableAvatars;
+ private StateEventResponse? _currentlyEditingEvent;
- static readonly Dictionary Avatars = new();
+ // static readonly Dictionary Avatars = new();
// static readonly Dictionary Servers = new();
// private static List PolicyEvents { get; set; } = new();
private Dictionary> PolicyEventsByType { get; set; } = new();
- public bool EnableAvatars {
- get => _enableAvatars;
+ private StateEventResponse? CurrentlyEditingEvent {
+ get => _currentlyEditingEvent;
set {
- _enableAvatars = value;
- if (value) GetAllAvatars();
+ _currentlyEditingEvent = value;
+ StateHasChanged();
}
}
+ // public bool EnableAvatars {
+ // get => _enableAvatars;
+ // set {
+ // _enableAvatars = value;
+ // if (value) GetAllAvatars();
+ // }
+ // }
+
+ private AuthenticatedHomeserverGeneric Homeserver { get; set; }
+ private GenericRoom Room { get; set; }
+ private RoomPowerLevelEventContent PowerLevels { get; set; }
+
protected override async Task OnInitializedAsync() {
var sw = Stopwatch.StartNew();
await base.OnInitializedAsync();
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- RoomId = RoomId.Replace('~', '.');
+ Homeserver = (await MRUStorage.GetCurrentSessionOrNavigate())!;
+ if (Homeserver is null) return;
+ Room = Homeserver.GetRoom(RoomId!);
+ PowerLevels = (await Room.GetPowerLevelsAsync())!;
await LoadStatesAsync();
Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!");
}
private async Task LoadStatesAsync() {
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
-
- var room = hs.GetRoom(RoomId);
-
- var states = room.GetFullStateAsync();
+ Loading = true;
+ var states = Room.GetFullStateAsync();
+ PolicyEventsByType.Clear();
await foreach (var state in states) {
if (state is null) continue;
if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue;
@@ -245,46 +189,79 @@ else {
PolicyEventsByType[state.MappedType].Add(state);
}
+ Loading = false;
StateHasChanged();
}
- private async Task GetAllAvatars() {
- // if (!_enableAvatars) return;
- Console.WriteLine("Getting avatars...");
- var users = GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Select(x => x.RawContent!["entity"]!.GetValue()).Where(x => x.Contains(':') && !x.Contains("*")).ToList();
- Console.WriteLine($"Got {users.Count} users!");
- var usersByHomeServer = users.GroupBy(x => x!.Split(':')[1]).ToDictionary(x => x.Key!, x => x.ToList());
- Console.WriteLine($"Got {usersByHomeServer.Count} homeservers!");
- var homeserverTasks = usersByHomeServer.Keys.Select(x => RemoteHomeserver.TryCreate(x)).ToAsyncEnumerable();
- await foreach (var server in homeserverTasks) {
- if (server is null) continue;
- var profileTasks = usersByHomeServer[server.BaseUrl].Select(x => TryGetProfile(server, x)).ToList();
- await Task.WhenAll(profileTasks);
- profileTasks.RemoveAll(x => x.Result is not { Value: { AvatarUrl: not null } });
- foreach (var profile in profileTasks.Select(x => x.Result!.Value)) {
- // if (profile is null) continue;
- if (!string.IsNullOrWhiteSpace(profile.Value.AvatarUrl)) {
- var url = await hsResolver.ResolveMediaUri(server.BaseUrl, profile.Value.AvatarUrl);
- Avatars.TryAdd(profile.Key, url);
- }
- else Avatars.TryAdd(profile.Key, null);
- }
- StateHasChanged();
- }
+ // private async Task GetAllAvatars() {
+ // // if (!_enableAvatars) return;
+ // Console.WriteLine("Getting avatars...");
+ // var users = GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Select(x => x.RawContent!["entity"]!.GetValue()).Where(x => x.Contains(':') && !x.Contains("*")).ToList();
+ // Console.WriteLine($"Got {users.Count} users!");
+ // var usersByHomeServer = users.GroupBy(x => x!.Split(':')[1]).ToDictionary(x => x.Key!, x => x.ToList());
+ // Console.WriteLine($"Got {usersByHomeServer.Count} homeservers!");
+ // var homeserverTasks = usersByHomeServer.Keys.Select(x => RemoteHomeserver.TryCreate(x)).ToAsyncEnumerable();
+ // await foreach (var server in homeserverTasks) {
+ // if (server is null) continue;
+ // var profileTasks = usersByHomeServer[server.BaseUrl].Select(x => TryGetProfile(server, x)).ToList();
+ // await Task.WhenAll(profileTasks);
+ // profileTasks.RemoveAll(x => x.Result is not { Value: { AvatarUrl: not null } });
+ // foreach (var profile in profileTasks.Select(x => x.Result!.Value)) {
+ // // if (profile is null) continue;
+ // if (!string.IsNullOrWhiteSpace(profile.Value.AvatarUrl)) {
+ // var url = await hsResolver.ResolveMediaUri(server.BaseUrl, profile.Value.AvatarUrl);
+ // Avatars.TryAdd(profile.Key, url);
+ // }
+ // else Avatars.TryAdd(profile.Key, null);
+ // }
+ //
+ // StateHasChanged();
+ // }
+ // }
+ //
+ // private async Task?> TryGetProfile(RemoteHomeserver server, string mxid) {
+ // try {
+ // return new KeyValuePair(mxid, await server.GetProfileAsync(mxid));
+ // }
+ // catch {
+ // return null;
+ // }
+ // }
+
+ private List GetPolicyEventsByType(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type] : [];
+
+ private List GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type)
+ .Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
+
+ private List GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type)
+ .Where(x => string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
+
+ private string? GetPolicyTypeNameOrNull(Type type) => type.GetFriendlyNamePluralOrNull()
+ ?? type.GetCustomAttributes()
+ .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.EventName))?.EventName;
+
+ private string GetPolicyTypeName(Type type) => GetPolicyTypeNameOrNull(type) ?? type.Name;
+
+ private async Task RemovePolicyAsync(StateEventResponse policyEvent) {
+ await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, new { });
+ PolicyEventsByType[policyEvent.MappedType].Remove(policyEvent);
+ await LoadStatesAsync();
}
- private async Task?> TryGetProfile(RemoteHomeserver server, string mxid) {
- try {
- return new KeyValuePair(mxid, await server.GetProfileAsync(mxid));
- }
- catch {
- return null;
- }
+ private async Task UpdatePolicyAsync(StateEventResponse policyEvent) {
+ await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, policyEvent.RawContent);
+ await LoadStatesAsync();
}
- private List GetPolicyEventsByType(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type] : [];
- private List GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type).Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
- private List GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type).Where(x => string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
- private int GetPolicyCount(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type].Count : 0;
+ private async Task UpgradePolicyAsync(StateEventResponse policyEvent) {
+ policyEvent.RawContent["upgraded_from_type"] = policyEvent.Type;
+ await LoadStatesAsync();
+ }
+
+ private static FrozenSet KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet();
+
+ // event types, unnamed
+ private static Dictionary PolicyTypes = KnownPolicyTypes
+ .ToDictionary(x => x.GetCustomAttributes().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x);
}
\ No newline at end of file
--
cgit 1.4.1