Use collective settings for shares

This commit is contained in:
eikek
2022-01-29 14:15:49 +01:00
parent 3710f525d1
commit e1a8b9b121
7 changed files with 60 additions and 55 deletions

View File

@ -6,6 +6,7 @@
package docspell.backend.ops package docspell.backend.ops
import cats.Semigroup
import cats.data.OptionT import cats.data.OptionT
import cats.effect.{Async, Resource} import cats.effect.{Async, Resource}
import cats.implicits._ import cats.implicits._
@ -32,6 +33,7 @@ trait OClientSettings[F[_]] {
account: AccountId account: AccountId
): F[Option[RClientSettingsCollective]] ): F[Option[RClientSettingsCollective]]
def loadMerged(clientId: Ident, account: AccountId): F[Option[Json]]
} }
object OClientSettings { object OClientSettings {
@ -108,5 +110,14 @@ object OClientSettings {
data <- OptionT(store.transact(RClientSettingsUser.find(clientId, userId))) data <- OptionT(store.transact(RClientSettingsUser.find(clientId, userId)))
} yield data).value } yield data).value
def loadMerged(clientId: Ident, account: AccountId) =
for {
collData <- loadCollective(clientId, account)
userData <- loadUser(clientId, account)
mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData)
} yield mergedData
implicit def jsonSemigroup: Semigroup[Json] =
Semigroup.instance((a1, a2) => a1.deepMerge(a2))
}) })
} }

View File

@ -2356,13 +2356,7 @@ paths:
summary: Return the client settings of current user summary: Return the client settings of current user
description: | description: |
Returns the settings for the share. This is the settings of Returns the settings for the share. This is the settings of
the user who created the share. It is created by merging the the collective that belongs to the share.
client settings for the collective and the user's own client
settings into one json structure.
Null, Array, Boolean, String and Number are treated as values,
and values from the users settings completely replace values
from the collective's settings.
The `clientId` is an identifier to a client application. It The `clientId` is an identifier to a client application. It
returns a JSON structure. The server doesn't care about the returns a JSON structure. The server doesn't care about the

View File

@ -6,10 +6,9 @@
package docspell.restserver.routes package docspell.restserver.routes
import cats.data.OptionT
import cats.effect._ import cats.effect._
import cats.implicits._ import cats.implicits._
import cats.{Monad, Semigroup}
import cats.data.OptionT
import docspell.backend.BackendApp import docspell.backend.BackendApp
import docspell.backend.auth.{AuthToken, ShareToken} import docspell.backend.auth.{AuthToken, ShareToken}
@ -33,16 +32,14 @@ object ClientSettingsRoutes {
val dsl = new Http4sDsl[F] {} val dsl = new Http4sDsl[F] {}
import dsl._ import dsl._
HttpRoutes.of { HttpRoutes.of { case GET -> Root / Ident(clientId) =>
case GET -> Root / Ident(clientId) =>
(for { (for {
_ <- OptionT.liftF(logger.debug(s"Get client settings for share ${token.id}")) _ <- OptionT.liftF(logger.debug(s"Get client settings for share ${token.id}"))
share <- backend.share.findActiveById(token.id) share <- backend.share.findActiveById(token.id)
merged <- OptionT.liftF(getMergedSettings(backend, share.user.accountId, clientId)) sett <- OptionT(
res <- OptionT.liftF(merged match { backend.clientSettings.loadCollective(clientId, share.user.accountId)
case Some(j) => Ok(j) )
case None => NotFound() res <- OptionT.liftF(Ok(sett.settingsData))
})
} yield res) } yield res)
.getOrElseF(Ok(Map.empty[String, String])) .getOrElseF(Ok(Map.empty[String, String]))
} }
@ -55,11 +52,10 @@ object ClientSettingsRoutes {
HttpRoutes.of { HttpRoutes.of {
case GET -> Root / Ident(clientId) => case GET -> Root / Ident(clientId) =>
for { for {
mergedData <- getMergedSettings(backend, user.account, clientId) mergedData <- backend.clientSettings.loadMerged(clientId, user.account)
res <- mergedData match { res <- mergedData match {
case Some(j) => Ok(j) case Some(j) => Ok(j)
case None => NotFound() case None => Ok(Map.empty[String, String])
} }
} yield res } yield res
@ -118,17 +114,4 @@ object ClientSettingsRoutes {
} yield res } yield res
} }
} }
def getMergedSettings[F[_]: Monad](backend:BackendApp[F], account: AccountId, clientId: Ident) =
for {
collData <- backend.clientSettings.loadCollective(clientId, account)
userData <- backend.clientSettings.loadUser(clientId, account)
mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData)
} yield mergedData
implicit def jsonSemigroup: Semigroup[Json] =
Semigroup.instance((a1, a2) => a1.deepMerge(a2))
} }

