mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-28 09:45:07 +00:00
Finish mail settings
This commit is contained in:
parent
f235f3a030
commit
32050a9faf
@ -3,6 +3,8 @@ package docspell.backend.ops
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
import cats.data.OptionT
|
||||
import emil.{MailAddress, SSLType}
|
||||
|
||||
import docspell.common._
|
||||
import docspell.store._
|
||||
import docspell.store.records.RUserEmail
|
||||
@ -11,33 +13,71 @@ trait OMail[F[_]] {
|
||||
|
||||
def getSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserEmail]]
|
||||
|
||||
def createSettings(data: F[RUserEmail]): F[AddResult]
|
||||
def findSettings(accId: AccountId, name: Ident): OptionT[F, RUserEmail]
|
||||
|
||||
def updateSettings(accId: AccountId, name: Ident, data: RUserEmail): F[Int]
|
||||
def createSettings(accId: AccountId, data: OMail.SmtpSettings): F[AddResult]
|
||||
|
||||
def updateSettings(accId: AccountId, name: Ident, data: OMail.SmtpSettings): F[Int]
|
||||
|
||||
def deleteSettings(accId: AccountId, name: Ident): F[Int]
|
||||
}
|
||||
|
||||
object OMail {
|
||||
|
||||
case class SmtpSettings(
|
||||
name: Ident,
|
||||
smtpHost: String,
|
||||
smtpPort: Option[Int],
|
||||
smtpUser: Option[String],
|
||||
smtpPassword: Option[Password],
|
||||
smtpSsl: SSLType,
|
||||
smtpCertCheck: Boolean,
|
||||
mailFrom: MailAddress,
|
||||
mailReplyTo: Option[MailAddress]
|
||||
) {
|
||||
|
||||
def toRecord(accId: AccountId) =
|
||||
RUserEmail.fromAccount(
|
||||
accId,
|
||||
name,
|
||||
smtpHost,
|
||||
smtpPort,
|
||||
smtpUser,
|
||||
smtpPassword,
|
||||
smtpSsl,
|
||||
smtpCertCheck,
|
||||
mailFrom,
|
||||
mailReplyTo
|
||||
)
|
||||
}
|
||||
|
||||
def apply[F[_]: Effect](store: Store[F]): Resource[F, OMail[F]] =
|
||||
Resource.pure(new OMail[F] {
|
||||
def getSettings(accId: AccountId, nameQ: Option[String]): F[Vector[RUserEmail]] =
|
||||
store.transact(RUserEmail.findByAccount(accId, nameQ))
|
||||
|
||||
def createSettings(data: F[RUserEmail]): F[AddResult] =
|
||||
for {
|
||||
ru <- data
|
||||
ins = RUserEmail.insert(ru)
|
||||
exists = RUserEmail.exists(ru.uid, ru.name)
|
||||
ar <- store.add(ins, exists)
|
||||
} yield ar
|
||||
def findSettings(accId: AccountId, name: Ident): OptionT[F, RUserEmail] =
|
||||
OptionT(store.transact(RUserEmail.getByName(accId, name)))
|
||||
|
||||
def updateSettings(accId: AccountId, name: Ident, data: RUserEmail): F[Int] = {
|
||||
def createSettings(accId: AccountId, s: SmtpSettings): F[AddResult] =
|
||||
(for {
|
||||
ru <- OptionT(store.transact(s.toRecord(accId).value))
|
||||
ins = RUserEmail.insert(ru)
|
||||
exists = RUserEmail.exists(ru.uid, ru.name)
|
||||
res <- OptionT.liftF(store.add(ins, exists))
|
||||
} yield res).getOrElse(AddResult.Failure(new Exception("User not found")))
|
||||
|
||||
def updateSettings(accId: AccountId, name: Ident, data: SmtpSettings): F[Int] = {
|
||||
val op = for {
|
||||
um <- OptionT(RUserEmail.getByName(accId, name))
|
||||
n <- OptionT.liftF(RUserEmail.update(um.id, data))
|
||||
ru <- data.toRecord(accId)
|
||||
n <- OptionT.liftF(RUserEmail.update(um.id, ru))
|
||||
} yield n
|
||||
|
||||
store.transact(op.value).map(_.getOrElse(0))
|
||||
}
|
||||
|
||||
def deleteSettings(accId: AccountId, name: Ident): F[Int] =
|
||||
store.transact(RUserEmail.delete(accId, name))
|
||||
})
|
||||
}
|
||||
|
@ -2,17 +2,21 @@ package docspell.restserver.routes
|
||||
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
import cats.data.OptionT
|
||||
import org.http4s._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.circe.CirceEntityDecoder._
|
||||
import emil.MailAddress
|
||||
|
||||
import docspell.backend.BackendApp
|
||||
import docspell.backend.auth.AuthToken
|
||||
import docspell.backend.ops.OMail
|
||||
import docspell.common._
|
||||
import docspell.restapi.model._
|
||||
import docspell.store.records.RUserEmail
|
||||
import docspell.store.EmilUtil
|
||||
import docspell.restserver.conv.Conversions
|
||||
|
||||
object MailSettingsRoutes {
|
||||
|
||||
@ -29,10 +33,51 @@ object MailSettingsRoutes {
|
||||
resp <- Ok(EmailSettingsList(res.toList))
|
||||
} yield resp
|
||||
|
||||
case GET -> Root / Ident(name) =>
|
||||
(for {
|
||||
ems <- backend.mail.findSettings(user.account, name)
|
||||
resp <- OptionT.liftF(Ok(convert(ems)))
|
||||
} yield resp).getOrElseF(NotFound())
|
||||
|
||||
case req @ POST -> Root =>
|
||||
(for {
|
||||
in <- OptionT.liftF(req.as[EmailSettings])
|
||||
ru = makeSettings(in)
|
||||
up <- OptionT.liftF(ru.traverse(r => backend.mail.createSettings(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 / Ident(name) =>
|
||||
(for {
|
||||
in <- OptionT.liftF(req.as[EmailSettings])
|
||||
ru = makeSettings(in)
|
||||
up <- OptionT.liftF(ru.traverse(r => backend.mail.updateSettings(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 / Ident(name) =>
|
||||
for {
|
||||
in <- req.as[EmailSettings]
|
||||
resp <- Ok(BasicResult(false, "not implemented"))
|
||||
n <- backend.mail.deleteSettings(user.account, name)
|
||||
resp <- Ok(
|
||||
if (n > 0) BasicResult(true, "Mail settings removed")
|
||||
else BasicResult(false, "Mail settings could not be removed")
|
||||
)
|
||||
} yield resp
|
||||
}
|
||||
|
||||
@ -50,4 +95,29 @@ object MailSettingsRoutes {
|
||||
EmilUtil.sslTypeString(ru.smtpSsl),
|
||||
!ru.smtpCertCheck
|
||||
)
|
||||
|
||||
def makeSettings(ems: EmailSettings): Either[String, OMail.SmtpSettings] = {
|
||||
def readMail(str: String): Either[String, MailAddress] =
|
||||
EmilUtil.readMailAddress(str).left.map(err => s"E-Mail address '$str' invalid: $err")
|
||||
|
||||
def readMailOpt(str: Option[String]): Either[String, Option[MailAddress]] =
|
||||
str.traverse(readMail)
|
||||
|
||||
for {
|
||||
from <- readMail(ems.from)
|
||||
repl <- readMailOpt(ems.replyTo)
|
||||
sslt <- EmilUtil.readSSLType(ems.sslType)
|
||||
} yield OMail.SmtpSettings(
|
||||
ems.name,
|
||||
ems.smtpHost,
|
||||
ems.smtpPort,
|
||||
ems.smtpUser,
|
||||
ems.smtpPassword,
|
||||
sslt,
|
||||
!ems.ignoreCertificates,
|
||||
from,
|
||||
repl
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ CREATE TABLE "useremail" (
|
||||
"uid" varchar(254) not null,
|
||||
"name" varchar(254) not null,
|
||||
"smtp_host" varchar(254) not null,
|
||||
"smtp_port" int not null,
|
||||
"smtp_user" varchar(254) not null,
|
||||
"smtp_password" varchar(254) not null,
|
||||
"smtp_port" int,
|
||||
"smtp_user" varchar(254),
|
||||
"smtp_password" varchar(254),
|
||||
"smtp_ssl" varchar(254) not null,
|
||||
"smtp_certcheck" boolean not null,
|
||||
"mail_from" varchar(254) not null,
|
||||
|
@ -57,7 +57,7 @@ object RUserEmail {
|
||||
now
|
||||
)
|
||||
|
||||
def apply(
|
||||
def fromAccount(
|
||||
accId: AccountId,
|
||||
name: Ident,
|
||||
smtpHost: String,
|
||||
@ -130,17 +130,21 @@ object RUserEmail {
|
||||
).update.run
|
||||
|
||||
def update(eId: Ident, v: RUserEmail): ConnectionIO[Int] =
|
||||
updateRow(table, id.is(eId), commas(
|
||||
name.setTo(v.name),
|
||||
smtpHost.setTo(v.smtpHost),
|
||||
smtpPort.setTo(v.smtpPort),
|
||||
smtpUser.setTo(v.smtpUser),
|
||||
smtpPass.setTo(v.smtpPassword),
|
||||
smtpSsl.setTo(v.smtpSsl),
|
||||
smtpCertCheck.setTo(v.smtpCertCheck),
|
||||
mailFrom.setTo(v.mailFrom),
|
||||
mailReplyTo.setTo(v.mailReplyTo)
|
||||
)).update.run
|
||||
updateRow(
|
||||
table,
|
||||
id.is(eId),
|
||||
commas(
|
||||
name.setTo(v.name),
|
||||
smtpHost.setTo(v.smtpHost),
|
||||
smtpPort.setTo(v.smtpPort),
|
||||
smtpUser.setTo(v.smtpUser),
|
||||
smtpPass.setTo(v.smtpPassword),
|
||||
smtpSsl.setTo(v.smtpSsl),
|
||||
smtpCertCheck.setTo(v.smtpCertCheck),
|
||||
mailFrom.setTo(v.mailFrom),
|
||||
mailReplyTo.setTo(v.mailReplyTo)
|
||||
)
|
||||
).update.run
|
||||
|
||||
def findByUser(userId: Ident): ConnectionIO[Vector[RUserEmail]] =
|
||||
selectSimple(all, table, uid.is(userId)).query[RUserEmail].to[Vector]
|
||||
@ -162,7 +166,7 @@ object RUserEmail {
|
||||
case None => Seq.empty
|
||||
})
|
||||
|
||||
selectSimple(all, from, and(cond)).query[RUserEmail]
|
||||
(selectSimple(all.map(_.prefix("m")), from, and(cond)) ++ orderBy(mName.f)).query[RUserEmail]
|
||||
}
|
||||
|
||||
def findByAccount(
|
||||
@ -174,6 +178,20 @@ object RUserEmail {
|
||||
def getByName(accId: AccountId, name: Ident): ConnectionIO[Option[RUserEmail]] =
|
||||
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)
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
module Api exposing
|
||||
( cancelJob
|
||||
, changePassword
|
||||
, createMailSettings
|
||||
, deleteEquip
|
||||
, deleteItem
|
||||
, deleteMailSettings
|
||||
, deleteOrg
|
||||
, deletePerson
|
||||
, deleteSource
|
||||
@ -15,6 +17,7 @@ module Api exposing
|
||||
, getItemProposals
|
||||
, getJobQueueState
|
||||
, getJobQueueStateIn
|
||||
, getMailSettings
|
||||
, getOrgLight
|
||||
, getOrganizations
|
||||
, getPersons
|
||||
@ -60,6 +63,8 @@ import Api.Model.BasicResult exposing (BasicResult)
|
||||
import Api.Model.Collective exposing (Collective)
|
||||
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
|
||||
import Api.Model.DirectionValue exposing (DirectionValue)
|
||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
||||
import Api.Model.Equipment exposing (Equipment)
|
||||
import Api.Model.EquipmentList exposing (EquipmentList)
|
||||
import Api.Model.GenInvite exposing (GenInvite)
|
||||
@ -99,6 +104,57 @@ import Util.File
|
||||
import Util.Http as Http2
|
||||
|
||||
|
||||
|
||||
--- Mail Settings
|
||||
|
||||
|
||||
deleteMailSettings : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||
deleteMailSettings flags name receive =
|
||||
Http2.authDelete
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/" ++ name
|
||||
, account = getAccount flags
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
|
||||
getMailSettings : Flags -> String -> (Result Http.Error EmailSettingsList -> msg) -> Cmd msg
|
||||
getMailSettings flags query receive =
|
||||
Http2.authGet
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings?q=" ++ Url.percentEncode query
|
||||
, account = getAccount flags
|
||||
, expect = Http.expectJson receive Api.Model.EmailSettingsList.decoder
|
||||
}
|
||||
|
||||
|
||||
createMailSettings :
|
||||
Flags
|
||||
-> Maybe String
|
||||
-> EmailSettings
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
createMailSettings flags mname ems receive =
|
||||
case mname of
|
||||
Just en ->
|
||||
Http2.authPut
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings/" ++ en
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.EmailSettings.encode ems)
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
Nothing ->
|
||||
Http2.authPost
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/email/settings"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.EmailSettings.encode ems)
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Upload
|
||||
|
||||
|
||||
upload : Flags -> Maybe String -> ItemUploadMeta -> List File -> (String -> Result Http.Error BasicResult -> msg) -> List (Cmd msg)
|
||||
upload flags sourceId meta files receive =
|
||||
let
|
||||
|
@ -4,6 +4,7 @@ module Comp.EmailSettingsForm exposing
|
||||
, emptyModel
|
||||
, getSettings
|
||||
, init
|
||||
, isValid
|
||||
, update
|
||||
, view
|
||||
)
|
||||
@ -40,7 +41,7 @@ emptyModel =
|
||||
{ settings = Api.Model.EmailSettings.empty
|
||||
, name = ""
|
||||
, host = ""
|
||||
, portField = Comp.IntField.init (Just 0) Nothing "SMTP Port"
|
||||
, portField = Comp.IntField.init (Just 0) Nothing True "SMTP Port"
|
||||
, portNum = Nothing
|
||||
, user = Nothing
|
||||
, passField = Comp.PasswordInput.init
|
||||
@ -63,7 +64,7 @@ init ems =
|
||||
{ settings = ems
|
||||
, name = ems.name
|
||||
, host = ems.smtpHost
|
||||
, portField = Comp.IntField.init (Just 0) Nothing "SMTP Port"
|
||||
, portField = Comp.IntField.init (Just 0) Nothing True "SMTP Port"
|
||||
, portNum = ems.smtpPort
|
||||
, user = ems.smtpUser
|
||||
, passField = Comp.PasswordInput.init
|
||||
@ -184,7 +185,7 @@ view model =
|
||||
]
|
||||
]
|
||||
, div [ class "fields" ]
|
||||
[ div [ class "fifteen wide required field" ]
|
||||
[ div [ class "thirteen wide required field" ]
|
||||
[ label [] [ text "SMTP Host" ]
|
||||
, input
|
||||
[ type_ "text"
|
||||
@ -194,7 +195,11 @@ view model =
|
||||
]
|
||||
[]
|
||||
]
|
||||
, Html.map PortMsg (Comp.IntField.view model.portNum model.portField)
|
||||
, Html.map PortMsg
|
||||
(Comp.IntField.view model.portNum
|
||||
"three wide field"
|
||||
model.portField
|
||||
)
|
||||
]
|
||||
, div [ class "two fields" ]
|
||||
[ div [ class "field" ]
|
||||
|
@ -18,6 +18,8 @@ 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 =
|
||||
@ -43,9 +45,9 @@ emptyModel =
|
||||
}
|
||||
|
||||
|
||||
init : ( Model, Cmd Msg )
|
||||
init =
|
||||
( emptyModel, Cmd.none )
|
||||
init : Flags -> ( Model, Cmd Msg )
|
||||
init flags =
|
||||
( emptyModel, Api.getMailSettings flags "" MailSettingsResp )
|
||||
|
||||
|
||||
type ViewMode
|
||||
@ -61,6 +63,10 @@ type Msg
|
||||
| YesNoMsg Comp.YesNoDimmer.Msg
|
||||
| RequestDelete
|
||||
| SetViewMode ViewMode
|
||||
| Submit
|
||||
| SubmitResp (Result Http.Error BasicResult)
|
||||
| LoadSettings
|
||||
| MailSettingsResp (Result Http.Error EmailSettingsList)
|
||||
|
||||
|
||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
@ -84,8 +90,27 @@ update flags msg model =
|
||||
let
|
||||
( tm, tc ) =
|
||||
Comp.EmailSettingsTable.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.EmailSettingsForm.init ems
|
||||
|
||||
Nothing ->
|
||||
model.formModel
|
||||
}
|
||||
in
|
||||
( { model | tableModel = tm }, Cmd.map TableMsg tc )
|
||||
( m2, Cmd.map TableMsg tc )
|
||||
|
||||
FormMsg m ->
|
||||
let
|
||||
@ -95,21 +120,84 @@ update flags msg model =
|
||||
( { model | formModel = fm }, Cmd.map FormMsg fc )
|
||||
|
||||
SetQuery str ->
|
||||
( { model | query = str }, Cmd.none )
|
||||
let
|
||||
m =
|
||||
{ model | query = str }
|
||||
in
|
||||
( m, Api.getMailSettings flags str MailSettingsResp )
|
||||
|
||||
YesNoMsg m ->
|
||||
let
|
||||
( dm, flag ) =
|
||||
Comp.YesNoDimmer.update m model.deleteConfirm
|
||||
|
||||
( mid, _ ) =
|
||||
Comp.EmailSettingsForm.getSettings model.formModel
|
||||
|
||||
cmd =
|
||||
case ( flag, mid ) of
|
||||
( True, Just name ) ->
|
||||
Api.deleteMailSettings flags name SubmitResp
|
||||
|
||||
_ ->
|
||||
Cmd.none
|
||||
in
|
||||
( { model | deleteConfirm = dm }, Cmd.none )
|
||||
( { model | deleteConfirm = dm }, cmd )
|
||||
|
||||
RequestDelete ->
|
||||
( model, Cmd.none )
|
||||
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
|
||||
|
||||
SetViewMode m ->
|
||||
( { model | viewMode = m }, Cmd.none )
|
||||
|
||||
Submit ->
|
||||
let
|
||||
( mid, ems ) =
|
||||
Comp.EmailSettingsForm.getSettings model.formModel
|
||||
|
||||
valid =
|
||||
Comp.EmailSettingsForm.isValid model.formModel
|
||||
in
|
||||
if valid then
|
||||
( { model | loading = True }, Api.createMailSettings flags mid ems SubmitResp )
|
||||
|
||||
else
|
||||
( { model | formError = Just "Please fill required fields." }, Cmd.none )
|
||||
|
||||
LoadSettings ->
|
||||
( { model | loading = True }, Api.getMailSettings 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.EmailSettingsTable.init ems.items
|
||||
}
|
||||
in
|
||||
( m2, Cmd.none )
|
||||
|
||||
MailSettingsResp (Err _) ->
|
||||
( { model | loading = False }, Cmd.none )
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
@ -171,10 +259,18 @@ viewForm model =
|
||||
[ Maybe.withDefault "" model.formError |> text
|
||||
]
|
||||
, div [ class "ui divider" ] []
|
||||
, button [ class "ui primary button" ]
|
||||
, button
|
||||
[ class "ui primary button"
|
||||
, onClick Submit
|
||||
, href "#"
|
||||
]
|
||||
[ text "Submit"
|
||||
]
|
||||
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
|
||||
, a
|
||||
[ class "ui secondary button"
|
||||
, onClick (SetViewMode Table)
|
||||
, href ""
|
||||
]
|
||||
[ text "Cancel"
|
||||
]
|
||||
, if model.formModel.settings.name /= "" then
|
||||
|
@ -2,6 +2,7 @@ module Comp.EmailSettingsTable exposing
|
||||
( Model
|
||||
, Msg
|
||||
, emptyModel
|
||||
, init
|
||||
, update
|
||||
, view
|
||||
)
|
||||
@ -9,10 +10,12 @@ module Comp.EmailSettingsTable exposing
|
||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ emailSettings : List EmailSettings
|
||||
, selected : Maybe EmailSettings
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +27,7 @@ emptyModel =
|
||||
init : List EmailSettings -> Model
|
||||
init ems =
|
||||
{ emailSettings = ems
|
||||
, selected = Nothing
|
||||
}
|
||||
|
||||
|
||||
@ -33,17 +37,40 @@ type Msg
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
( model, Cmd.none )
|
||||
case msg of
|
||||
Select ems ->
|
||||
( { model | selected = Just ems }, Cmd.none )
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
table [ class "ui table" ]
|
||||
table [ class "ui selectable pointer table" ]
|
||||
[ thead []
|
||||
[ th [] [ text "Name" ]
|
||||
[ th [ class "collapsible" ] [ text "Name" ]
|
||||
, th [] [ text "Host/Port" ]
|
||||
, th [] [ text "From" ]
|
||||
]
|
||||
, tbody []
|
||||
[]
|
||||
(List.map (renderLine model) model.emailSettings)
|
||||
]
|
||||
|
||||
|
||||
renderLine : Model -> EmailSettings -> Html Msg
|
||||
renderLine model ems =
|
||||
let
|
||||
hostport =
|
||||
case ems.smtpPort of
|
||||
Just p ->
|
||||
ems.smtpHost ++ ":" ++ String.fromInt p
|
||||
|
||||
Nothing ->
|
||||
ems.smtpHost
|
||||
in
|
||||
tr
|
||||
[ classList [ ( "active", model.selected == Just ems ) ]
|
||||
, onClick (Select ems)
|
||||
]
|
||||
[ td [ class "collapsible" ] [ text ems.name ]
|
||||
, td [] [ text hostport ]
|
||||
, td [] [ text ems.from ]
|
||||
]
|
||||
|
@ -11,6 +11,7 @@ type alias Model =
|
||||
, label : String
|
||||
, error : Maybe String
|
||||
, lastInput : String
|
||||
, optional : Bool
|
||||
}
|
||||
|
||||
|
||||
@ -18,26 +19,27 @@ type Msg
|
||||
= SetValue String
|
||||
|
||||
|
||||
init : Maybe Int -> Maybe Int -> String -> Model
|
||||
init min max label =
|
||||
init : Maybe Int -> Maybe Int -> Bool -> String -> Model
|
||||
init min max opt label =
|
||||
{ min = min
|
||||
, max = max
|
||||
, label = label
|
||||
, error = Nothing
|
||||
, lastInput = ""
|
||||
, optional = opt
|
||||
}
|
||||
|
||||
|
||||
tooLow : Model -> Int -> Bool
|
||||
tooLow model n =
|
||||
Maybe.map ((<) n) model.min
|
||||
|> Maybe.withDefault False
|
||||
|> Maybe.withDefault (not model.optional)
|
||||
|
||||
|
||||
tooHigh : Model -> Int -> Bool
|
||||
tooHigh model n =
|
||||
Maybe.map ((>) n) model.max
|
||||
|> Maybe.withDefault False
|
||||
|> Maybe.withDefault (not model.optional)
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Maybe Int )
|
||||
@ -75,16 +77,20 @@ update msg model =
|
||||
( { m | error = Nothing }, Just n )
|
||||
|
||||
Nothing ->
|
||||
( { m | error = Just ("'" ++ str ++ "' is not a valid number!") }
|
||||
, Nothing
|
||||
)
|
||||
if model.optional && String.trim str == "" then
|
||||
( { m | error = Nothing }, Nothing )
|
||||
|
||||
else
|
||||
( { m | error = Just ("'" ++ str ++ "' is not a valid number!") }
|
||||
, Nothing
|
||||
)
|
||||
|
||||
|
||||
view : Maybe Int -> Model -> Html Msg
|
||||
view nval model =
|
||||
view : Maybe Int -> String -> Model -> Html Msg
|
||||
view nval classes model =
|
||||
div
|
||||
[ classList
|
||||
[ ( "field", True )
|
||||
[ ( classes, True )
|
||||
, ( "error", model.error /= Nothing )
|
||||
]
|
||||
]
|
||||
|
@ -13,8 +13,20 @@ update flags msg model =
|
||||
let
|
||||
m =
|
||||
{ model | currentTab = Just t }
|
||||
|
||||
( m2, cmd ) =
|
||||
case t of
|
||||
EmailSettingsTab ->
|
||||
let
|
||||
( em, c ) =
|
||||
Comp.EmailSettingsManage.init flags
|
||||
in
|
||||
( { m | emailSettingsModel = em }, Cmd.map EmailSettingsMsg c )
|
||||
|
||||
ChangePassTab ->
|
||||
( m, Cmd.none )
|
||||
in
|
||||
( m, Cmd.none )
|
||||
( m2, cmd )
|
||||
|
||||
ChangePassMsg m ->
|
||||
let
|
||||
|
@ -88,6 +88,10 @@
|
||||
background-color: #d8dfe5;
|
||||
}
|
||||
|
||||
.ui.selectable.pointer.table tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span.small-info {
|
||||
font-size: smaller;
|
||||
color: rgba(0,0,0,0.6);
|
||||
|
Loading…
x
Reference in New Issue
Block a user