Add support for signatures

Add config values to `composing` config section to enable signatures:

   signature_file Path                   (optional) Plain text file with signature that will pre-populate an email draft.  Signatures must be explicitly enabled to be used, otherwise this setting will be ignored.  (None)

   use_signature bool                    Pre-populate email drafts with signature, if any.  meli will lookup the signature value in this order:
                                         1.   The signature_file setting.
                                         2.   ${XDG_CONFIG_DIR}/meli/<account>/signature
                                         3.   ${XDG_CONFIG_DIR}/meli/signature
                                         4.   ${XDG_CONFIG_DIR}/signature
                                         5.   ${HOME}/.signature
                                         6.   No signature otherwise.
                                         (false)

   signature_delimiter String            (optional) Signature delimiter, that is, text that will be prefixed to your signature to separate it from the email body.  (‘\n\n-- \n’)

Closes #498

Resolves: https://git.meli-email.org/meli/meli/issues/498
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/500/head
Manos Pitsidianakis 2 weeks ago
parent b930cb4940
commit 77e7c3df60
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -1030,6 +1030,36 @@ or draft body mention attachments but they are missing.
.Ic empty-draft-warn
— Warn if draft has no subject and no body.
.El
.It Ic signature_file Ar Path
.Pq Em optional
Plain text file with signature that will pre-populate an email draft.
Signatures must be explicitly enabled to be used, otherwise this setting will be ignored.
.Pq Em None \" default value
.It Ic use_signature Ar bool
Pre-populate email drafts with signature, if any.
.Sy meli
will lookup the signature value in this order:
.Bl -enum -compact
.It
The
.Ic signature_file
setting.
.It
.Pa ${XDG_CONFIG_DIR}/meli/<account>/signature
.It
.Pa ${XDG_CONFIG_DIR}/meli/signature
.It
.Pa ${XDG_CONFIG_DIR}/signature
.It
.Pa ${HOME}/.signature
.It
No signature otherwise.
.El
.Pq Em false \" default value
.It Ic signature_delimiter Ar String
.Pq Em optional
Signature delimiter, that is, text that will be prefixed to your signature to separate it from the email body.
.Pq Ql \en\en\-\- \en
.El
.\"
.\"

@ -29,6 +29,7 @@ use std::{
io,
ops::{Index, IndexMut},
os::unix::fs::PermissionsExt,
path::{Path, PathBuf},
pin::Pin,
result,
sync::{Arc, RwLock},
@ -42,7 +43,7 @@ use melib::{
error::{Error, ErrorKind, NetworkErrorKind, Result},
log,
thread::Threads,
utils::{fnmatch::Fnmatch, futures::sleep, random},
utils::{fnmatch::Fnmatch, futures::sleep, random, shellexpand::ShellExpandTrait},
Contacts, SortField, SortOrder,
};
use smallvec::SmallVec;
@ -1800,6 +1801,33 @@ impl Account {
IsAsync::Blocking
}
}
pub fn signature_file(&self) -> Option<PathBuf> {
xdg::BaseDirectories::with_profile("meli", &self.name)
.ok()
.and_then(|d| {
d.place_config_file("signature")
.ok()
.filter(|p| p.is_file())
})
.or_else(|| {
xdg::BaseDirectories::with_prefix("meli")
.ok()
.and_then(|d| {
d.place_config_file("signature")
.ok()
.filter(|p| p.is_file())
})
})
.or_else(|| {
xdg::BaseDirectories::new().ok().and_then(|d| {
d.place_config_file("signature")
.ok()
.filter(|p| p.is_file())
})
})
.or_else(|| Some(Path::new("~/.signature").expand()).filter(|p| p.is_file()))
}
}
impl Index<&MailboxHash> for Account {

@ -21,6 +21,8 @@
//! Configuration for composing email.
use std::path::PathBuf;
use indexmap::IndexMap;
use melib::{conf::ActionFlag, email::HeaderName};
use serde::{de, Deserialize, Deserializer};
@ -110,6 +112,34 @@ pub struct ComposingSettings {
/// Disabled `compose-hooks`.
#[serde(default, alias = "disabled-compose-hooks")]
pub disabled_compose_hooks: Vec<String>,
/// Plain text file with signature that will pre-populate an email draft.
///
/// Signatures must be explicitly enabled to be used, otherwise this setting
/// will be ignored.
///
/// Default: `None`
#[serde(default, alias = "signature-file")]
pub signature_file: Option<PathBuf>,
/// Pre-populate email drafts with signature, if any.
///
/// `meli` will lookup the signature value in this order:
///
/// 1. The `signature_file` setting.
/// 2. `${XDG_CONFIG_DIR}/meli/<account>/signature`
/// 3. `${XDG_CONFIG_DIR}/meli/signature`
/// 4. `${XDG_CONFIG_DIR}/signature`
/// 5. `${HOME}/.signature`
/// 6. No signature otherwise.
///
/// Default: `false`
#[serde(default = "false_val", alias = "use-signature")]
pub use_signature: bool,
/// Signature delimiter, that is, text that will be prefixed to your
/// signature to separate it from the email body.
///
/// Default: `"\n\n-- \n"`
#[serde(default, alias = "signature-delimiter")]
pub signature_delimiter: Option<String>,
}
impl Default for ComposingSettings {
@ -129,6 +159,9 @@ impl Default for ComposingSettings {
reply_prefix: res(),
custom_compose_hooks: vec![],
disabled_compose_hooks: vec![],
signature_file: None,
use_signature: false,
signature_delimiter: None,
}
}
}