View File

@ -580,7 +580,6 @@ update flags sett msg model =
TagsTab -> TagsTab ->
{ sett | tagCategoryColors = Nothing } { sett | tagCategoryColors = Nothing }
-- no reset here
FieldsTab -> FieldsTab ->
{ sett | formFields = Nothing } { sett | formFields = Nothing }
@ -658,7 +657,7 @@ settingFormTabs texts flags _ model =
, onClick (ResetTab tab) , onClick (ResetTab tab)
] ]
[ i [ class "fa fa-eraser mr-1" ] [] [ i [ class "fa fa-eraser mr-1" ] []
, text "Reset" , text texts.resetLabel
] ]
in in
[ { name = akkordionTabName GeneralTab [ { name = akkordionTabName GeneralTab

View File

@ -28,7 +28,9 @@ import Html.Attributes exposing (..)
import Http import Http
import Messages.Comp.UiSettingsManage exposing (Texts) import Messages.Comp.UiSettingsManage exposing (Texts)
import Page.Search.Data exposing (Msg(..)) import Page.Search.Data exposing (Msg(..))
import Process
import Styles as S import Styles as S
import Task
type alias Model = type alias Model =
@ -67,6 +69,7 @@ type Msg
| ReceiveServerSettings (Result Http.Error ( StoredUiSettings, StoredUiSettings )) | ReceiveServerSettings (Result Http.Error ( StoredUiSettings, StoredUiSettings ))
| ToggleExpandCollapse | ToggleExpandCollapse
| SwitchForm AccountScope | SwitchForm AccountScope
| ResetFormState
init : Flags -> ( Model, Cmd Msg ) init : Flags -> ( Model, Cmd Msg )
@ -134,12 +137,6 @@ update flags settings msg model =
| collSettings = Maybe.withDefault data.collSettings sett | collSettings = Maybe.withDefault data.collSettings sett
, collModel = m_ , collModel = m_
} }
, formResult =
if sett /= Nothing then
FormInit
else
model.formResult
} }
Data.AccountScope.User -> Data.AccountScope.User ->
@ -155,12 +152,6 @@ update flags settings msg model =
| userSettings = Maybe.withDefault data.userSettings sett | userSettings = Maybe.withDefault data.userSettings sett
, userModel = m_ , userModel = m_
} }
, formResult =
if sett /= Nothing then
FormInit
else
model.formResult
} }
Submit -> Submit ->
@ -198,9 +189,13 @@ update flags settings msg model =
update flags update flags
settings settings
(ReceiveServerSettings (Ok ( data.collSettings, data.userSettings ))) (ReceiveServerSettings (Ok ( data.collSettings, data.userSettings )))
model { model | formResult = FormSaved }
cmd =
Process.sleep 2000
|> Task.perform (\_ -> ResetFormState)
in in
{ result | appEvent = AppReloadUiSettings } { result | appEvent = AppReloadUiSettings, cmd = Cmd.batch [ cmd, result.cmd ] }
_ -> _ ->
unit { model | formResult = FormUnknownError } unit { model | formResult = FormUnknownError }
@ -231,7 +226,13 @@ update flags settings msg model =
, collSettings = coll , collSettings = coll
, collModel = cm , collModel = cm
} }
, formModel = ViewUser , formModel =
case model.formModel of
ViewLoading ->
ViewUser
_ ->
model.formModel
} }
cmds = cmds =
@ -262,6 +263,14 @@ update flags settings msg model =
in in
Data.AccountScope.fold forUser forColl scope Data.AccountScope.fold forUser forColl scope
ResetFormState ->
case model.formResult of
FormSaved ->
unit { model | formResult = FormInit }
_ ->
unit model
isError : Model -> Bool isError : Model -> Bool
isError model = isError model =
@ -364,6 +373,9 @@ view2 texts flags _ classes model =
[ h2 [ class S.header2 ] [ h2 [ class S.header2 ]
[ text texts.collectiveHeader [ text texts.collectiveHeader
] ]
, div [ class "py-1 opacity-80" ]
[ text texts.collectiveInfo
]
, Html.map (UiFormMsg scope) , Html.map (UiFormMsg scope)
(Comp.UiSettingsForm.view2 (Comp.UiSettingsForm.view2
texts.uiSettingsForm texts.uiSettingsForm

View File

@ -56,6 +56,7 @@ type alias Texts =
, fieldLabel : Field -> String , fieldLabel : Field -> String
, templateHelpMessage : String , templateHelpMessage : String
, pdfMode : PdfMode -> String , pdfMode : PdfMode -> String
, resetLabel : String
} }
@ -131,6 +132,7 @@ and if that is not present the person. If both are absent a dash `-`
is rendered. is rendered.
""" """
, pdfMode = Messages.Data.PdfMode.gb , pdfMode = Messages.Data.PdfMode.gb
, resetLabel = "Reset"
} }
@ -208,4 +210,5 @@ oder, wenn diese leer ist, die Person. Sind beide leer wird ein `-`
dargestellt. dargestellt.
""" """
, pdfMode = Messages.Data.PdfMode.de , pdfMode = Messages.Data.PdfMode.de
, resetLabel = "Zurücksetzen"
} }

