using PrivaPub.ClientModels.Resources; using System.ComponentModel.DataAnnotations; namespace PrivaPub.ClientModels.Admin { // The staff's acts on accounts and posts (completion program R6). Moderators silence and suspend personas here and // accounts elsewhere, remove posts and resolve reports with one of those acts; administrators also change logins // (roles, bans, deletions, two-factor sign-in). Every act is kept in the staff's log. public static class ModerationActions { public const string None = "none"; public const string Silence = "silence"; public const string Unsilence = "unsilence"; public const string Suspend = "suspend"; public const string Unsuspend = "unsuspend"; public const string RemovePosts = "remove_posts";//a report's resolution only: the posts it names public static bool IsAccountAction(string action) => action is Silence or Unsilence or Suspend or Unsuspend; public static bool IsResolution(string action) => action is None or Silence or Suspend or RemovePosts; } // POST /clientapi/moderator/accounts/moderate: a persona here (its id) or an account elsewhere (its id here) public class ModerateAccountForm { [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(64, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string AccountId { get; set; } [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(16, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Action { get; set; }//ModerationActions: silence, unsilence, suspend, unsuspend // the staff's own words: kept in the log and on the account, never shown to it [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Note { get; set; } // what a persona here is told with its warning (Mastodon's moderation_warning); nothing reaches an account elsewhere [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Warning { get; set; } } // POST /clientapi/moderator/posts/remove: a post here is deleted as its author would (the deletion federates), a copy of // a post elsewhere is taken down here only public class RemovePostForm { [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(64, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string PostId { get; set; } [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Note { get; set; } [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Warning { get; set; } } // POST /clientapi/moderator/reports/{id}/resolve: the body is optional; without one, or with action none, the report is // only marked resolved public class ResolveReportForm { [StringLength(16, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Action { get; set; } = ModerationActions.None;//none, silence, suspend, remove_posts [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Note { get; set; } [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Warning { get; set; } } // an account as moderators see it: a persona here (never its login) or an account elsewhere, and what the staff did to it public class ViewModeratedAccount { public string Id { get; set; } public bool IsLocal { get; set; } = false; public string UserName { get; set; } public string DisplayName { get; set; } public string Handle { get; set; }//user@host public string Url { get; set; }//its actor's id public string Domain { get; set; } public DateTime? SilencedAt { get; set; } public DateTime? SuspendedAt { get; set; } public bool DomainSilenced { get; set; } = false;//an account elsewhere whose server is silenced: limited without its own silence public bool DomainSuspended { get; set; } = false; public string Note { get; set; }//the staff's last note on it } // GET /clientapi/moderator/accounts?q= and /clientapi/moderator/accounts/moderated?offset=&limit=: Next is the offset of // the next page, null on the last public class ViewModeratedAccountsPage { public List Items { get; set; } = []; public int? Next { get; set; } } // a login as administrators see it (GET /clientapi/admin/roots), with its personas: only administrators ever read a login // next to its personas public class ViewAdminRoot { public string Id { get; set; } public string UserName { get; set; } public string Role { get; set; }//user, moderator, admin public bool IsBanned { get; set; } = false; public bool HasEmail { get; set; } = false; public bool IsEmailValidated { get; set; } = false; public bool HasTotp { get; set; } = false; public bool IsYou { get; set; } = false; public DateTime CreatedAt { get; set; } public List Personas { get; set; } = []; } public class ViewAdminPersona { public string Id { get; set; } public string UserName { get; set; } public string DisplayName { get; set; } public string Handle { get; set; } public DateTime? SilencedAt { get; set; } public DateTime? SuspendedAt { get; set; } public List Groups { get; set; } = [];//the groups it owns } public class ViewAdminGroup { public string Id { get; set; } public string UserName { get; set; } public string Name { get; set; } public bool IsCommunity { get; set; } = false; public int MembersCount { get; set; } = 0; } // newest first; Next is the `before` of the next page, null on the last public class ViewAdminRootsPage { public List Items { get; set; } = []; public string Next { get; set; } } // POST /clientapi/admin/roots/{id}/role public class RootRoleForm { [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(16, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Role { get; set; }//user, moderator, admin } // what can't be undone asks for the administrator's own password: deleting a persona or a group public class AdminPasswordForm { [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), Display(Name = "Password", ResourceType = typeof(FieldsNameResource))] public string Password { get; set; } [StringLength(1000, ErrorMessageResourceName = "MaxLengthString", ErrorMessageResourceType = typeof(ErrorsResource))] public string Note { get; set; } } }