mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-05 14:45:59 +00:00
Add imap settings
This commit is contained in:
parent
2a5ffd391e
commit
c9de74fd91
@ -12,19 +12,29 @@ import docspell.common._
|
|||||||
import docspell.store._
|
import docspell.store._
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
import docspell.store.queries.QMails
|
import docspell.store.queries.QMails
|
||||||
import OMail.{ItemMail, Sent, SmtpSettings}
|
import OMail.{ImapSettings, ItemMail, Sent, SmtpSettings}
|
||||||
|
|
||||||
trait OMail[F[_]] {
|
trait OMail[F[_]] {
|
||||||
|
|
||||||
def getSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserEmail]]
|
def getSmtpSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserEmail]]
|
||||||
|
|
||||||
def findSettings(accId: AccountId, name: Ident): OptionT[F, RUserEmail]
|
def findSmtpSettings(accId: AccountId, name: Ident): OptionT[F, RUserEmail]
|
||||||
|
|
||||||
def createSettings(accId: AccountId, data: SmtpSettings): F[AddResult]
|
def createSmtpSettings(accId: AccountId, data: SmtpSettings): F[AddResult]
|
||||||
|
|
||||||
def updateSettings(accId: AccountId, name: Ident, data: OMail.SmtpSettings): F[Int]
|
def updateSmtpSettings(accId: AccountId, name: Ident, data: OMail.SmtpSettings): F[Int]
|
||||||
|
|
||||||
def deleteSettings(accId: AccountId, name: Ident): F[Int]
|
def deleteSmtpSettings(accId: AccountId, name: Ident): F[Int]
|
||||||
|
|
||||||
|
def getImapSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserImap]]
|
||||||
|
|
||||||
|
def findImapSettings(accId: AccountId, name: Ident): OptionT[F, RUserImap]
|
||||||
|
|
||||||
|
def createImapSettings(accId: AccountId, data: ImapSettings): F[AddResult]
|
||||||
|
|
||||||
|
def updateImapSettings(accId: AccountId, name: Ident, data: OMail.ImapSettings): F[Int]
|
||||||
|
|
||||||
|
def deleteImapSettings(accId: AccountId, name: Ident): F[Int]
|
||||||
|
|
||||||
def sendMail(accId: AccountId, name: Ident, m: ItemMail): F[SendResult]
|
def sendMail(accId: AccountId, name: Ident, m: ItemMail): F[SendResult]
|
||||||
|
|
||||||
@ -103,15 +113,41 @@ object OMail {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class ImapSettings(
|
||||||
|
name: Ident,
|
||||||
|
imapHost: String,
|
||||||
|
imapPort: Option[Int],
|
||||||
|
imapUser: Option[String],
|
||||||
|
imapPassword: Option[Password],
|
||||||
|
imapSsl: SSLType,
|
||||||
|
imapCertCheck: Boolean
|
||||||
|
) {
|
||||||
|
|
||||||
|
def toRecord(accId: AccountId) =
|
||||||
|
RUserImap.fromAccount(
|
||||||
|
accId,
|
||||||
|
name,
|
||||||
|
imapHost,
|
||||||
|
imapPort,
|
||||||
|
imapUser,
|
||||||
|
imapPassword,
|
||||||
|
imapSsl,
|
||||||
|
imapCertCheck
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def apply[F[_]: Effect](store: Store[F], emil: Emil[F]): Resource[F, OMail[F]] =
|
def apply[F[_]: Effect](store: Store[F], emil: Emil[F]): Resource[F, OMail[F]] =
|
||||||
Resource.pure[F, OMail[F]](new OMail[F] {
|
Resource.pure[F, OMail[F]](new OMail[F] {
|
||||||
def getSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserEmail]] =
|
def getSmtpSettings(
|
||||||
|
accId: AccountId,
|
||||||
|
nameQ: Option[String]
|
||||||
|
): F[Vector[RUserEmail]] =
|
||||||
store.transact(RUserEmail.findByAccount(accId, nameQ))
|
store.transact(RUserEmail.findByAccount(accId, nameQ))
|
||||||
|
|
||||||
def findSettings(accId: AccountId, name: Ident): OptionT[F, RUserEmail] =
|
def findSmtpSettings(accId: AccountId, name: Ident): OptionT[F, RUserEmail] =
|
||||||
OptionT(store.transact(RUserEmail.getByName(accId, name)))
|
OptionT(store.transact(RUserEmail.getByName(accId, name)))
|
||||||
|
|
||||||
def createSettings(accId: AccountId, s: SmtpSettings): F[AddResult] =
|
def createSmtpSettings(accId: AccountId, s: SmtpSettings): F[AddResult] =
|
||||||
(for {
|
(for {
|
||||||
ru <- OptionT(store.transact(s.toRecord(accId).value))
|
ru <- OptionT(store.transact(s.toRecord(accId).value))
|
||||||
ins = RUserEmail.insert(ru)
|
ins = RUserEmail.insert(ru)
|
||||||
@ -119,7 +155,11 @@ object OMail {
|
|||||||
res <- OptionT.liftF(store.add(ins, exists))
|
res <- OptionT.liftF(store.add(ins, exists))
|
||||||
} yield res).getOrElse(AddResult.Failure(new Exception("User not found")))
|
} yield res).getOrElse(AddResult.Failure(new Exception("User not found")))
|
||||||
|
|
||||||
def updateSettings(accId: AccountId, name: Ident, data: SmtpSettings): F[Int] = {
|
def updateSmtpSettings(
|
||||||
|
accId: AccountId,
|
||||||
|
name: Ident,
|
||||||
|
data: SmtpSettings
|
||||||
|
): F[Int] = {
|
||||||
val op = for {
|
val op = for {
|
||||||
um <- OptionT(RUserEmail.getByName(accId, name))
|
um <- OptionT(RUserEmail.getByName(accId, name))
|
||||||
ru <- data.toRecord(accId)
|
ru <- data.toRecord(accId)
|
||||||
@ -129,12 +169,43 @@ object OMail {
|
|||||||
store.transact(op.value).map(_.getOrElse(0))
|
store.transact(op.value).map(_.getOrElse(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteSettings(accId: AccountId, name: Ident): F[Int] =
|
def deleteSmtpSettings(accId: AccountId, name: Ident): F[Int] =
|
||||||
store.transact(RUserEmail.delete(accId, name))
|
store.transact(RUserEmail.delete(accId, name))
|
||||||
|
|
||||||
|
def getImapSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserImap]] =
|
||||||
|
store.transact(RUserImap.findByAccount(accId, nameQ))
|
||||||
|
|
||||||
|
def findImapSettings(accId: AccountId, name: Ident): OptionT[F, RUserImap] =
|
||||||
|
OptionT(store.transact(RUserImap.getByName(accId, name)))
|
||||||
|
|
||||||
|
def createImapSettings(accId: AccountId, data: ImapSettings): F[AddResult] =
|
||||||
|
(for {
|
||||||
|
ru <- OptionT(store.transact(data.toRecord(accId).value))
|
||||||
|
ins = RUserImap.insert(ru)
|
||||||
|
exists = RUserImap.exists(ru.uid, ru.name)
|
||||||
|
res <- OptionT.liftF(store.add(ins, exists))
|
||||||
|
} yield res).getOrElse(AddResult.Failure(new Exception("User not found")))
|
||||||
|
|
||||||
|
def updateImapSettings(
|
||||||
|
accId: AccountId,
|
||||||
|
name: Ident,
|
||||||
|
data: OMail.ImapSettings
|
||||||
|
): F[Int] = {
|
||||||
|
val op = for {
|
||||||
|
um <- OptionT(RUserImap.getByName(accId, name))
|
||||||
|
ru <- data.toRecord(accId)
|
||||||
|
n <- OptionT.liftF(RUserImap.update(um.id, ru))
|
||||||
|
} yield n
|
||||||
|
|
||||||
|
store.transact(op.value).map(_.getOrElse(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteImapSettings(accId: AccountId, name: Ident): F[Int] =
|
||||||
|
store.transact(RUserImap.delete(accId, name))
|
||||||
|
|
||||||
def sendMail(accId: AccountId, name: Ident, m: ItemMail): F[SendResult] = {
|
def sendMail(accId: AccountId, name: Ident, m: ItemMail): F[SendResult] = {
|
||||||
|
|
||||||
val getSettings: OptionT[F, RUserEmail] =
|
val getSmtpSettings: OptionT[F, RUserEmail] =
|
||||||
OptionT(store.transact(RUserEmail.getByName(accId, name)))
|
OptionT(store.transact(RUserEmail.getByName(accId, name)))
|
||||||
|
|
||||||
def createMail(sett: RUserEmail): OptionT[F, Mail[F]] = {
|
def createMail(sett: RUserEmail): OptionT[F, Mail[F]] = {
|
||||||
@ -198,7 +269,7 @@ object OMail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
mailCfg <- getSettings
|
mailCfg <- getSmtpSettings
|
||||||
mail <- createMail(mailCfg)
|
mail <- createMail(mailCfg)
|
||||||
mid <- OptionT.liftF(sendMail(mailCfg.toMailConfig, mail))
|
mid <- OptionT.liftF(sendMail(mailCfg.toMailConfig, mail))
|
||||||
res <- mid.traverse(id => OptionT.liftF(storeMail(id, mailCfg)))
|
res <- mid.traverse(id => OptionT.liftF(storeMail(id, mailCfg)))
|
||||||
|
@ -1414,7 +1414,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/BasicResult"
|
$ref: "#/components/schemas/BasicResult"
|
||||||
/sec/email/settings:
|
/sec/email/settings/smtp:
|
||||||
get:
|
get:
|
||||||
tags: [ E-Mail ]
|
tags: [ E-Mail ]
|
||||||
summary: List email settings for current user.
|
summary: List email settings for current user.
|
||||||
@ -1456,7 +1456,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/BasicResult"
|
$ref: "#/components/schemas/BasicResult"
|
||||||
/sec/email/settings/{name}:
|
/sec/email/settings/smtp/{name}:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "#/components/parameters/name"
|
- $ref: "#/components/parameters/name"
|
||||||
get:
|
get:
|
||||||
@ -1507,6 +1507,99 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/BasicResult"
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
/sec/email/settings/imap:
|
||||||
|
get:
|
||||||
|
tags: [ E-Mail ]
|
||||||
|
summary: List email settings for current user.
|
||||||
|
description: |
|
||||||
|
List all available e-mail settings for the current user.
|
||||||
|
E-Mail settings specify imap connections that can be used to
|
||||||
|
retrieve e-mails.
|
||||||
|
|
||||||
|
Multiple e-mail settings can be specified, they are
|
||||||
|
distinguished by their `name`. The query `q` parameter does a
|
||||||
|
simple substring search in the connection name.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/q"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ImapSettingsList"
|
||||||
|
post:
|
||||||
|
tags: [ E-Mail ]
|
||||||
|
summary: Create new email settings
|
||||||
|
description: |
|
||||||
|
Create new e-mail settings.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ImapSettings"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
/sec/email/settings/imap/{name}:
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/name"
|
||||||
|
get:
|
||||||
|
tags: [ E-Mail ]
|
||||||
|
summary: Return specific email settings by name.
|
||||||
|
description: |
|
||||||
|
Return the stored e-mail settings for the given connection
|
||||||
|
name.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ImapSettings"
|
||||||
|
put:
|
||||||
|
tags: [ E-Mail ]
|
||||||
|
summary: Change specific email settings.
|
||||||
|
description: |
|
||||||
|
Changes all settings for the connection with the given `name`.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ImapSettings"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
delete:
|
||||||
|
tags: [ E-Mail ]
|
||||||
|
summary: Delete e-mail settings.
|
||||||
|
description: |
|
||||||
|
Deletes the e-mail settings with the specified `name`.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
|
||||||
/sec/email/send/{name}/{id}:
|
/sec/email/send/{name}/{id}:
|
||||||
post:
|
post:
|
||||||
@ -1664,6 +1757,43 @@ paths:
|
|||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
|
ImapSettingsList:
|
||||||
|
description: |
|
||||||
|
A list of user email settings.
|
||||||
|
required:
|
||||||
|
- items
|
||||||
|
properties:
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/ImapSettings"
|
||||||
|
ImapSettings:
|
||||||
|
description: |
|
||||||
|
IMAP settings for sending mail.
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- imapHost
|
||||||
|
- from
|
||||||
|
- sslType
|
||||||
|
- ignoreCertificates
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
format: ident
|
||||||
|
imapHost:
|
||||||
|
type: string
|
||||||
|
imapPort:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
imapUser:
|
||||||
|
type: string
|
||||||
|
imapPassword:
|
||||||
|
type: string
|
||||||
|
format: password
|
||||||
|
sslType:
|
||||||
|
type: string
|
||||||
|
ignoreCertificates:
|
||||||
|
type: boolean
|
||||||
CalEventCheckResult:
|
CalEventCheckResult:
|
||||||
description: |
|
description: |
|
||||||
The result of checking a calendar event string.
|
The result of checking a calendar event string.
|
||||||
|
@ -15,7 +15,7 @@ import docspell.backend.auth.AuthToken
|
|||||||
import docspell.backend.ops.OMail
|
import docspell.backend.ops.OMail
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.restapi.model._
|
import docspell.restapi.model._
|
||||||
import docspell.store.records.RUserEmail
|
import docspell.store.records.{RUserEmail, RUserImap}
|
||||||
import docspell.restserver.conv.Conversions
|
import docspell.restserver.conv.Conversions
|
||||||
import docspell.restserver.http4s.QueryParam
|
import docspell.restserver.http4s.QueryParam
|
||||||
|
|
||||||
@ -26,25 +26,38 @@ object MailSettingsRoutes {
|
|||||||
import dsl._
|
import dsl._
|
||||||
|
|
||||||
HttpRoutes.of {
|
HttpRoutes.of {
|
||||||
case GET -> Root :? QueryParam.QueryOpt(q) =>
|
case GET -> Root / "smtp" :? QueryParam.QueryOpt(q) =>
|
||||||
for {
|
for {
|
||||||
list <- backend.mail.getSettings(user.account, q.map(_.q))
|
list <- backend.mail.getSmtpSettings(user.account, q.map(_.q))
|
||||||
res = list.map(convert)
|
res = list.map(convert)
|
||||||
resp <- Ok(EmailSettingsList(res.toList))
|
resp <- Ok(EmailSettingsList(res.toList))
|
||||||
} yield resp
|
} yield resp
|
||||||
|
|
||||||
case GET -> Root / Ident(name) =>
|
case GET -> Root / "imap" :? QueryParam.QueryOpt(q) =>
|
||||||
|
for {
|
||||||
|
list <- backend.mail.getImapSettings(user.account, q.map(_.q))
|
||||||
|
res = list.map(convert)
|
||||||
|
resp <- Ok(ImapSettingsList(res.toList))
|
||||||
|
} yield resp
|
||||||
|
|
||||||
|
case GET -> Root / "smtp" / Ident(name) =>
|
||||||
(for {
|
(for {
|
||||||
ems <- backend.mail.findSettings(user.account, name)
|
ems <- backend.mail.findSmtpSettings(user.account, name)
|
||||||
resp <- OptionT.liftF(Ok(convert(ems)))
|
resp <- OptionT.liftF(Ok(convert(ems)))
|
||||||
} yield resp).getOrElseF(NotFound())
|
} yield resp).getOrElseF(NotFound())
|
||||||
|
|
||||||
case req @ POST -> Root =>
|
case GET -> Root / "imap" / Ident(name) =>
|
||||||
|
(for {
|
||||||
|
ems <- backend.mail.findImapSettings(user.account, name)
|
||||||
|
resp <- OptionT.liftF(Ok(convert(ems)))
|
||||||
|
} yield resp).getOrElseF(NotFound())
|
||||||
|
|
||||||
|
case req @ POST -> Root / "smtp" =>
|
||||||
(for {
|
(for {
|
||||||
in <- OptionT.liftF(req.as[EmailSettings])
|
in <- OptionT.liftF(req.as[EmailSettings])
|
||||||
ru = makeSettings(in)
|
ru = makeSmtpSettings(in)
|
||||||
up <- OptionT.liftF(
|
up <- OptionT.liftF(
|
||||||
ru.traverse(r => backend.mail.createSettings(user.account, r))
|
ru.traverse(r => backend.mail.createSmtpSettings(user.account, r))
|
||||||
)
|
)
|
||||||
resp <- OptionT.liftF(
|
resp <- OptionT.liftF(
|
||||||
Ok(
|
Ok(
|
||||||
@ -56,12 +69,29 @@ object MailSettingsRoutes {
|
|||||||
)
|
)
|
||||||
} yield resp).getOrElseF(NotFound())
|
} yield resp).getOrElseF(NotFound())
|
||||||
|
|
||||||
case req @ PUT -> Root / Ident(name) =>
|
case req @ POST -> Root / "imap" =>
|
||||||
|
(for {
|
||||||
|
in <- OptionT.liftF(req.as[ImapSettings])
|
||||||
|
ru = makeImapSettings(in)
|
||||||
|
up <- OptionT.liftF(
|
||||||
|
ru.traverse(r => backend.mail.createImapSettings(user.account, r))
|
||||||
|
)
|
||||||
|
resp <- OptionT.liftF(
|
||||||
|
Ok(
|
||||||
|
up.fold(
|
||||||
|
err => BasicResult(false, err),
|
||||||
|
ar => Conversions.basicResult(ar, "Mail settings stored.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield resp).getOrElseF(NotFound())
|
||||||
|
|
||||||
|
case req @ PUT -> Root / "smtp" / Ident(name) =>
|
||||||
(for {
|
(for {
|
||||||
in <- OptionT.liftF(req.as[EmailSettings])
|
in <- OptionT.liftF(req.as[EmailSettings])
|
||||||
ru = makeSettings(in)
|
ru = makeSmtpSettings(in)
|
||||||
up <- OptionT.liftF(
|
up <- OptionT.liftF(
|
||||||
ru.traverse(r => backend.mail.updateSettings(user.account, name, r))
|
ru.traverse(r => backend.mail.updateSmtpSettings(user.account, name, r))
|
||||||
)
|
)
|
||||||
resp <- OptionT.liftF(
|
resp <- OptionT.liftF(
|
||||||
Ok(
|
Ok(
|
||||||
@ -75,16 +105,43 @@ object MailSettingsRoutes {
|
|||||||
)
|
)
|
||||||
} yield resp).getOrElseF(NotFound())
|
} yield resp).getOrElseF(NotFound())
|
||||||
|
|
||||||
case DELETE -> Root / Ident(name) =>
|
case req @ PUT -> Root / "imap" / Ident(name) =>
|
||||||
|
(for {
|
||||||
|
in <- OptionT.liftF(req.as[ImapSettings])
|
||||||
|
ru = makeImapSettings(in)
|
||||||
|
up <- OptionT.liftF(
|
||||||
|
ru.traverse(r => backend.mail.updateImapSettings(user.account, name, r))
|
||||||
|
)
|
||||||
|
resp <- OptionT.liftF(
|
||||||
|
Ok(
|
||||||
|
up.fold(
|
||||||
|
err => BasicResult(false, err),
|
||||||
|
n =>
|
||||||
|
if (n > 0) BasicResult(true, "Mail settings stored.")
|
||||||
|
else BasicResult(false, "Mail settings could not be saved")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield resp).getOrElseF(NotFound())
|
||||||
|
|
||||||
|
case DELETE -> Root / "smtp" / Ident(name) =>
|
||||||
for {
|
for {
|
||||||
n <- backend.mail.deleteSettings(user.account, name)
|
n <- backend.mail.deleteSmtpSettings(user.account, name)
|
||||||
|
resp <- Ok(
|
||||||
|
if (n > 0) BasicResult(true, "Mail settings removed")
|
||||||
|
else BasicResult(false, "Mail settings could not be removed")
|
||||||
|
)
|
||||||
|
} yield resp
|
||||||
|
|
||||||
|
case DELETE -> Root / "imap" / Ident(name) =>
|
||||||
|
for {
|
||||||
|
n <- backend.mail.deleteImapSettings(user.account, name)
|
||||||
resp <- Ok(
|
resp <- Ok(
|
||||||
if (n > 0) BasicResult(true, "Mail settings removed")
|
if (n > 0) BasicResult(true, "Mail settings removed")
|
||||||
else BasicResult(false, "Mail settings could not be removed")
|
else BasicResult(false, "Mail settings could not be removed")
|
||||||
)
|
)
|
||||||
} yield resp
|
} yield resp
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def convert(ru: RUserEmail): EmailSettings =
|
def convert(ru: RUserEmail): EmailSettings =
|
||||||
@ -100,7 +157,18 @@ object MailSettingsRoutes {
|
|||||||
!ru.smtpCertCheck
|
!ru.smtpCertCheck
|
||||||
)
|
)
|
||||||
|
|
||||||
def makeSettings(ems: EmailSettings): Either[String, OMail.SmtpSettings] = {
|
def convert(ru: RUserImap): ImapSettings =
|
||||||
|
ImapSettings(
|
||||||
|
ru.name,
|
||||||
|
ru.imapHost,
|
||||||
|
ru.imapPort,
|
||||||
|
ru.imapUser,
|
||||||
|
ru.imapPassword,
|
||||||
|
ru.imapSsl.name,
|
||||||
|
!ru.imapCertCheck
|
||||||
|
)
|
||||||
|
|
||||||
|
def makeSmtpSettings(ems: EmailSettings): Either[String, OMail.SmtpSettings] = {
|
||||||
def readMail(str: String): Either[String, MailAddress] =
|
def readMail(str: String): Either[String, MailAddress] =
|
||||||
MailAddress.parse(str).left.map(err => s"E-Mail address '$str' invalid: $err")
|
MailAddress.parse(str).left.map(err => s"E-Mail address '$str' invalid: $err")
|
||||||
|
|
||||||
@ -122,6 +190,18 @@ object MailSettingsRoutes {
|
|||||||
from,
|
from,
|
||||||
repl
|
repl
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def makeImapSettings(ims: ImapSettings): Either[String, OMail.ImapSettings] =
|
||||||
|
for {
|
||||||
|
sslt <- SSLType.fromString(ims.sslType)
|
||||||
|
} yield OMail.ImapSettings(
|
||||||
|
ims.name,
|
||||||
|
ims.imapHost,
|
||||||
|
ims.imapPort,
|
||||||
|
ims.imapUser,
|
||||||
|
ims.imapPassword,
|
||||||
|
sslt,
|
||||||
|
!ims.ignoreCertificates
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ object NotifyDueItemsRoutes {
|
|||||||
texc <- backend.tag.loadAll(task.args.tagsExclude)
|
texc <- backend.tag.loadAll(task.args.tagsExclude)
|
||||||
conn <-
|
conn <-
|
||||||
backend.mail
|
backend.mail
|
||||||
.getSettings(account, None)
|
.getSmtpSettings(account, None)
|
||||||
.map(
|
.map(
|
||||||
_.find(_.name == task.args.smtpConnection)
|
_.find(_.name == task.args.smtpConnection)
|
||||||
.map(_.name)
|
.map(_.name)
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE `userimap` (
|
||||||
|
`id` varchar(254) not null primary key,
|
||||||
|
`uid` varchar(254) not null,
|
||||||
|
`name` varchar(254) not null,
|
||||||
|
`imap_host` varchar(254) not null,
|
||||||
|
`imap_port` int,
|
||||||
|
`imap_user` varchar(254),
|
||||||
|
`imap_password` varchar(254),
|
||||||
|
`imap_ssl` varchar(254) not null,
|
||||||
|
`imap_certcheck` boolean not null,
|
||||||
|
`created` timestamp not null,
|
||||||
|
unique (`uid`, `name`),
|
||||||
|
foreign key (`uid`) references `user_`(`uid`)
|
||||||
|
);
|
@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE "userimap" (
|
||||||
|
"id" varchar(254) not null primary key,
|
||||||
|
"uid" varchar(254) not null,
|
||||||
|
"name" varchar(254) not null,
|
||||||
|
"imap_host" varchar(254) not null,
|
||||||
|
"imap_port" int,
|
||||||
|
"imap_user" varchar(254),
|
||||||
|
"imap_password" varchar(254),
|
||||||
|
"imap_ssl" varchar(254) not null,
|
||||||
|
"imap_certcheck" boolean not null,
|
||||||
|
"created" timestamp not null,
|
||||||
|
unique ("uid", "name"),
|
||||||
|
foreign key ("uid") references "user_"("uid")
|
||||||
|
);
|
@ -0,0 +1,201 @@
|
|||||||
|
package docspell.store.records
|
||||||
|
|
||||||
|
import doobie._
|
||||||
|
import doobie.implicits._
|
||||||
|
import cats.effect._
|
||||||
|
import cats.implicits._
|
||||||
|
import cats.data.OptionT
|
||||||
|
import docspell.common._
|
||||||
|
import docspell.store.impl.Column
|
||||||
|
import docspell.store.impl.Implicits._
|
||||||
|
import emil.{MailConfig, SSLType}
|
||||||
|
|
||||||
|
case class RUserImap(
|
||||||
|
id: Ident,
|
||||||
|
uid: Ident,
|
||||||
|
name: Ident,
|
||||||
|
imapHost: String,
|
||||||
|
imapPort: Option[Int],
|
||||||
|
imapUser: Option[String],
|
||||||
|
imapPassword: Option[Password],
|
||||||
|
imapSsl: SSLType,
|
||||||
|
imapCertCheck: Boolean,
|
||||||
|
created: Timestamp
|
||||||
|
) {
|
||||||
|
|
||||||
|
def toMailConfig: MailConfig = {
|
||||||
|
val port = imapPort.map(p => s":$p").getOrElse("")
|
||||||
|
MailConfig(
|
||||||
|
s"imap://$imapHost$port",
|
||||||
|
imapUser.getOrElse(""),
|
||||||
|
imapPassword.map(_.pass).getOrElse(""),
|
||||||
|
imapSsl,
|
||||||
|
!imapCertCheck
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object RUserImap {
|
||||||
|
|
||||||
|
def apply[F[_]: Sync](
|
||||||
|
uid: Ident,
|
||||||
|
name: Ident,
|
||||||
|
imapHost: String,
|
||||||
|
imapPort: Option[Int],
|
||||||
|
imapUser: Option[String],
|
||||||
|
imapPassword: Option[Password],
|
||||||
|
imapSsl: SSLType,
|
||||||
|
imapCertCheck: Boolean
|
||||||
|
): F[RUserImap] =
|
||||||
|
for {
|
||||||
|
now <- Timestamp.current[F]
|
||||||
|
id <- Ident.randomId[F]
|
||||||
|
} yield RUserImap(
|
||||||
|
id,
|
||||||
|
uid,
|
||||||
|
name,
|
||||||
|
imapHost,
|
||||||
|
imapPort,
|
||||||
|
imapUser,
|
||||||
|
imapPassword,
|
||||||
|
imapSsl,
|
||||||
|
imapCertCheck,
|
||||||
|
now
|
||||||
|
)
|
||||||
|
|
||||||
|
def fromAccount(
|
||||||
|
accId: AccountId,
|
||||||
|
name: Ident,
|
||||||
|
imapHost: String,
|
||||||
|
imapPort: Option[Int],
|
||||||
|
imapUser: Option[String],
|
||||||
|
imapPassword: Option[Password],
|
||||||
|
imapSsl: SSLType,
|
||||||
|
imapCertCheck: Boolean
|
||||||
|
): OptionT[ConnectionIO, RUserImap] =
|
||||||
|
for {
|
||||||
|
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
||||||
|
id <- OptionT.liftF(Ident.randomId[ConnectionIO])
|
||||||
|
user <- OptionT(RUser.findByAccount(accId))
|
||||||
|
} yield RUserImap(
|
||||||
|
id,
|
||||||
|
user.uid,
|
||||||
|
name,
|
||||||
|
imapHost,
|
||||||
|
imapPort,
|
||||||
|
imapUser,
|
||||||
|
imapPassword,
|
||||||
|
imapSsl,
|
||||||
|
imapCertCheck,
|
||||||
|
now
|
||||||
|
)
|
||||||
|
|
||||||
|
val table = fr"userimap"
|
||||||
|
|
||||||
|
object Columns {
|
||||||
|
val id = Column("id")
|
||||||
|
val uid = Column("uid")
|
||||||
|
val name = Column("name")
|
||||||
|
val imapHost = Column("imap_host")
|
||||||
|
val imapPort = Column("imap_port")
|
||||||
|
val imapUser = Column("imap_user")
|
||||||
|
val imapPass = Column("imap_password")
|
||||||
|
val imapSsl = Column("imap_ssl")
|
||||||
|
val imapCertCheck = Column("imap_certcheck")
|
||||||
|
val created = Column("created")
|
||||||
|
|
||||||
|
val all = List(
|
||||||
|
id,
|
||||||
|
uid,
|
||||||
|
name,
|
||||||
|
imapHost,
|
||||||
|
imapPort,
|
||||||
|
imapUser,
|
||||||
|
imapPass,
|
||||||
|
imapSsl,
|
||||||
|
imapCertCheck,
|
||||||
|
created
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
import Columns._
|
||||||
|
|
||||||
|
def insert(v: RUserImap): ConnectionIO[Int] =
|
||||||
|
insertRow(
|
||||||
|
table,
|
||||||
|
all,
|
||||||
|
sql"${v.id},${v.uid},${v.name},${v.imapHost},${v.imapPort},${v.imapUser},${v.imapPassword},${v.imapSsl},${v.imapCertCheck},${v.created}"
|
||||||
|
).update.run
|
||||||
|
|
||||||
|
def update(eId: Ident, v: RUserImap): ConnectionIO[Int] =
|
||||||
|
updateRow(
|
||||||
|
table,
|
||||||
|
id.is(eId),
|
||||||
|
commas(
|
||||||
|
name.setTo(v.name),
|
||||||
|
imapHost.setTo(v.imapHost),
|
||||||
|
imapPort.setTo(v.imapPort),
|
||||||
|
imapUser.setTo(v.imapUser),
|
||||||
|
imapPass.setTo(v.imapPassword),
|
||||||
|
imapSsl.setTo(v.imapSsl),
|
||||||
|
imapCertCheck.setTo(v.imapCertCheck)
|
||||||
|
)
|
||||||
|
).update.run
|
||||||
|
|
||||||
|
def findByUser(userId: Ident): ConnectionIO[Vector[RUserImap]] =
|
||||||
|
selectSimple(all, table, uid.is(userId)).query[RUserImap].to[Vector]
|
||||||
|
|
||||||
|
private def findByAccount0(
|
||||||
|
accId: AccountId,
|
||||||
|
nameQ: Option[String],
|
||||||
|
exact: Boolean
|
||||||
|
): Query0[RUserImap] = {
|
||||||
|
val mUid = uid.prefix("m")
|
||||||
|
val mName = name.prefix("m")
|
||||||
|
val uId = RUser.Columns.uid.prefix("u")
|
||||||
|
val uColl = RUser.Columns.cid.prefix("u")
|
||||||
|
val uLogin = RUser.Columns.login.prefix("u")
|
||||||
|
val from = table ++ fr"m INNER JOIN" ++ RUser.table ++ fr"u ON" ++ mUid.is(uId)
|
||||||
|
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match {
|
||||||
|
case Some(str) if exact => Seq(mName.is(str))
|
||||||
|
case Some(str) if !exact => Seq(mName.lowerLike(s"%${str.toLowerCase}%"))
|
||||||
|
case None => Seq.empty
|
||||||
|
})
|
||||||
|
|
||||||
|
(selectSimple(all.map(_.prefix("m")), from, and(cond)) ++ orderBy(mName.f))
|
||||||
|
.query[RUserImap]
|
||||||
|
}
|
||||||
|
|
||||||
|
def findByAccount(
|
||||||
|
accId: AccountId,
|
||||||
|
nameQ: Option[String]
|
||||||
|
): ConnectionIO[Vector[RUserImap]] =
|
||||||
|
findByAccount0(accId, nameQ, false).to[Vector]
|
||||||
|
|
||||||
|
def getByName(accId: AccountId, name: Ident): ConnectionIO[Option[RUserImap]] =
|
||||||
|
findByAccount0(accId, Some(name.id), true).option
|
||||||
|
|
||||||
|
def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = {
|
||||||
|
val uId = RUser.Columns.uid
|
||||||
|
val uColl = RUser.Columns.cid
|
||||||
|
val uLogin = RUser.Columns.login
|
||||||
|
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user))
|
||||||
|
|
||||||
|
deleteFrom(
|
||||||
|
table,
|
||||||
|
fr"uid in (" ++ selectSimple(Seq(uId), RUser.table, and(cond)) ++ fr") AND" ++ name
|
||||||
|
.is(
|
||||||
|
connName
|
||||||
|
)
|
||||||
|
).update.run
|
||||||
|
}
|
||||||
|
|
||||||
|
def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] =
|
||||||
|
getByName(accId, name).map(_.isDefined)
|
||||||
|
|
||||||
|
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] =
|
||||||
|
selectCount(id, table, and(uid.is(userId), name.is(connName)))
|
||||||
|
.query[Int]
|
||||||
|
.unique
|
||||||
|
.map(_ > 0)
|
||||||
|
}
|
@ -2,9 +2,11 @@ module Api exposing
|
|||||||
( cancelJob
|
( cancelJob
|
||||||
, changePassword
|
, changePassword
|
||||||
, checkCalEvent
|
, checkCalEvent
|
||||||
|
, createImapSettings
|
||||||
, createMailSettings
|
, createMailSettings
|
||||||
, deleteAttachment
|
, deleteAttachment
|
||||||
, deleteEquip
|
, deleteEquip
|
||||||
|
, deleteImapSettings
|
||||||
, deleteItem
|
, deleteItem
|
||||||
, deleteMailSettings
|
, deleteMailSettings
|
||||||
, deleteOrg
|
, deleteOrg
|
||||||
@ -17,6 +19,7 @@ module Api exposing
|
|||||||
, getCollectiveSettings
|
, getCollectiveSettings
|
||||||
, getContacts
|
, getContacts
|
||||||
, getEquipments
|
, getEquipments
|
||||||
|
, getImapSettings
|
||||||
, getInsights
|
, getInsights
|
||||||
, getItemProposals
|
, getItemProposals
|
||||||
, getJobQueueState
|
, getJobQueueState
|
||||||
@ -81,6 +84,8 @@ import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
|||||||
import Api.Model.Equipment exposing (Equipment)
|
import Api.Model.Equipment exposing (Equipment)
|
||||||
import Api.Model.EquipmentList exposing (EquipmentList)
|
import Api.Model.EquipmentList exposing (EquipmentList)
|
||||||
import Api.Model.GenInvite exposing (GenInvite)
|
import Api.Model.GenInvite exposing (GenInvite)
|
||||||
|
import Api.Model.ImapSettings exposing (ImapSettings)
|
||||||
|
import Api.Model.ImapSettingsList exposing (ImapSettingsList)
|
||||||
import Api.Model.InviteResult exposing (InviteResult)
|
import Api.Model.InviteResult exposing (InviteResult)
|
||||||
import Api.Model.ItemDetail exposing (ItemDetail)
|
import Api.Model.ItemDetail exposing (ItemDetail)
|
||||||
import Api.Model.ItemInsights exposing (ItemInsights)
|
import Api.Model.ItemInsights exposing (ItemInsights)
|
||||||
@ -259,7 +264,16 @@ sendMail flags opts receive =
|
|||||||
deleteMailSettings : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
deleteMailSettings : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||||
deleteMailSettings flags name receive =
|
deleteMailSettings flags name receive =
|
||||||
Http2.authDelete
|
Http2.authDelete
|
||||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/" ++ name
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/smtp/" ++ name
|
||||||
|
, account = getAccount flags
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deleteImapSettings : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||||
|
deleteImapSettings flags name receive =
|
||||||
|
Http2.authDelete
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/imap/" ++ name
|
||||||
, account = getAccount flags
|
, account = getAccount flags
|
||||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
}
|
}
|
||||||
@ -268,12 +282,21 @@ deleteMailSettings flags name receive =
|
|||||||
getMailSettings : Flags -> String -> (Result Http.Error EmailSettingsList -> msg) -> Cmd msg
|
getMailSettings : Flags -> String -> (Result Http.Error EmailSettingsList -> msg) -> Cmd msg
|
||||||
getMailSettings flags query receive =
|
getMailSettings flags query receive =
|
||||||
Http2.authGet
|
Http2.authGet
|
||||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings?q=" ++ Url.percentEncode query
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/smtp?q=" ++ Url.percentEncode query
|
||||||
, account = getAccount flags
|
, account = getAccount flags
|
||||||
, expect = Http.expectJson receive Api.Model.EmailSettingsList.decoder
|
, expect = Http.expectJson receive Api.Model.EmailSettingsList.decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getImapSettings : Flags -> String -> (Result Http.Error ImapSettingsList -> msg) -> Cmd msg
|
||||||
|
getImapSettings flags query receive =
|
||||||
|
Http2.authGet
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/imap?q=" ++ Url.percentEncode query
|
||||||
|
, account = getAccount flags
|
||||||
|
, expect = Http.expectJson receive Api.Model.ImapSettingsList.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
createMailSettings :
|
createMailSettings :
|
||||||
Flags
|
Flags
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
@ -284,7 +307,7 @@ createMailSettings flags mname ems receive =
|
|||||||
case mname of
|
case mname of
|
||||||
Just en ->
|
Just en ->
|
||||||
Http2.authPut
|
Http2.authPut
|
||||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/" ++ en
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/smtp/" ++ en
|
||||||
, account = getAccount flags
|
, account = getAccount flags
|
||||||
, body = Http.jsonBody (Api.Model.EmailSettings.encode ems)
|
, body = Http.jsonBody (Api.Model.EmailSettings.encode ems)
|
||||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
@ -292,13 +315,38 @@ createMailSettings flags mname ems receive =
|
|||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
Http2.authPost
|
Http2.authPost
|
||||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings"
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/smtp"
|
||||||
, account = getAccount flags
|
, account = getAccount flags
|
||||||
, body = Http.jsonBody (Api.Model.EmailSettings.encode ems)
|
, body = Http.jsonBody (Api.Model.EmailSettings.encode ems)
|
||||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createImapSettings :
|
||||||
|
Flags
|
||||||
|
-> Maybe String
|
||||||
|
-> ImapSettings
|
||||||
|
-> (Result Http.Error BasicResult -> msg)
|
||||||
|
-> Cmd msg
|
||||||
|
createImapSettings flags mname ems receive =
|
||||||
|
case mname of
|
||||||
|
Just en ->
|
||||||
|
Http2.authPut
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/imap/" ++ en
|
||||||
|
, account = getAccount flags
|
||||||
|
, body = Http.jsonBody (Api.Model.ImapSettings.encode ems)
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
Http2.authPost
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/imap"
|
||||||
|
, account = getAccount flags
|
||||||
|
, body = Http.jsonBody (Api.Model.ImapSettings.encode ems)
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Upload
|
--- Upload
|
||||||
|
|
||||||
|
226
modules/webapp/src/main/elm/Comp/ImapSettingsForm.elm
Normal file
226
modules/webapp/src/main/elm/Comp/ImapSettingsForm.elm
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
module Comp.ImapSettingsForm exposing
|
||||||
|
( Model
|
||||||
|
, Msg
|
||||||
|
, emptyModel
|
||||||
|
, getSettings
|
||||||
|
, init
|
||||||
|
, isValid
|
||||||
|
, update
|
||||||
|
, view
|
||||||
|
)
|
||||||
|
|
||||||
|
import Api.Model.ImapSettings exposing (ImapSettings)
|
||||||
|
import Comp.Dropdown
|
||||||
|
import Comp.IntField
|
||||||
|
import Comp.PasswordInput
|
||||||
|
import Data.SSLType exposing (SSLType)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (onCheck, onInput)
|
||||||
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ settings : ImapSettings
|
||||||
|
, name : String
|
||||||
|
, host : String
|
||||||
|
, portField : Comp.IntField.Model
|
||||||
|
, portNum : Maybe Int
|
||||||
|
, user : Maybe String
|
||||||
|
, passField : Comp.PasswordInput.Model
|
||||||
|
, password : Maybe String
|
||||||
|
, sslType : Comp.Dropdown.Model SSLType
|
||||||
|
, ignoreCertificates : Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
{ settings = Api.Model.ImapSettings.empty
|
||||||
|
, name = ""
|
||||||
|
, host = ""
|
||||||
|
, portField = Comp.IntField.init (Just 0) Nothing True "IMAP Port"
|
||||||
|
, portNum = Nothing
|
||||||
|
, user = Nothing
|
||||||
|
, passField = Comp.PasswordInput.init
|
||||||
|
, password = Nothing
|
||||||
|
, sslType =
|
||||||
|
Comp.Dropdown.makeSingleList
|
||||||
|
{ makeOption = \s -> { value = Data.SSLType.toString s, text = Data.SSLType.label s }
|
||||||
|
, placeholder = ""
|
||||||
|
, options = Data.SSLType.all
|
||||||
|
, selected = Just Data.SSLType.None
|
||||||
|
}
|
||||||
|
, ignoreCertificates = False
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : ImapSettings -> Model
|
||||||
|
init ems =
|
||||||
|
{ settings = ems
|
||||||
|
, name = ems.name
|
||||||
|
, host = ems.imapHost
|
||||||
|
, portField = Comp.IntField.init (Just 0) Nothing True "IMAP Port"
|
||||||
|
, portNum = ems.imapPort
|
||||||
|
, user = ems.imapUser
|
||||||
|
, passField = Comp.PasswordInput.init
|
||||||
|
, password = ems.imapPassword
|
||||||
|
, sslType =
|
||||||
|
Comp.Dropdown.makeSingleList
|
||||||
|
{ makeOption = \s -> { value = Data.SSLType.toString s, text = Data.SSLType.label s }
|
||||||
|
, placeholder = ""
|
||||||
|
, options = Data.SSLType.all
|
||||||
|
, selected =
|
||||||
|
Data.SSLType.fromString ems.sslType
|
||||||
|
|> Maybe.withDefault Data.SSLType.None
|
||||||
|
|> Just
|
||||||
|
}
|
||||||
|
, ignoreCertificates = ems.ignoreCertificates
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getSettings : Model -> ( Maybe String, ImapSettings )
|
||||||
|
getSettings model =
|
||||||
|
( Util.Maybe.fromString model.settings.name
|
||||||
|
, { name = model.name
|
||||||
|
, imapHost = model.host
|
||||||
|
, imapUser = model.user
|
||||||
|
, imapPort = model.portNum
|
||||||
|
, imapPassword = model.password
|
||||||
|
, sslType =
|
||||||
|
Comp.Dropdown.getSelected model.sslType
|
||||||
|
|> List.head
|
||||||
|
|> Maybe.withDefault Data.SSLType.None
|
||||||
|
|> Data.SSLType.toString
|
||||||
|
, ignoreCertificates = model.ignoreCertificates
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= SetName String
|
||||||
|
| SetHost String
|
||||||
|
| PortMsg Comp.IntField.Msg
|
||||||
|
| SetUser String
|
||||||
|
| PassMsg Comp.PasswordInput.Msg
|
||||||
|
| SSLTypeMsg (Comp.Dropdown.Msg SSLType)
|
||||||
|
| ToggleCheckCert
|
||||||
|
|
||||||
|
|
||||||
|
isValid : Model -> Bool
|
||||||
|
isValid model =
|
||||||
|
model.host /= "" && model.name /= ""
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
SetName str ->
|
||||||
|
( { model | name = str }, Cmd.none )
|
||||||
|
|
||||||
|
SetHost str ->
|
||||||
|
( { model | host = str }, Cmd.none )
|
||||||
|
|
||||||
|
PortMsg m ->
|
||||||
|
let
|
||||||
|
( pm, val ) =
|
||||||
|
Comp.IntField.update m model.portField
|
||||||
|
in
|
||||||
|
( { model | portField = pm, portNum = val }, Cmd.none )
|
||||||
|
|
||||||
|
SetUser str ->
|
||||||
|
( { model | user = Util.Maybe.fromString str }, Cmd.none )
|
||||||
|
|
||||||
|
PassMsg m ->
|
||||||
|
let
|
||||||
|
( pm, val ) =
|
||||||
|
Comp.PasswordInput.update m model.passField
|
||||||
|
in
|
||||||
|
( { model | passField = pm, password = val }, Cmd.none )
|
||||||
|
|
||||||
|
SSLTypeMsg m ->
|
||||||
|
let
|
||||||
|
( sm, sc ) =
|
||||||
|
Comp.Dropdown.update m model.sslType
|
||||||
|
in
|
||||||
|
( { model | sslType = sm }, Cmd.map SSLTypeMsg sc )
|
||||||
|
|
||||||
|
ToggleCheckCert ->
|
||||||
|
( { model | ignoreCertificates = not model.ignoreCertificates }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view model =
|
||||||
|
div
|
||||||
|
[ classList
|
||||||
|
[ ( "ui form", True )
|
||||||
|
, ( "error", not (isValid model) )
|
||||||
|
, ( "success", isValid model )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ div [ class "required field" ]
|
||||||
|
[ label [] [ text "Name" ]
|
||||||
|
, input
|
||||||
|
[ type_ "text"
|
||||||
|
, value model.name
|
||||||
|
, onInput SetName
|
||||||
|
, placeholder "Connection name, e.g. 'gmail.com'"
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, div [ class "ui info message" ]
|
||||||
|
[ text "The connection name must not contain whitespace or special characters."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "fields" ]
|
||||||
|
[ div [ class "thirteen wide required field" ]
|
||||||
|
[ label [] [ text "IMAP Host" ]
|
||||||
|
, input
|
||||||
|
[ type_ "text"
|
||||||
|
, placeholder "IMAP host name, e.g. 'mail.gmail.com'"
|
||||||
|
, value model.host
|
||||||
|
, onInput SetHost
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, Html.map PortMsg
|
||||||
|
(Comp.IntField.view model.portNum
|
||||||
|
"three wide field"
|
||||||
|
model.portField
|
||||||
|
)
|
||||||
|
]
|
||||||
|
, div [ class "two fields" ]
|
||||||
|
[ div [ class "field" ]
|
||||||
|
[ label [] [ text "IMAP User" ]
|
||||||
|
, input
|
||||||
|
[ type_ "text"
|
||||||
|
, placeholder "IMAP Username, e.g. 'your.name@gmail.com'"
|
||||||
|
, Maybe.withDefault "" model.user |> value
|
||||||
|
, onInput SetUser
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, div [ class "field" ]
|
||||||
|
[ label [] [ text "IMAP Password" ]
|
||||||
|
, Html.map PassMsg (Comp.PasswordInput.view model.password model.passField)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "two fields" ]
|
||||||
|
[ div [ class "inline field" ]
|
||||||
|
[ div [ class "ui checkbox" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "checkbox"
|
||||||
|
, checked model.ignoreCertificates
|
||||||
|
, onCheck (\_ -> ToggleCheckCert)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label [] [ text "Ignore certificate check" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "two fields" ]
|
||||||
|
[ div [ class "field" ]
|
||||||
|
[ label [] [ text "SSL" ]
|
||||||
|
, Html.map SSLTypeMsg (Comp.Dropdown.view model.sslType)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
288
modules/webapp/src/main/elm/Comp/ImapSettingsManage.elm
Normal file
288
modules/webapp/src/main/elm/Comp/ImapSettingsManage.elm
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
module Comp.ImapSettingsManage exposing
|
||||||
|
( Model
|
||||||
|
, Msg
|
||||||
|
, emptyModel
|
||||||
|
, init
|
||||||
|
, update
|
||||||
|
, view
|
||||||
|
)
|
||||||
|
|
||||||
|
import Api
|
||||||
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
|
import Api.Model.ImapSettings
|
||||||
|
import Api.Model.ImapSettingsList exposing (ImapSettingsList)
|
||||||
|
import Comp.ImapSettingsForm
|
||||||
|
import Comp.ImapSettingsTable
|
||||||
|
import Comp.YesNoDimmer
|
||||||
|
import Data.Flags exposing (Flags)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (onClick, onInput)
|
||||||
|
import Http
|
||||||
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ tableModel : Comp.ImapSettingsTable.Model
|
||||||
|
, formModel : Comp.ImapSettingsForm.Model
|
||||||
|
, viewMode : ViewMode
|
||||||
|
, formError : Maybe String
|
||||||
|
, loading : Bool
|
||||||
|
, query : String
|
||||||
|
, deleteConfirm : Comp.YesNoDimmer.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
{ tableModel = Comp.ImapSettingsTable.emptyModel
|
||||||
|
, formModel = Comp.ImapSettingsForm.emptyModel
|
||||||
|
, viewMode = Table
|
||||||
|
, formError = Nothing
|
||||||
|
, loading = False
|
||||||
|
, query = ""
|
||||||
|
, deleteConfirm = Comp.YesNoDimmer.emptyModel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Flags -> ( Model, Cmd Msg )
|
||||||
|
init flags =
|
||||||
|
( emptyModel, Api.getImapSettings flags "" MailSettingsResp )
|
||||||
|
|
||||||
|
|
||||||
|
type ViewMode
|
||||||
|
= Table
|
||||||
|
| Form
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= TableMsg Comp.ImapSettingsTable.Msg
|
||||||
|
| FormMsg Comp.ImapSettingsForm.Msg
|
||||||
|
| SetQuery String
|
||||||
|
| InitNew
|
||||||
|
| YesNoMsg Comp.YesNoDimmer.Msg
|
||||||
|
| RequestDelete
|
||||||
|
| SetViewMode ViewMode
|
||||||
|
| Submit
|
||||||
|
| SubmitResp (Result Http.Error BasicResult)
|
||||||
|
| LoadSettings
|
||||||
|
| MailSettingsResp (Result Http.Error ImapSettingsList)
|
||||||
|
|
||||||
|
|
||||||
|
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update flags msg model =
|
||||||
|
case msg of
|
||||||
|
InitNew ->
|
||||||
|
let
|
||||||
|
ems =
|
||||||
|
Api.Model.ImapSettings.empty
|
||||||
|
|
||||||
|
nm =
|
||||||
|
{ model
|
||||||
|
| viewMode = Form
|
||||||
|
, formError = Nothing
|
||||||
|
, formModel = Comp.ImapSettingsForm.init ems
|
||||||
|
}
|
||||||
|
in
|
||||||
|
( nm, Cmd.none )
|
||||||
|
|
||||||
|
TableMsg m ->
|
||||||
|
let
|
||||||
|
( tm, tc ) =
|
||||||
|
Comp.ImapSettingsTable.update m model.tableModel
|
||||||
|
|
||||||
|
m2 =
|
||||||
|
{ model
|
||||||
|
| tableModel = tm
|
||||||
|
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
|
||||||
|
, formError =
|
||||||
|
if tm.selected /= Nothing then
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
else
|
||||||
|
model.formError
|
||||||
|
, formModel =
|
||||||
|
case tm.selected of
|
||||||
|
Just ems ->
|
||||||
|
Comp.ImapSettingsForm.init ems
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
model.formModel
|
||||||
|
}
|
||||||
|
in
|
||||||
|
( m2, Cmd.map TableMsg tc )
|
||||||
|
|
||||||
|
FormMsg m ->
|
||||||
|
let
|
||||||
|
( fm, fc ) =
|
||||||
|
Comp.ImapSettingsForm.update m model.formModel
|
||||||
|
in
|
||||||
|
( { model | formModel = fm }, Cmd.map FormMsg fc )
|
||||||
|
|
||||||
|
SetQuery str ->
|
||||||
|
let
|
||||||
|
m =
|
||||||
|
{ model | query = str }
|
||||||
|
in
|
||||||
|
( m, Api.getImapSettings flags str MailSettingsResp )
|
||||||
|
|
||||||
|
YesNoMsg m ->
|
||||||
|
let
|
||||||
|
( dm, flag ) =
|
||||||
|
Comp.YesNoDimmer.update m model.deleteConfirm
|
||||||
|
|
||||||
|
( mid, _ ) =
|
||||||
|
Comp.ImapSettingsForm.getSettings model.formModel
|
||||||
|
|
||||||
|
cmd =
|
||||||
|
case ( flag, mid ) of
|
||||||
|
( True, Just name ) ->
|
||||||
|
Api.deleteImapSettings flags name SubmitResp
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Cmd.none
|
||||||
|
in
|
||||||
|
( { model | deleteConfirm = dm }, cmd )
|
||||||
|
|
||||||
|
RequestDelete ->
|
||||||
|
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
|
||||||
|
|
||||||
|
SetViewMode m ->
|
||||||
|
( { model | viewMode = m }, Cmd.none )
|
||||||
|
|
||||||
|
Submit ->
|
||||||
|
let
|
||||||
|
( mid, ems ) =
|
||||||
|
Comp.ImapSettingsForm.getSettings model.formModel
|
||||||
|
|
||||||
|
valid =
|
||||||
|
Comp.ImapSettingsForm.isValid model.formModel
|
||||||
|
in
|
||||||
|
if valid then
|
||||||
|
( { model | loading = True }, Api.createImapSettings flags mid ems SubmitResp )
|
||||||
|
|
||||||
|
else
|
||||||
|
( { model | formError = Just "Please fill required fields." }, Cmd.none )
|
||||||
|
|
||||||
|
LoadSettings ->
|
||||||
|
( { model | loading = True }, Api.getImapSettings flags model.query MailSettingsResp )
|
||||||
|
|
||||||
|
SubmitResp (Ok res) ->
|
||||||
|
if res.success then
|
||||||
|
let
|
||||||
|
( m2, c2 ) =
|
||||||
|
update flags (SetViewMode Table) model
|
||||||
|
|
||||||
|
( m3, c3 ) =
|
||||||
|
update flags LoadSettings m2
|
||||||
|
in
|
||||||
|
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
|
||||||
|
|
||||||
|
else
|
||||||
|
( { model | formError = Just res.message, loading = False }, Cmd.none )
|
||||||
|
|
||||||
|
SubmitResp (Err err) ->
|
||||||
|
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
|
||||||
|
|
||||||
|
MailSettingsResp (Ok ems) ->
|
||||||
|
let
|
||||||
|
m2 =
|
||||||
|
{ model
|
||||||
|
| viewMode = Table
|
||||||
|
, loading = False
|
||||||
|
, tableModel = Comp.ImapSettingsTable.init ems.items
|
||||||
|
}
|
||||||
|
in
|
||||||
|
( m2, Cmd.none )
|
||||||
|
|
||||||
|
MailSettingsResp (Err _) ->
|
||||||
|
( { model | loading = False }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view model =
|
||||||
|
case model.viewMode of
|
||||||
|
Table ->
|
||||||
|
viewTable model
|
||||||
|
|
||||||
|
Form ->
|
||||||
|
viewForm model
|
||||||
|
|
||||||
|
|
||||||
|
viewTable : Model -> Html Msg
|
||||||
|
viewTable model =
|
||||||
|
div []
|
||||||
|
[ div [ class "ui secondary menu" ]
|
||||||
|
[ div [ class "horizontally fitted item" ]
|
||||||
|
[ div [ class "ui icon input" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "text"
|
||||||
|
, onInput SetQuery
|
||||||
|
, value model.query
|
||||||
|
, placeholder "Search…"
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, i [ class "ui search icon" ]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "right menu" ]
|
||||||
|
[ div [ class "item" ]
|
||||||
|
[ a
|
||||||
|
[ class "ui primary button"
|
||||||
|
, href "#"
|
||||||
|
, onClick InitNew
|
||||||
|
]
|
||||||
|
[ i [ class "plus icon" ] []
|
||||||
|
, text "New Settings"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, Html.map TableMsg (Comp.ImapSettingsTable.view model.tableModel)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewForm : Model -> Html Msg
|
||||||
|
viewForm model =
|
||||||
|
div [ class "ui segment" ]
|
||||||
|
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
|
||||||
|
, Html.map FormMsg (Comp.ImapSettingsForm.view model.formModel)
|
||||||
|
, div
|
||||||
|
[ classList
|
||||||
|
[ ( "ui error message", True )
|
||||||
|
, ( "invisible", model.formError == Nothing )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ Maybe.withDefault "" model.formError |> text
|
||||||
|
]
|
||||||
|
, div [ class "ui divider" ] []
|
||||||
|
, button
|
||||||
|
[ class "ui primary button"
|
||||||
|
, onClick Submit
|
||||||
|
, href "#"
|
||||||
|
]
|
||||||
|
[ text "Submit"
|
||||||
|
]
|
||||||
|
, a
|
||||||
|
[ class "ui secondary button"
|
||||||
|
, onClick (SetViewMode Table)
|
||||||
|
, href ""
|
||||||
|
]
|
||||||
|
[ text "Cancel"
|
||||||
|
]
|
||||||
|
, if model.formModel.settings.name /= "" then
|
||||||
|
a [ class "ui right floated red button", href "", onClick RequestDelete ]
|
||||||
|
[ text "Delete" ]
|
||||||
|
|
||||||
|
else
|
||||||
|
span [] []
|
||||||
|
, div
|
||||||
|
[ classList
|
||||||
|
[ ( "ui dimmer", True )
|
||||||
|
, ( "active", model.loading )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ div [ class "ui loader" ] []
|
||||||
|
]
|
||||||
|
]
|
74
modules/webapp/src/main/elm/Comp/ImapSettingsTable.elm
Normal file
74
modules/webapp/src/main/elm/Comp/ImapSettingsTable.elm
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
module Comp.ImapSettingsTable exposing
|
||||||
|
( Model
|
||||||
|
, Msg
|
||||||
|
, emptyModel
|
||||||
|
, init
|
||||||
|
, update
|
||||||
|
, view
|
||||||
|
)
|
||||||
|
|
||||||
|
import Api.Model.ImapSettings exposing (ImapSettings)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (onClick)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ emailSettings : List ImapSettings
|
||||||
|
, selected : Maybe ImapSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
init []
|
||||||
|
|
||||||
|
|
||||||
|
init : List ImapSettings -> Model
|
||||||
|
init ems =
|
||||||
|
{ emailSettings = ems
|
||||||
|
, selected = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= Select ImapSettings
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
Select ems ->
|
||||||
|
( { model | selected = Just ems }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view model =
|
||||||
|
table [ class "ui selectable pointer table" ]
|
||||||
|
[ thead []
|
||||||
|
[ th [ class "collapsible" ] [ text "Name" ]
|
||||||
|
, th [] [ text "Host/Port" ]
|
||||||
|
]
|
||||||
|
, tbody []
|
||||||
|
(List.map (renderLine model) model.emailSettings)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
renderLine : Model -> ImapSettings -> Html Msg
|
||||||
|
renderLine model ems =
|
||||||
|
let
|
||||||
|
hostport =
|
||||||
|
case ems.imapPort of
|
||||||
|
Just p ->
|
||||||
|
ems.imapHost ++ ":" ++ String.fromInt p
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
ems.imapHost
|
||||||
|
in
|
||||||
|
tr
|
||||||
|
[ classList [ ( "active", model.selected == Just ems ) ]
|
||||||
|
, onClick (Select ems)
|
||||||
|
]
|
||||||
|
[ td [ class "collapsible" ] [ text ems.name ]
|
||||||
|
, td [] [ text hostport ]
|
||||||
|
]
|
@ -7,6 +7,7 @@ module Page.UserSettings.Data exposing
|
|||||||
|
|
||||||
import Comp.ChangePasswordForm
|
import Comp.ChangePasswordForm
|
||||||
import Comp.EmailSettingsManage
|
import Comp.EmailSettingsManage
|
||||||
|
import Comp.ImapSettingsManage
|
||||||
import Comp.NotificationForm
|
import Comp.NotificationForm
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ type alias Model =
|
|||||||
{ currentTab : Maybe Tab
|
{ currentTab : Maybe Tab
|
||||||
, changePassModel : Comp.ChangePasswordForm.Model
|
, changePassModel : Comp.ChangePasswordForm.Model
|
||||||
, emailSettingsModel : Comp.EmailSettingsManage.Model
|
, emailSettingsModel : Comp.EmailSettingsManage.Model
|
||||||
|
, imapSettingsModel : Comp.ImapSettingsManage.Model
|
||||||
, notificationModel : Comp.NotificationForm.Model
|
, notificationModel : Comp.NotificationForm.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ emptyModel flags =
|
|||||||
{ currentTab = Nothing
|
{ currentTab = Nothing
|
||||||
, changePassModel = Comp.ChangePasswordForm.emptyModel
|
, changePassModel = Comp.ChangePasswordForm.emptyModel
|
||||||
, emailSettingsModel = Comp.EmailSettingsManage.emptyModel
|
, emailSettingsModel = Comp.EmailSettingsManage.emptyModel
|
||||||
|
, imapSettingsModel = Comp.ImapSettingsManage.emptyModel
|
||||||
, notificationModel = Tuple.first (Comp.NotificationForm.init flags)
|
, notificationModel = Tuple.first (Comp.NotificationForm.init flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +34,7 @@ emptyModel flags =
|
|||||||
type Tab
|
type Tab
|
||||||
= ChangePassTab
|
= ChangePassTab
|
||||||
| EmailSettingsTab
|
| EmailSettingsTab
|
||||||
|
| ImapSettingsTab
|
||||||
| NotificationTab
|
| NotificationTab
|
||||||
|
|
||||||
|
|
||||||
@ -39,3 +43,4 @@ type Msg
|
|||||||
| ChangePassMsg Comp.ChangePasswordForm.Msg
|
| ChangePassMsg Comp.ChangePasswordForm.Msg
|
||||||
| EmailSettingsMsg Comp.EmailSettingsManage.Msg
|
| EmailSettingsMsg Comp.EmailSettingsManage.Msg
|
||||||
| NotificationMsg Comp.NotificationForm.Msg
|
| NotificationMsg Comp.NotificationForm.Msg
|
||||||
|
| ImapSettingsMsg Comp.ImapSettingsManage.Msg
|
||||||
|
@ -2,6 +2,7 @@ module Page.UserSettings.Update exposing (update)
|
|||||||
|
|
||||||
import Comp.ChangePasswordForm
|
import Comp.ChangePasswordForm
|
||||||
import Comp.EmailSettingsManage
|
import Comp.EmailSettingsManage
|
||||||
|
import Comp.ImapSettingsManage
|
||||||
import Comp.NotificationForm
|
import Comp.NotificationForm
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Page.UserSettings.Data exposing (..)
|
import Page.UserSettings.Data exposing (..)
|
||||||
@ -24,6 +25,13 @@ update flags msg model =
|
|||||||
in
|
in
|
||||||
( { m | emailSettingsModel = em }, Cmd.map EmailSettingsMsg c )
|
( { m | emailSettingsModel = em }, Cmd.map EmailSettingsMsg c )
|
||||||
|
|
||||||
|
ImapSettingsTab ->
|
||||||
|
let
|
||||||
|
( em, c ) =
|
||||||
|
Comp.ImapSettingsManage.init flags
|
||||||
|
in
|
||||||
|
( { m | imapSettingsModel = em }, Cmd.map ImapSettingsMsg c )
|
||||||
|
|
||||||
ChangePassTab ->
|
ChangePassTab ->
|
||||||
( m, Cmd.none )
|
( m, Cmd.none )
|
||||||
|
|
||||||
@ -51,6 +59,13 @@ update flags msg model =
|
|||||||
in
|
in
|
||||||
( { model | emailSettingsModel = m2 }, Cmd.map EmailSettingsMsg c2 )
|
( { model | emailSettingsModel = m2 }, Cmd.map EmailSettingsMsg c2 )
|
||||||
|
|
||||||
|
ImapSettingsMsg m ->
|
||||||
|
let
|
||||||
|
( m2, c2 ) =
|
||||||
|
Comp.ImapSettingsManage.update flags m model.imapSettingsModel
|
||||||
|
in
|
||||||
|
( { model | imapSettingsModel = m2 }, Cmd.map ImapSettingsMsg c2 )
|
||||||
|
|
||||||
NotificationMsg lm ->
|
NotificationMsg lm ->
|
||||||
let
|
let
|
||||||
( m2, c2 ) =
|
( m2, c2 ) =
|
||||||
|
@ -2,6 +2,7 @@ module Page.UserSettings.View exposing (view)
|
|||||||
|
|
||||||
import Comp.ChangePasswordForm
|
import Comp.ChangePasswordForm
|
||||||
import Comp.EmailSettingsManage
|
import Comp.EmailSettingsManage
|
||||||
|
import Comp.ImapSettingsManage
|
||||||
import Comp.NotificationForm
|
import Comp.NotificationForm
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@ -20,7 +21,8 @@ view model =
|
|||||||
, div [ class "ui attached fluid segment" ]
|
, div [ class "ui attached fluid segment" ]
|
||||||
[ div [ class "ui fluid vertical secondary menu" ]
|
[ div [ class "ui fluid vertical secondary menu" ]
|
||||||
[ makeTab model ChangePassTab "Change Password" "user secret icon"
|
[ makeTab model ChangePassTab "Change Password" "user secret icon"
|
||||||
, makeTab model EmailSettingsTab "E-Mail Settings" "mail icon"
|
, makeTab model EmailSettingsTab "E-Mail Settings (SMTP)" "mail icon"
|
||||||
|
, makeTab model ImapSettingsTab "E-Mail Settings (IMAP)" "mail icon"
|
||||||
, makeTab model NotificationTab "Notifications" "bullhorn icon"
|
, makeTab model NotificationTab "Notifications" "bullhorn icon"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -37,6 +39,9 @@ view model =
|
|||||||
Just NotificationTab ->
|
Just NotificationTab ->
|
||||||
viewNotificationForm model
|
viewNotificationForm model
|
||||||
|
|
||||||
|
Just ImapSettingsTab ->
|
||||||
|
viewImapSettings model
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
@ -61,13 +66,25 @@ viewEmailSettings model =
|
|||||||
[ h2 [ class "ui header" ]
|
[ h2 [ class "ui header" ]
|
||||||
[ i [ class "mail icon" ] []
|
[ i [ class "mail icon" ] []
|
||||||
, div [ class "content" ]
|
, div [ class "content" ]
|
||||||
[ text "E-Mail Settings"
|
[ text "E-Mail Settings (Smtp)"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, Html.map EmailSettingsMsg (Comp.EmailSettingsManage.view model.emailSettingsModel)
|
, Html.map EmailSettingsMsg (Comp.EmailSettingsManage.view model.emailSettingsModel)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewImapSettings : Model -> List (Html Msg)
|
||||||
|
viewImapSettings model =
|
||||||
|
[ h2 [ class "ui header" ]
|
||||||
|
[ i [ class "mail icon" ] []
|
||||||
|
, div [ class "content" ]
|
||||||
|
[ text "E-Mail Settings (Imap)"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, Html.map ImapSettingsMsg (Comp.ImapSettingsManage.view model.imapSettingsModel)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
viewChangePassword : Model -> List (Html Msg)
|
viewChangePassword : Model -> List (Html Msg)
|
||||||
viewChangePassword model =
|
viewChangePassword model =
|
||||||
[ h2 [ class "ui header" ]
|
[ h2 [ class "ui header" ]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user