core: impl help request emails
parent
e9d05fce2e
commit
876e32bb76
|
@ -220,7 +220,8 @@ impl Connection {
|
|||
details => &reason,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("PostAction::Reject {{ reason: {} }}", reason),
|
||||
comment: format!("PostAction::Reject {{ reason: {} }}", reason)
|
||||
.into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -242,7 +243,8 @@ impl Connection {
|
|||
details => &reason,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("PostAction::Defer {{ reason: {} }}", reason),
|
||||
comment: format!("PostAction::Defer {{ reason: {} }}", reason)
|
||||
.into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -283,6 +285,35 @@ impl Connection {
|
|||
) -> Result<()> {
|
||||
let post_policy = self.list_post_policy(list.pk)?;
|
||||
match request {
|
||||
ListRequest::Help => {
|
||||
// [ref:TODO] add test for this
|
||||
trace!(
|
||||
"help action for addresses {:?} in list {}",
|
||||
env.from(),
|
||||
list
|
||||
);
|
||||
let subscription_policy = self.list_subscription_policy(list.pk)?;
|
||||
let subject = format!("Help for {}", list.name);
|
||||
let details = list
|
||||
.generate_help_email(post_policy.as_deref(), subscription_policy.as_deref());
|
||||
for f in env.from() {
|
||||
self.send_reply_with_list_template(
|
||||
TemplateRenderContext {
|
||||
template: Template::GENERIC_HELP,
|
||||
default_fn: Some(Template::default_generic_help),
|
||||
list,
|
||||
context: minijinja::context! {
|
||||
list => &list,
|
||||
subject => &subject,
|
||||
details => &details,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: "Help request".into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
ListRequest::Subscribe => {
|
||||
trace!(
|
||||
"subscribe action for addresses {:?} in list {}",
|
||||
|
@ -311,7 +342,7 @@ impl Connection {
|
|||
details => "No action has been taken since you are already subscribed to the list.",
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("Address {} is already subscribed to list {}", f, list.id),
|
||||
comment: format!("Address {} is already subscribed to list {}", f, list.id).into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -348,8 +379,7 @@ impl Connection {
|
|||
candidate => &v,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: Template::SUBSCRIPTION_REQUEST_NOTICE_OWNER
|
||||
.to_string(),
|
||||
comment: Template::SUBSCRIPTION_REQUEST_NOTICE_OWNER.into(),
|
||||
},
|
||||
list_owners.iter().map(|owner| Cow::Owned(owner.address())),
|
||||
)?;
|
||||
|
@ -371,7 +401,8 @@ impl Connection {
|
|||
comment: format!(
|
||||
"Could not create candidate subscription for {f:?}: \
|
||||
{err}"
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -392,7 +423,8 @@ impl Connection {
|
|||
comment: format!(
|
||||
"Could not create candidate subscription for {f:?}: \
|
||||
{err}"
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
},
|
||||
list_owners.iter().map(|owner| Cow::Owned(owner.address())),
|
||||
)?;
|
||||
|
@ -412,7 +444,8 @@ impl Connection {
|
|||
list => &list,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("Could not create subscription for {f:?}: {err}"),
|
||||
comment: format!("Could not create subscription for {f:?}: {err}")
|
||||
.into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -430,7 +463,8 @@ impl Connection {
|
|||
details => err.to_string(),
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("Could not create subscription for {f:?}: {err}"),
|
||||
comment: format!("Could not create subscription for {f:?}: {err}")
|
||||
.into(),
|
||||
},
|
||||
list_owners.iter().map(|owner| Cow::Owned(owner.address())),
|
||||
)?;
|
||||
|
@ -448,7 +482,7 @@ impl Connection {
|
|||
list => &list,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: Template::SUBSCRIPTION_CONFIRMATION.to_string(),
|
||||
comment: Template::SUBSCRIPTION_CONFIRMATION.into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -475,7 +509,7 @@ impl Connection {
|
|||
list => &list,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("Could not unsubscribe {f:?}: {err}"),
|
||||
comment: format!("Could not unsubscribe {f:?}: {err}").into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -493,7 +527,7 @@ impl Connection {
|
|||
details => err.to_string(),
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: format!("Could not unsubscribe {f:?}: {err}"),
|
||||
comment: format!("Could not unsubscribe {f:?}: {err}").into(),
|
||||
},
|
||||
list_owners.iter().map(|owner| Cow::Owned(owner.address())),
|
||||
)?;
|
||||
|
@ -507,7 +541,7 @@ impl Connection {
|
|||
list => &list,
|
||||
},
|
||||
queue: Queue::Out,
|
||||
comment: Template::UNSUBSCRIPTION_CONFIRMATION.to_string(),
|
||||
comment: Template::UNSUBSCRIPTION_CONFIRMATION.into(),
|
||||
},
|
||||
std::iter::once(Cow::Borrowed(f)),
|
||||
)?;
|
||||
|
@ -536,9 +570,8 @@ impl Connection {
|
|||
}
|
||||
ListRequest::Other(ref req) if req.trim().eq_ignore_ascii_case("password") => {
|
||||
trace!(
|
||||
"list-request password set action for addresses {:?} in list {}",
|
||||
"list-request password set action for addresses {:?} in list {list}",
|
||||
env.from(),
|
||||
list
|
||||
);
|
||||
let body = env.body_bytes(raw);
|
||||
let password = body.text();
|
||||
|
@ -574,38 +607,31 @@ impl Connection {
|
|||
}
|
||||
ListRequest::RetrieveMessages(ref message_ids) => {
|
||||
trace!(
|
||||
"retrieve messages {:?} action for addresses {:?} in list {}",
|
||||
message_ids,
|
||||
"retrieve messages {message_ids:?} action for addresses {:?} in list {list}",
|
||||
env.from(),
|
||||
list
|
||||
);
|
||||
return Err("message retrievals are not implemented yet.".into());
|
||||
}
|
||||
ListRequest::RetrieveArchive(ref from, ref to) => {
|
||||
trace!(
|
||||
"retrieve archive action from {:?} to {:?} for addresses {:?} in list {}",
|
||||
from,
|
||||
to,
|
||||
"retrieve archive action from {from:?} to {to:?} for addresses {:?} in list \
|
||||
{list}",
|
||||
env.from(),
|
||||
list
|
||||
);
|
||||
return Err("message retrievals are not implemented yet.".into());
|
||||
}
|
||||
ListRequest::SetDigest(ref toggle) => {
|
||||
ListRequest::ChangeSetting(ref setting, ref toggle) => {
|
||||
trace!(
|
||||
"set digest action with value {} for addresses {:?} in list {}",
|
||||
toggle,
|
||||
"change setting {setting}, request with value {toggle:?} for addresses {:?} \
|
||||
in list {list}",
|
||||
env.from(),
|
||||
list
|
||||
);
|
||||
return Err("setting digest options via e-mail is not implemented yet.".into());
|
||||
}
|
||||
ListRequest::Other(ref req) => {
|
||||
trace!(
|
||||
"unknown request action {} for addresses {:?} in list {}",
|
||||
req,
|
||||
"unknown request action {req} for addresses {:?} in list {list}",
|
||||
env.from(),
|
||||
list
|
||||
);
|
||||
return Err(format!("Unknown request {req}.").into());
|
||||
}
|
||||
|
@ -712,7 +738,7 @@ impl Connection {
|
|||
Some(list.pk),
|
||||
None,
|
||||
draft.finalise()?.as_bytes(),
|
||||
Some(comment.clone()),
|
||||
Some(comment.to_string()),
|
||||
)?)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -733,5 +759,5 @@ pub struct TemplateRenderContext<'ctx, F: Fn() -> Template> {
|
|||
/// Destination queue in the database.
|
||||
pub queue: Queue,
|
||||
/// Comment for the queue entry in the database.
|
||||
pub comment: String,
|
||||
pub comment: Cow<'static, str>,
|
||||
}
|
||||
|
|
|
@ -123,6 +123,8 @@ pub enum MailJob {
|
|||
/// Type of mailing list request.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub enum ListRequest {
|
||||
/// Get help about a mailing list and its available interfaces.
|
||||
Help,
|
||||
/// Request subscription.
|
||||
Subscribe,
|
||||
/// Request removal of subscription.
|
||||
|
@ -132,8 +134,9 @@ pub enum ListRequest {
|
|||
/// Request reception of specific mailing list posts from `Message-ID`
|
||||
/// values.
|
||||
RetrieveMessages(Vec<String>),
|
||||
/// Request change in digest preferences. (See [`ListSubscription`])
|
||||
SetDigest(bool),
|
||||
/// Request change in subscription settings.
|
||||
/// See [`ListSubscription`].
|
||||
ChangeSetting(String, bool),
|
||||
/// Other type of request.
|
||||
Other(String),
|
||||
}
|
||||
|
@ -152,8 +155,10 @@ impl<S: AsRef<str>> TryFrom<(S, &melib::Envelope)> for ListRequest {
|
|||
Ok(match val {
|
||||
"subscribe" | "request" if env.subject().trim() == "subscribe" => Self::Subscribe,
|
||||
"unsubscribe" | "request" if env.subject().trim() == "unsubscribe" => Self::Unsubscribe,
|
||||
"help" => Self::Help,
|
||||
"request" => Self::Other(env.subject().trim().to_string()),
|
||||
_ => {
|
||||
// [ref:TODO] add ChangeSetting parsing
|
||||
trace!("unknown action = {} for addresses {:?}", val, env.from(),);
|
||||
Self::Other(val.trim().to_string())
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
use super::*;
|
||||
pub mod changesets;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use melib::email::Address;
|
||||
|
||||
/// A database entry and its primary key. Derefs to its inner type.
|
||||
|
@ -257,6 +259,65 @@ impl MailingList {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate help e-mail body containing information on how to subscribe,
|
||||
/// unsubscribe, post and how to contact the list owners.
|
||||
pub fn generate_help_email(
|
||||
&self,
|
||||
post_policy: Option<&PostPolicy>,
|
||||
subscription_policy: Option<&SubscriptionPolicy>,
|
||||
) -> String {
|
||||
format!(
|
||||
"Help for {list_name}\n\n{subscribe}\n\n{post}\n\nTo contact the list owners, send an \
|
||||
e-mail to {contact}\n",
|
||||
list_name = self.name,
|
||||
subscribe = subscription_policy.map_or(
|
||||
Cow::Borrowed("This list is not open to subscriptions."),
|
||||
|p| if p.open {
|
||||
Cow::Owned(format!(
|
||||
"Anyone can subscribe without restrictions. Send an e-mail to {} with the \
|
||||
subject `subscribe`.",
|
||||
self.request_subaddr(),
|
||||
))
|
||||
} else if p.manual {
|
||||
Cow::Borrowed(
|
||||
"The list owners must manually add you to the list of subscriptions.",
|
||||
)
|
||||
} else if p.request {
|
||||
Cow::Owned(format!(
|
||||
"Anyone can request to subscribe. Send an e-mail to {} with the subject \
|
||||
`subscribe` and a confirmation will be sent to you when your request is \
|
||||
approved.",
|
||||
self.request_subaddr(),
|
||||
))
|
||||
} else {
|
||||
Cow::Borrowed("Please contact the list owners for details on how to subscribe.")
|
||||
}
|
||||
),
|
||||
post = post_policy.map_or(Cow::Borrowed("This list does not allow posting."), |p| {
|
||||
if p.announce_only {
|
||||
Cow::Borrowed(
|
||||
"This list is announce only, which means that you can only receive posts \
|
||||
from the list owners.",
|
||||
)
|
||||
} else if p.subscription_only {
|
||||
Cow::Owned(format!(
|
||||
"Only list subscriptions can post to this list. Send your post to {}",
|
||||
self.address
|
||||
))
|
||||
} else if p.approval_needed {
|
||||
Cow::Owned(format!(
|
||||
"Anyone can post, but approval from list owners is required if they are \
|
||||
not subscribed. Send your post to {}",
|
||||
self.address
|
||||
))
|
||||
} else {
|
||||
Cow::Borrowed("This list does not allow posting.")
|
||||
}
|
||||
}),
|
||||
contact = self.owner_mailto().address,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A mailing list subscription entry.
|
||||
|
|
|
@ -45,6 +45,8 @@ impl std::fmt::Display for Template {
|
|||
}
|
||||
|
||||
impl Template {
|
||||
/// Template name for generic list help e-mail.
|
||||
pub const GENERIC_HELP: &str = "generic-help";
|
||||
/// Template name for generic failure e-mail.
|
||||
pub const GENERIC_FAILURE: &str = "generic-failure";
|
||||
/// Template name for generic success e-mail.
|
||||
|
@ -187,4 +189,16 @@ impl Template {
|
|||
body: "{{ details|safe if details else \"\" }}".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a plain template for generic list help replies.
|
||||
pub fn default_generic_help() -> Self {
|
||||
Self {
|
||||
pk: -1,
|
||||
name: Self::GENERIC_HELP.to_string(),
|
||||
list: None,
|
||||
subject: Some("{{ subject if subject else \"Help for mailing list\" }}".to_string()),
|
||||
headers_json: None,
|
||||
body: "{{ details }}".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -699,7 +699,6 @@ mod tests {
|
|||
"6PxWKC/OELf3gyEBRPouxsF7xSZQ==\n",
|
||||
"-----END SSH SIGNATURE-----\n"
|
||||
);
|
||||
const NAMESPACE: &str = "doc-test@example.com";
|
||||
|
||||
let mut sig = SshSignature {
|
||||
email: "user@example.com".to_string(),
|
||||
|
|
|
@ -58,6 +58,7 @@ impl SessionMessages for WritableSession {
|
|||
ret
|
||||
}
|
||||
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
fn add_message(&mut self, message: Message) -> Result<(), ResponseError> {
|
||||
let mut messages: Vec<Message> = self.get(Message::MESSAGE_KEY).unwrap_or_default();
|
||||
messages.push(message);
|
||||
|
|
Loading…
Reference in New Issue