File diff suppressed because one or more lines are too long

@ -20,7 +20,9 @@
*/
use std::{
borrow::Cow,
convert::TryInto,
fmt::Write as _,
future::Future,
io::Write,
pin::Pin,
@ -241,7 +243,41 @@ impl Composer {
format!("meli {}", option_env!("CARGO_PKG_VERSION").unwrap_or("0.0")),
);
}
if *account_settings!(context[account_hash].composing.format_flowed) {
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
if *account_settings!(context[account_hash].composing.use_signature) {
let override_value = account_settings!(context[account_hash].composing.signature_file)
.as_deref()
.map(Cow::Borrowed)
.filter(|p| p.as_ref().is_file());
let account_value = || {
context.accounts[&account_hash]
.signature_file()
.map(Cow::Owned)
};
if let Some(path) = override_value.or_else(account_value) {
match std::fs::read_to_string(path.as_ref()).chain_err_related_path(path.as_ref()) {
Ok(sig) => {
let mut delimiter =
account_settings!(context[account_hash].composing.signature_delimiter)
.as_deref()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Borrowed("\n\n-- \n"));
if format_flowed {
delimiter = Cow::Owned(delimiter.replace(" \n", " \n\n"));
}
_ = write!(&mut ret.draft.body, "{}{}", delimiter.as_ref(), sig);
}
Err(err) => {
log::error!(
"Could not open signature file for account `{}`: {}.",
context.accounts[&account_hash].name(),
err
);
}
}
}
}
if format_flowed {
ret.pager.set_reflow(melib::text::Reflow::FormatFlowed);
}
ret
@ -420,7 +456,7 @@ impl Composer {
.set_header(HeaderName::TO, envelope.field_from_to_string());
}
ret.draft.body = {
let mut ret = attribution_string(
let mut quoted = attribution_string(
account_settings!(
context[ret.account_hash]
.composing
@ -437,11 +473,12 @@ impl Composer {
),
);
for l in reply_body.lines() {
ret.push('>');
ret.push_str(l);
ret.push('\n');
quoted.push('>');
quoted.push_str(l);
quoted.push('\n');
}
ret
_ = write!(&mut quoted, "{}", ret.draft.body);
quoted
};
ret.account_hash = coordinates.0;

Loading…
Cancel
Save