Merge pull request 'Add user search form to admin panel' (#1143) from moderation-improvement into main

Reviewed-on: https://git.joinplu.me/Plume/Plume/pulls/1143
This commit is contained in:
KitaitiMakoto 2023-03-21 10:29:33 +00:00
commit 613ccbcd94
7 changed files with 71 additions and 5 deletions

View file

@ -422,6 +422,7 @@ impl CommentTree {
mod tests { mod tests {
use super::*; use super::*;
use crate::blogs::Blog; use crate::blogs::Blog;
use crate::db_conn::DbConn;
use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::inbox::{inbox, tests::fill_database, InboxResult};
use crate::safe_string::SafeString; use crate::safe_string::SafeString;
use crate::tests::{db, format_datetime}; use crate::tests::{db, format_datetime};

View file

@ -232,7 +232,9 @@ impl IntoId for Follow {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{tests::db, users::tests as user_tests, users::tests::fill_database}; use crate::{
db_conn::DbConn, tests::db, users::tests as user_tests, users::tests::fill_database,
};
use assert_json_diff::assert_json_eq; use assert_json_diff::assert_json_eq;
use diesel::Connection; use diesel::Connection;
use serde_json::{json, to_value}; use serde_json::{json, to_value};

View file

@ -1027,6 +1027,7 @@ impl From<PostEvent> for Arc<Post> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::db_conn::DbConn;
use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::inbox::{inbox, tests::fill_database, InboxResult};
use crate::mentions::{Mention, NewMention}; use crate::mentions::{Mention, NewMention};
use crate::safe_string::SafeString; use crate::safe_string::SafeString;

View file

@ -15,7 +15,10 @@ use activitystreams::{
prelude::*, prelude::*,
}; };
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; use diesel::{
self, BelongingToDsl, BoolExpressionMethods, ExpressionMethods, OptionalExtension, QueryDsl,
RunQueryDsl, TextExpressionMethods,
};
use ldap3::{LdapConn, Scope, SearchEntry}; use ldap3::{LdapConn, Scope, SearchEntry};
use openssl::{ use openssl::{
hash::MessageDigest, hash::MessageDigest,
@ -186,6 +189,7 @@ impl User {
pub fn count_local(conn: &Connection) -> Result<i64> { pub fn count_local(conn: &Connection) -> Result<i64> {
users::table users::table
.filter(users::instance_id.eq(Instance::get_local()?.id)) .filter(users::instance_id.eq(Instance::get_local()?.id))
.filter(users::role.ne(Role::Instance as i32))
.count() .count()
.get_result(conn) .get_result(conn)
.map_err(Error::from) .map_err(Error::from)
@ -203,6 +207,27 @@ impl User {
} }
} }
pub fn search_local_by_name(
conn: &Connection,
name: &str,
(min, max): (i32, i32),
) -> Result<Vec<User>> {
users::table
.filter(users::instance_id.eq(Instance::get_local()?.id))
.filter(users::role.ne(Role::Instance as i32))
// TODO: use `ilike` instead of `like` for PostgreSQL
.filter(
users::username
.like(format!("%{}%", name))
.or(users::display_name.like(format!("%{}%", name))),
)
.order(users::username.asc())
.offset(min.into())
.limit((max - min).into())
.load::<User>(conn)
.map_err(Error::from)
}
/** /**
* TODO: Should create user record with normalized(lowercased) email * TODO: Should create user record with normalized(lowercased) email
*/ */
@ -431,6 +456,7 @@ impl User {
pub fn get_local_page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<User>> { pub fn get_local_page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<User>> {
users::table users::table
.filter(users::instance_id.eq(Instance::get_local()?.id)) .filter(users::instance_id.eq(Instance::get_local()?.id))
.filter(users::role.ne(Role::Instance as i32))
.order(users::username.asc()) .order(users::username.asc())
.offset(min.into()) .offset(min.into())
.limit((max - min).into()) .limit((max - min).into())

View file

@ -157,6 +157,7 @@ Then try to restart Plume.
routes::instance::admin_mod, routes::instance::admin_mod,
routes::instance::admin_instances, routes::instance::admin_instances,
routes::instance::admin_users, routes::instance::admin_users,
routes::instance::admin_search_users,
routes::instance::admin_email_blocklist, routes::instance::admin_email_blocklist,
routes::instance::add_email_blocklist, routes::instance::add_email_blocklist,
routes::instance::delete_email_blocklist, routes::instance::delete_email_blocklist,

View file

@ -160,7 +160,7 @@ pub fn toggle_block(
)) ))
} }
#[get("/admin/users?<page>")] #[get("/admin/users?<page>", rank = 2)]
pub fn admin_users( pub fn admin_users(
_mod: Moderator, _mod: Moderator,
page: Option<Page>, page: Option<Page>,
@ -171,6 +171,30 @@ pub fn admin_users(
Ok(render!(instance::users( Ok(render!(instance::users(
&(&conn, &rockets).to_context(), &(&conn, &rockets).to_context(),
User::get_local_page(&conn, page.limits())?, User::get_local_page(&conn, page.limits())?,
None,
page.0,
Page::total(User::count_local(&conn)? as i32)
)))
}
#[get("/admin/users?<user>&<page>", rank = 1)]
pub fn admin_search_users(
_mod: Moderator,
user: String,
page: Option<Page>,
conn: DbConn,
rockets: PlumeRocket,
) -> Result<Ructe, ErrorPage> {
let page = page.unwrap_or_default();
let users = if user.is_empty() {
User::get_local_page(&conn, page.limits())?
} else {
User::search_local_by_name(&conn, &user, page.limits())?
};
Ok(render!(instance::users(
&(&conn, &rockets).to_context(),
users,
Some(user.as_str()),
page.0, page.0,
Page::total(User::count_local(&conn)? as i32) Page::total(User::count_local(&conn)? as i32)
))) )))

View file

@ -3,7 +3,7 @@
@use crate::template_utils::*; @use crate::template_utils::*;
@use crate::routes::*; @use crate::routes::*;
@(ctx: BaseContext, users: Vec<User>, page: i32, n_pages: i32) @(ctx: BaseContext, users: Vec<User>, user: Option<&str>, page: i32, n_pages: i32)
@:base(ctx, i18n!(ctx.1, "Users"), {}, {}, { @:base(ctx, i18n!(ctx.1, "Users"), {}, {}, {
<h1>@i18n!(ctx.1, "Users")</h1> <h1>@i18n!(ctx.1, "Users")</h1>
@ -15,6 +15,13 @@
(&uri!(instance::admin_email_blocklist: page=_).to_string(), i18n!(ctx.1, "Email blocklist"), false) (&uri!(instance::admin_email_blocklist: page=_).to_string(), i18n!(ctx.1, "Email blocklist"), false)
]) ])
<form method="get" action="@uri!(instance::admin_search_users: page = _, user = user.unwrap_or_default())">
<header>
<input type="search" name="user" value="@user.unwrap_or_default()">
<input type="submit" value="@i18n!(ctx.1, "Search users")">
</header>
</form>
<form method="post" action="@uri!(instance::edit_users)"> <form method="post" action="@uri!(instance::edit_users)">
<header> <header>
<select name="action"> <select name="action">
@ -46,5 +53,9 @@
} }
</div> </div>
</form> </form>
@paginate(ctx.1, page, n_pages) @if user.is_some() {
@paginate_param(ctx.1, page, n_pages, Some(format!("user={}", encode_query_param(user.unwrap_or_default()))))
} else {
@paginate(ctx.1, page, n_pages)
}
}) })