Set list-id header for notification mails

This commit is contained in:
Eike Kettner
2020-04-29 22:44:05 +02:00
parent 03003f2317
commit 0a1b3fcf95
9 changed files with 57 additions and 8 deletions

View File

@ -9,12 +9,21 @@
the periodic-task framework introduced in the last release. the periodic-task framework introduced in the last release.
- Fix issues when converting HTML with unkown links. This especially - Fix issues when converting HTML with unkown links. This especially
happens with e-mails that contain images to attachments. happens with e-mails that contain images to attachments.
- Fix issues when importing e-mail files: - Fix various issues when importing e-mail files, for example:
- fixes encoding problems for mails without explicit transfer encoding - fixes encoding problems for mails without explicit transfer encoding
- add meta info (from, to, subject) to the converted pdf document - add meta info (from, to, subject) to the converted pdf document
- clean html mails to remove unwanted content (like javascript) - clean html mails to remove unwanted content (like javascript)
- Fix classpath issue with javax.mail vs jakarta.mail - Fix classpath issue with javax.mail vs jakarta.mail
### Configuration Changes
The Joex component has config changes:
- A new section `send-mail` containing a `List-Id` e-mail header to
use. Use an empty string (the default) to avoid setting such header.
This header is only applied for notification mails.
## v0.4.0 ## v0.4.0
*Mar. 29, 2020* *Mar. 29, 2020*

View File