View File

@ -30,6 +30,7 @@ type alias Texts =
, userHeader : String , userHeader : String
, userInfo : String , userInfo : String
, collectiveHeader : String , collectiveHeader : String
, collectiveInfo : String
, expandCollapse : String , expandCollapse : String
} }
@ -45,8 +46,9 @@ gb =
, unknownSaveError = "Unknown error while trying to save settings." , unknownSaveError = "Unknown error while trying to save settings."
, httpError = Messages.Comp.HttpError.gb , httpError = Messages.Comp.HttpError.gb
, userHeader = "Personal settings" , userHeader = "Personal settings"
, userInfo = "Your personal settings override those of the collective. On reset, settings are set back to those of the collective." , userInfo = "Your personal settings override those of the collective. On reset, settings are taken from the collective settings."
, collectiveHeader = "Collective settings" , collectiveHeader = "Collective settings"
, collectiveInfo = "These settings apply to all users, unless overriden by personal ones. A reset loads the provided default values of the application."
, expandCollapse = "Expand/collapse all" , expandCollapse = "Expand/collapse all"
} }
@ -62,7 +64,8 @@ de =
, unknownSaveError = "Unbekannter Fehler beim Speichern der Einstellungen." , unknownSaveError = "Unbekannter Fehler beim Speichern der Einstellungen."
, httpError = Messages.Comp.HttpError.de , httpError = Messages.Comp.HttpError.de
, userHeader = "Persönliche Einstellungen" , userHeader = "Persönliche Einstellungen"
, userInfo = "Die persönlichen Einstellungen überschreiben die des Kollektivs. Wenn Einstellungen zurückgesetzt werden, werden sie auf die Werte des Kollektivs gesetzt." , userInfo = "Die persönlichen Einstellungen überschreiben die des Kollektivs. Wenn Einstellungen zurückgesetzt werden, gelten automatisch die Werte des Kollektivs."
, collectiveHeader = "Kollektiv Einstellungen" , collectiveHeader = "Kollektiv Einstellungen"
, collectiveInfo = "Diese Einstellungen sind für alle Benutzer, können aber in den persönlichen Einstellungen überschrieben werden. Durch ein Zurücksetzen erhält man die bereitgestellten Standardwerte der Anwendung."
, expandCollapse = "Alle ein-/ausklappen" , expandCollapse = "Alle ein-/ausklappen"
} }