forked from Plume/Plume
Email blocklisting (#718)
* Interface complete for the email blacklisting * Everything seems to work * Neutralize language * fix clippy warnings * Add missing spaces * Added matching test * Correct primary key datatype for postgresql * Address review comments * Add placeholder when empty. Fix missing 'i'
This commit is contained in:
parent
e6bdeb7c4b
commit
f3c05dae62
17 changed files with 341 additions and 6 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2221,6 +2221,7 @@ dependencies = [
|
||||||
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"diesel-derive-newtype 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"diesel-derive-newtype 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
|
||||||
|
drop table email_blocklist;
|
|
@ -0,0 +1,6 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
CREATE TABLE email_blocklist(id SERIAL PRIMARY KEY,
|
||||||
|
email_address TEXT UNIQUE,
|
||||||
|
note TEXT,
|
||||||
|
notify_user BOOLEAN DEFAULT FALSE,
|
||||||
|
notification_text TEXT);
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
|
||||||
|
drop table email_blocklist;
|
6
migrations/sqlite/2020-01-05-232816_add_blocklist/up.sql
Normal file
6
migrations/sqlite/2020-01-05-232816_add_blocklist/up.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
CREATE TABLE email_blocklist(id INTEGER PRIMARY KEY,
|
||||||
|
email_address TEXT UNIQUE,
|
||||||
|
note TEXT,
|
||||||
|
notify_user BOOLEAN DEFAULT FALSE,
|
||||||
|
notification_text TEXT);
|
|
@ -28,6 +28,7 @@ webfinger = "0.4.1"
|
||||||
whatlang = "0.7.1"
|
whatlang = "0.7.1"
|
||||||
shrinkwraprs = "0.2.1"
|
shrinkwraprs = "0.2.1"
|
||||||
diesel-derive-newtype = "0.1.2"
|
diesel-derive-newtype = "0.1.2"
|
||||||
|
glob = "0.3.0"
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
142
plume-models/src/blocklisted_emails.rs
Normal file
142
plume-models/src/blocklisted_emails.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, TextExpressionMethods};
|
||||||
|
use glob::Pattern;
|
||||||
|
|
||||||
|
use schema::email_blocklist;
|
||||||
|
use {Connection, Error, Result};
|
||||||
|
|
||||||
|
#[derive(Clone, Queryable, Identifiable)]
|
||||||
|
#[table_name = "email_blocklist"]
|
||||||
|
pub struct BlocklistedEmail {
|
||||||
|
pub id: i32,
|
||||||
|
pub email_address: String,
|
||||||
|
pub note: String,
|
||||||
|
pub notify_user: bool,
|
||||||
|
pub notification_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, FromForm)]
|
||||||
|
#[table_name = "email_blocklist"]
|
||||||
|
pub struct NewBlocklistedEmail {
|
||||||
|
pub email_address: String,
|
||||||
|
pub note: String,
|
||||||
|
pub notify_user: bool,
|
||||||
|
pub notification_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlocklistedEmail {
|
||||||
|
insert!(email_blocklist, NewBlocklistedEmail);
|
||||||
|
get!(email_blocklist);
|
||||||
|
find_by!(email_blocklist, find_by_id, id as i32);
|
||||||
|
pub fn delete_entries(conn: &Connection, ids: Vec<i32>) -> Result<bool> {
|
||||||
|
use diesel::delete;
|
||||||
|
for i in ids {
|
||||||
|
let be: BlocklistedEmail = BlocklistedEmail::find_by_id(&conn, i)?;
|
||||||
|
delete(&be).execute(conn)?;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
pub fn find_for_domain(conn: &Connection, domain: &str) -> Result<Vec<BlocklistedEmail>> {
|
||||||
|
let effective = format!("%@{}", domain);
|
||||||
|
email_blocklist::table
|
||||||
|
.filter(email_blocklist::email_address.like(effective))
|
||||||
|
.load::<BlocklistedEmail>(conn)
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
pub fn matches_blocklist(conn: &Connection, email: &str) -> Result<Option<BlocklistedEmail>> {
|
||||||
|
let mut result = email_blocklist::table.load::<BlocklistedEmail>(conn)?;
|
||||||
|
for i in result.drain(..) {
|
||||||
|
if let Ok(x) = Pattern::new(&i.email_address) {
|
||||||
|
if x.matches(email) {
|
||||||
|
return Ok(Some(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
pub fn page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<BlocklistedEmail>> {
|
||||||
|
email_blocklist::table
|
||||||
|
.offset(min.into())
|
||||||
|
.limit((max - min).into())
|
||||||
|
.load::<BlocklistedEmail>(conn)
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
pub fn count(conn: &Connection) -> Result<i64> {
|
||||||
|
email_blocklist::table
|
||||||
|
.count()
|
||||||
|
.get_result(conn)
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
pub fn pattern_errors(pat: &str) -> Option<glob::PatternError> {
|
||||||
|
let c = Pattern::new(pat);
|
||||||
|
c.err()
|
||||||
|
}
|
||||||
|
pub fn new(
|
||||||
|
conn: &Connection,
|
||||||
|
pattern: &str,
|
||||||
|
note: &str,
|
||||||
|
show_notification: bool,
|
||||||
|
notification_text: &str,
|
||||||
|
) -> Result<BlocklistedEmail> {
|
||||||
|
let c = NewBlocklistedEmail {
|
||||||
|
email_address: pattern.to_owned(),
|
||||||
|
note: note.to_owned(),
|
||||||
|
notify_user: show_notification,
|
||||||
|
notification_text: notification_text.to_owned(),
|
||||||
|
};
|
||||||
|
BlocklistedEmail::insert(conn, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
use super::*;
|
||||||
|
use diesel::Connection;
|
||||||
|
use instance::tests as instance_tests;
|
||||||
|
use tests::rockets;
|
||||||
|
use Connection as Conn;
|
||||||
|
pub(crate) fn fill_database(conn: &Conn) -> Vec<BlocklistedEmail> {
|
||||||
|
instance_tests::fill_database(conn);
|
||||||
|
let domainblock =
|
||||||
|
BlocklistedEmail::new(conn, "*@bad-actor.com", "Mean spammers", false, "").unwrap();
|
||||||
|
let userblock = BlocklistedEmail::new(
|
||||||
|
conn,
|
||||||
|
"spammer@lax-administration.com",
|
||||||
|
"Decent enough domain, but this user is a problem.",
|
||||||
|
true,
|
||||||
|
"Stop it please",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
vec![domainblock, userblock]
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_match() {
|
||||||
|
let r = rockets();
|
||||||
|
let conn = &*r.conn;
|
||||||
|
conn.test_transaction::<_, (), _>(|| {
|
||||||
|
let various = fill_database(conn);
|
||||||
|
let match1 = "user1@bad-actor.com";
|
||||||
|
let match2 = "spammer@lax-administration.com";
|
||||||
|
let no_match = "happy-user@lax-administration.com";
|
||||||
|
assert_eq!(
|
||||||
|
BlocklistedEmail::matches_blocklist(conn, match1)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.id,
|
||||||
|
various[0].id
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
BlocklistedEmail::matches_blocklist(conn, match2)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.id,
|
||||||
|
various[1].id
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
BlocklistedEmail::matches_blocklist(conn, no_match)
|
||||||
|
.unwrap()
|
||||||
|
.is_none(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ extern crate plume_common;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate plume_macro;
|
extern crate plume_macro;
|
||||||
extern crate reqwest;
|
extern crate reqwest;
|
||||||
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
extern crate rocket_i18n;
|
extern crate rocket_i18n;
|
||||||
extern crate scheduled_thread_pool;
|
extern crate scheduled_thread_pool;
|
||||||
|
@ -32,6 +33,7 @@ extern crate serde_derive;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate tantivy;
|
extern crate tantivy;
|
||||||
|
extern crate glob;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
extern crate walkdir;
|
extern crate walkdir;
|
||||||
extern crate webfinger;
|
extern crate webfinger;
|
||||||
|
@ -53,6 +55,7 @@ pub type Connection = diesel::PgConnection;
|
||||||
/// All the possible errors that can be encoutered in this crate
|
/// All the possible errors that can be encoutered in this crate
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
Blocklisted(bool, String),
|
||||||
Db(diesel::result::Error),
|
Db(diesel::result::Error),
|
||||||
Inbox(Box<InboxError<Error>>),
|
Inbox(Box<InboxError<Error>>),
|
||||||
InvalidValue,
|
InvalidValue,
|
||||||
|
@ -351,6 +354,7 @@ mod tests {
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod api_tokens;
|
pub mod api_tokens;
|
||||||
pub mod apps;
|
pub mod apps;
|
||||||
|
pub mod blocklisted_emails;
|
||||||
pub mod blog_authors;
|
pub mod blog_authors;
|
||||||
pub mod blogs;
|
pub mod blogs;
|
||||||
pub mod comment_seers;
|
pub mod comment_seers;
|
||||||
|
|
|
@ -73,6 +73,15 @@ table! {
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
table! {
|
||||||
|
email_blocklist(id){
|
||||||
|
id -> Int4,
|
||||||
|
email_address -> VarChar,
|
||||||
|
note -> Text,
|
||||||
|
notify_user -> Bool,
|
||||||
|
notification_text -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
follows (id) {
|
follows (id) {
|
||||||
|
|
|
@ -49,7 +49,10 @@ use safe_string::SafeString;
|
||||||
use schema::users;
|
use schema::users;
|
||||||
use search::Searcher;
|
use search::Searcher;
|
||||||
use timeline::Timeline;
|
use timeline::Timeline;
|
||||||
use {ap_url, Connection, Error, PlumeRocket, Result, ITEMS_PER_PAGE};
|
use {
|
||||||
|
ap_url, blocklisted_emails::BlocklistedEmail, Connection, Error, PlumeRocket, Result,
|
||||||
|
ITEMS_PER_PAGE,
|
||||||
|
};
|
||||||
|
|
||||||
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
||||||
|
|
||||||
|
@ -992,6 +995,10 @@ impl NewUser {
|
||||||
) -> Result<User> {
|
) -> Result<User> {
|
||||||
let (pub_key, priv_key) = gen_keypair();
|
let (pub_key, priv_key) = gen_keypair();
|
||||||
let instance = Instance::get_local()?;
|
let instance = Instance::get_local()?;
|
||||||
|
let blocklisted = BlocklistedEmail::matches_blocklist(conn, &email)?;
|
||||||
|
if let Some(x) = blocklisted {
|
||||||
|
return Err(Error::Blocklisted(x.notify_user, x.notification_text));
|
||||||
|
}
|
||||||
|
|
||||||
let res = User::insert(
|
let res = User::insert(
|
||||||
conn,
|
conn,
|
||||||
|
|
|
@ -198,6 +198,9 @@ 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_email_blocklist,
|
||||||
|
routes::instance::add_email_blocklist,
|
||||||
|
routes::instance::delete_email_blocklist,
|
||||||
routes::instance::edit_users,
|
routes::instance::edit_users,
|
||||||
routes::instance::toggle_block,
|
routes::instance::toggle_block,
|
||||||
routes::instance::update_settings,
|
routes::instance::update_settings,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use rocket::{
|
use rocket::{
|
||||||
request::{FormItems, FromForm, LenientForm},
|
request::{Form, FormItems, FromForm, LenientForm},
|
||||||
response::{status, Flash, Redirect},
|
response::{status, Flash, Redirect},
|
||||||
};
|
};
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
@ -13,6 +13,7 @@ use inbox;
|
||||||
use plume_common::activity_pub::{broadcast, inbox::FromId};
|
use plume_common::activity_pub::{broadcast, inbox::FromId};
|
||||||
use plume_models::{
|
use plume_models::{
|
||||||
admin::*,
|
admin::*,
|
||||||
|
blocklisted_emails::*,
|
||||||
comments::Comment,
|
comments::Comment,
|
||||||
db_conn::DbConn,
|
db_conn::DbConn,
|
||||||
headers::Headers,
|
headers::Headers,
|
||||||
|
@ -174,6 +175,61 @@ pub fn admin_users(
|
||||||
Page::total(User::count_local(&*rockets.conn)? as i32)
|
Page::total(User::count_local(&*rockets.conn)? as i32)
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
pub struct BlocklistEmailDeletion {
|
||||||
|
ids: Vec<i32>,
|
||||||
|
}
|
||||||
|
impl<'f> FromForm<'f> for BlocklistEmailDeletion {
|
||||||
|
type Error = ();
|
||||||
|
fn from_form(items: &mut FormItems<'f>, _strict: bool) -> Result<BlocklistEmailDeletion, ()> {
|
||||||
|
let mut c: BlocklistEmailDeletion = BlocklistEmailDeletion { ids: Vec::new() };
|
||||||
|
for item in items {
|
||||||
|
let key = item.key.parse::<i32>();
|
||||||
|
if let Ok(i) = key {
|
||||||
|
c.ids.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[post("/admin/emails/delete", data = "<form>")]
|
||||||
|
pub fn delete_email_blocklist(
|
||||||
|
_mod: Moderator,
|
||||||
|
form: Form<BlocklistEmailDeletion>,
|
||||||
|
rockets: PlumeRocket,
|
||||||
|
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||||
|
BlocklistedEmail::delete_entries(&*rockets.conn, form.0.ids)?;
|
||||||
|
Ok(Flash::success(
|
||||||
|
Redirect::to(uri!(admin_email_blocklist: page = None)),
|
||||||
|
i18n!(rockets.intl.catalog, "Blocks deleted"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/admin/emails/new", data = "<form>")]
|
||||||
|
pub fn add_email_blocklist(
|
||||||
|
_mod: Moderator,
|
||||||
|
form: LenientForm<NewBlocklistedEmail>,
|
||||||
|
rockets: PlumeRocket,
|
||||||
|
) -> Result<Flash<Redirect>, ErrorPage> {
|
||||||
|
BlocklistedEmail::insert(&*rockets.conn, form.0)?;
|
||||||
|
Ok(Flash::success(
|
||||||
|
Redirect::to(uri!(admin_email_blocklist: page = None)),
|
||||||
|
i18n!(rockets.intl.catalog, "Email Blocked"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
#[get("/admin/emails?<page>")]
|
||||||
|
pub fn admin_email_blocklist(
|
||||||
|
_mod: Moderator,
|
||||||
|
page: Option<Page>,
|
||||||
|
rockets: PlumeRocket,
|
||||||
|
) -> Result<Ructe, ErrorPage> {
|
||||||
|
let page = page.unwrap_or_default();
|
||||||
|
Ok(render!(instance::emailblocklist(
|
||||||
|
&rockets.to_context(),
|
||||||
|
BlocklistedEmail::page(&*rockets.conn, page.limits())?,
|
||||||
|
page.0,
|
||||||
|
Page::total(BlocklistedEmail::count(&*rockets.conn)? as i32)
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
/// A structure to handle forms that are a list of items on which actions are applied.
|
/// A structure to handle forms that are a list of items on which actions are applied.
|
||||||
///
|
///
|
||||||
|
@ -307,11 +363,20 @@ fn ban(
|
||||||
) -> Result<(), ErrorPage> {
|
) -> Result<(), ErrorPage> {
|
||||||
let u = User::get(&*conn, id)?;
|
let u = User::get(&*conn, id)?;
|
||||||
u.delete(&*conn, searcher)?;
|
u.delete(&*conn, searcher)?;
|
||||||
|
|
||||||
if Instance::get_local()
|
if Instance::get_local()
|
||||||
.map(|i| u.instance_id == i.id)
|
.map(|i| u.instance_id == i.id)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
|
BlocklistedEmail::insert(
|
||||||
|
&conn,
|
||||||
|
NewBlocklistedEmail {
|
||||||
|
email_address: u.email.clone().unwrap(),
|
||||||
|
note: "Banned".to_string(),
|
||||||
|
notify_user: false,
|
||||||
|
notification_text: "".to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let target = User::one_by_instance(&*conn)?;
|
let target = User::one_by_instance(&*conn)?;
|
||||||
let delete_act = u.delete_activity(&*conn)?;
|
let delete_act = u.delete_activity(&*conn)?;
|
||||||
let u_clone = u.clone();
|
let u_clone = u.clone();
|
||||||
|
|
|
@ -484,8 +484,20 @@ pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_validation(_: Error) -> ValidationErrors {
|
fn to_validation(x: Error) -> ValidationErrors {
|
||||||
let mut errors = ValidationErrors::new();
|
let mut errors = ValidationErrors::new();
|
||||||
|
if let Error::Blocklisted(show, msg) = x {
|
||||||
|
if show {
|
||||||
|
errors.add(
|
||||||
|
"email",
|
||||||
|
ValidationError {
|
||||||
|
code: Cow::from("blocklisted"),
|
||||||
|
message: Some(Cow::from(msg)),
|
||||||
|
params: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
errors.add(
|
errors.add(
|
||||||
"",
|
"",
|
||||||
ValidationError {
|
ValidationError {
|
||||||
|
@ -529,8 +541,7 @@ pub fn create(
|
||||||
"",
|
"",
|
||||||
form.email.to_string(),
|
form.email.to_string(),
|
||||||
User::hash_pass(&form.password).map_err(to_validation)?,
|
User::hash_pass(&form.password).map_err(to_validation)?,
|
||||||
)
|
).map_err(to_validation)?;
|
||||||
.map_err(to_validation)?;
|
|
||||||
Ok(Flash::success(
|
Ok(Flash::success(
|
||||||
Redirect::to(uri!(super::session::new: m = _)),
|
Redirect::to(uri!(super::session::new: m = _)),
|
||||||
i18n!(
|
i18n!(
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), true),
|
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), true),
|
||||||
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
|
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
|
||||||
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
|
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
|
||||||
|
(&uri!(instance::admin_email_blocklist: page=_).to_string(), i18n!(ctx.1, "Email blocklist"), false)
|
||||||
])
|
])
|
||||||
|
|
||||||
<form method="post" action="@uri!(instance::update_settings)">
|
<form method="post" action="@uri!(instance::update_settings)">
|
||||||
|
|
71
templates/instance/emailblocklist.rs.html
Normal file
71
templates/instance/emailblocklist.rs.html
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
@use templates::base;
|
||||||
|
@use plume_models::blocklisted_emails::BlocklistedEmail;
|
||||||
|
@use template_utils::*;
|
||||||
|
@use routes::*;
|
||||||
|
|
||||||
|
@(ctx:BaseContext, emails: Vec<BlocklistedEmail>, page:i32, n_pages:i32)
|
||||||
|
@:base(ctx, i18n!(ctx.1, "Blocklisted Emails"), {}, {}, {
|
||||||
|
<h1>@i18n!(ctx.1,"Blocklisted Emails")</h1>
|
||||||
|
@tabs(&[
|
||||||
|
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
|
||||||
|
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
|
||||||
|
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
|
||||||
|
(&uri!(instance::admin_email_blocklist:page=_).to_string(), i18n!(ctx.1, "Email blocklist"), true),
|
||||||
|
])
|
||||||
|
<form method="post" action="@uri!(instance::add_email_blocklist)">
|
||||||
|
@(Input::new("email_address", i18n!(ctx.1, "Email address"))
|
||||||
|
.details(i18n!(ctx.1, "The email address you wish to block. In order to block domains, you can use globbing syntax, for example '*@example.com' blocks all addresses from example.com"))
|
||||||
|
.set_prop("minlength", 1)
|
||||||
|
.html(ctx.1))
|
||||||
|
@(Input::new("note", i18n!(ctx.1, "Note")).optional().html(ctx.1))
|
||||||
|
<label for="notify_user">@i18n!(ctx.1, "Notify the user?")
|
||||||
|
<input id="notify_user" type="checkbox" name="notify_user">
|
||||||
|
<small>
|
||||||
|
@i18n!(ctx.1, "Optional, shows a message to the user when they attempt to create an account with that address")
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
@(Input::new("notification_text", i18n!(ctx.1, "Blocklisting notification"))
|
||||||
|
.optional()
|
||||||
|
.details(i18n!(ctx.1, "The message to be shown when the user attempts to create an account with this email address")).html(ctx.1))
|
||||||
|
<input type="submit" value='@i18n!(ctx.1, "Add blocklisted address")'>
|
||||||
|
</form>
|
||||||
|
<form method="post" action="@uri!(instance::delete_email_blocklist)">
|
||||||
|
<header>
|
||||||
|
@if emails.is_empty() {
|
||||||
|
<input type="submit" class="destructive" value='@i18n!(ctx.1, "Delete selected emails")'>
|
||||||
|
} else {
|
||||||
|
<p class="center" >@i18n!(ctx.1, "There are no blocked emails on your instance")</p>
|
||||||
|
}
|
||||||
|
</header>
|
||||||
|
<div class="list">
|
||||||
|
@for email in emails {
|
||||||
|
<div class="card flex compact">
|
||||||
|
<input type="checkbox" name="@email.id">
|
||||||
|
<p class="grow">
|
||||||
|
<strong>
|
||||||
|
@i18n!(ctx.1, "Email address:")
|
||||||
|
</strong> @email.email_address
|
||||||
|
</p>
|
||||||
|
<p class="grow">
|
||||||
|
<strong>
|
||||||
|
@i18n!(ctx.1, "Blocklisted for:")
|
||||||
|
</strong> @email.note
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="grow">
|
||||||
|
@if email.notify_user {
|
||||||
|
<strong>
|
||||||
|
@i18n!(ctx.1, "Will notify them on account creation with this message:")
|
||||||
|
</strong>
|
||||||
|
@email.notification_text
|
||||||
|
} else {
|
||||||
|
@i18n!(ctx.1, "The user will be silently prevented from making an account")
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@paginate(ctx.1, page, n_pages)
|
||||||
|
})
|
|
@ -12,6 +12,7 @@
|
||||||
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
|
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
|
||||||
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), true),
|
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), true),
|
||||||
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
|
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
|
||||||
|
(&uri!(instance::admin_email_blocklist:page=_).to_string(), i18n!(ctx.1, "Email blocklist"), false),
|
||||||
])
|
])
|
||||||
|
|
||||||
<div class="list">
|
<div class="list">
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
|
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
|
||||||
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
|
(&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
|
||||||
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), true),
|
(&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), true),
|
||||||
|
(&uri!(instance::admin_email_blocklist: page=_).to_string(), i18n!(ctx.1, "Email blocklist"), false)
|
||||||
])
|
])
|
||||||
|
|
||||||
<form method="post" action="@uri!(instance::edit_users)">
|
<form method="post" action="@uri!(instance::edit_users)">
|
||||||
|
|
Loading…
Reference in a new issue