@ -141,6 +141,9 @@ val openapiScalaSettings = Seq(
// --- Modules // --- Modules
// Base module, everything depends on this including restapi and
// joexapi modules. This should aim to have least possible
// dependencies
val common = project.in(file("modules/common")). val common = project.in(file("modules/common")).
disablePlugins(RevolverPlugin). disablePlugins(RevolverPlugin).
settings(sharedSettings). settings(sharedSettings).

View File

@ -158,6 +158,7 @@ object OMail {
val fields: Seq[Trans[F]] = Seq( val fields: Seq[Trans[F]] = Seq(
From(sett.mailFrom), From(sett.mailFrom),
Tos(m.recipients), Tos(m.recipients),
XMailer.emil,
Subject(m.subject), Subject(m.subject),
TextBody[F](m.body) TextBody[F](m.body)
) )

View File

@ -0,0 +1,3 @@
package docspell.common
case class MailSendConfig(listId: String)

View File

@ -31,6 +31,19 @@ docspell.joex {
password = "" password = ""
} }
send-mail {
# This is used as the List-Id e-mail header when mails are sent
# from docspell to its users (example: for notification mails). It
# is not used when sending to external recipients. If it is empty,
# no such header is added. Using this header is often useful when
# filtering mails.
#
# It should be a string in angle brackets. See
# https://tools.ietf.org/html/rfc2919 for a formal specification
# of this header.
list-id = ""
}
# Configuration for the job scheduler. # Configuration for the job scheduler.
scheduler { scheduler {

View File

@ -1,7 +1,7 @@
package docspell.joex package docspell.joex
import docspell.analysis.TextAnalysisConfig import docspell.analysis.TextAnalysisConfig
import docspell.common.{Ident, LenientUri} import docspell.common._
import docspell.joex.scheduler.{PeriodicSchedulerConfig, SchedulerConfig} import docspell.joex.scheduler.{PeriodicSchedulerConfig, SchedulerConfig}
import docspell.store.JdbcConfig import docspell.store.JdbcConfig
import docspell.convert.ConvertConfig import docspell.convert.ConvertConfig
@ -18,7 +18,8 @@ case class Config(
houseKeeping: HouseKeepingConfig, houseKeeping: HouseKeepingConfig,
extraction: ExtractConfig, extraction: ExtractConfig,
textAnalysis: TextAnalysisConfig, textAnalysis: TextAnalysisConfig,
convert: ConvertConfig convert: ConvertConfig,
sendMail: MailSendConfig
) )
object Config { object Config {

View File

@ -78,7 +78,7 @@ object JoexAppImpl {
.withTask( .withTask(
JobTask.json( JobTask.json(
NotifyDueItemsArgs.taskName, NotifyDueItemsArgs.taskName,
NotifyDueItemsTask[F](JavaMailEmil(blocker)), NotifyDueItemsTask[F](cfg.sendMail, JavaMailEmil(blocker)),
NotifyDueItemsTask.onCancel[F] NotifyDueItemsTask.onCancel[F]
) )
) )

View File

@ -0,0 +1,14 @@
package docspell.joex.mail
import emil.builder._
object EmilHeader {
// Remove with next emil version
def optionalHeader[F[_]](name: String, value: Option[String]): Trans[F] =
value.map(v => CustomHeader[F](name, v)).getOrElse(Trans[F](identity))
def listId[F[_]](listId: String): Trans[F] =
optionalHeader("List-Id", Option(listId).filter(_.nonEmpty))
}

View File

@ -12,6 +12,7 @@ import docspell.store.records._
import docspell.store.queries.QItem import docspell.store.queries.QItem
import docspell.joex.scheduler.{Context, Task} import docspell.joex.scheduler.{Context, Task}
import cats.data.OptionT import cats.data.OptionT
import docspell.joex.mail.EmilHeader
import docspell.joex.notify.MailContext import docspell.joex.notify.MailContext
import docspell.joex.notify.MailTemplate import docspell.joex.notify.MailTemplate
@ -19,7 +20,7 @@ object NotifyDueItemsTask {
val maxItems: Long = 7 val maxItems: Long = 7
type Args = NotifyDueItemsArgs type Args = NotifyDueItemsArgs
def apply[F[_]: Sync](emil: Emil[F]): Task[F, Args, Unit] = def apply[F[_]: Sync](cfg: MailSendConfig, emil: Emil[F]): Task[F, Args, Unit] =
Task { ctx => Task { ctx =>
for { for {
_ <- ctx.logger.info("Getting mail configuration") _ <- ctx.logger.info("Getting mail configuration")
@ -27,7 +28,7 @@ object NotifyDueItemsTask {
_ <- ctx.logger.info( _ <- ctx.logger.info(
s"Searching for items due in ${ctx.args.remindDays} days…." s"Searching for items due in ${ctx.args.remindDays} days…."
) )
_ <- createMail(mailCfg, ctx) _ <- createMail(cfg, mailCfg, ctx)
.semiflatMap { mail => .semiflatMap { mail =>
for { for {
_ <- ctx.logger.info(s"Sending notification mail to ${ctx.args.recipients}") _ <- ctx.logger.info(s"Sending notification mail to ${ctx.args.recipients}")
@ -56,12 +57,13 @@ object NotifyDueItemsTask {
} }
def createMail[F[_]: Sync]( def createMail[F[_]: Sync](
sendCfg: MailSendConfig,
cfg: RUserEmail, cfg: RUserEmail,
ctx: Context[F, Args] ctx: Context[F, Args]
): OptionT[F, Mail[F]] = ): OptionT[F, Mail[F]] =
for { for {
items <- OptionT.liftF(findItems(ctx)).filter(_.nonEmpty) items <- OptionT.liftF(findItems(ctx)).filter(_.nonEmpty)
mail <- OptionT.liftF(makeMail(cfg, ctx.args, items)) mail <- OptionT.liftF(makeMail(sendCfg, cfg, ctx.args, items))
} yield mail } yield mail
def findItems[F[_]: Sync](ctx: Context[F, Args]): F[Vector[QItem.ListItem]] = def findItems[F[_]: Sync](ctx: Context[F, Args]): F[Vector[QItem.ListItem]] =
@ -81,6 +83,7 @@ object NotifyDueItemsTask {
} yield res } yield res
def makeMail[F[_]: Sync]( def makeMail[F[_]: Sync](
sendCfg: MailSendConfig,
cfg: RUserEmail, cfg: RUserEmail,
args: Args, args: Args,
items: Vector[QItem.ListItem] items: Vector[QItem.ListItem]
@ -99,7 +102,9 @@ object NotifyDueItemsTask {
MailBuilder.build( MailBuilder.build(
From(cfg.mailFrom), From(cfg.mailFrom),
Tos(recp), Tos(recp),
Subject("Next due items"), XMailer.emil,
Subject("[Docspell] Next due items"),
EmilHeader.listId(sendCfg.listId),
MarkdownBody[F](md).withConfig( MarkdownBody[F](md).withConfig(
MarkdownConfig("body { font-size: 10pt; font-family: sans-serif; }") MarkdownConfig("body { font-size: 10pt; font-family: sans-serif; }")
) )