mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-11-03 18:00:11 +00:00 
			
		
		
		
	Merge pull request #415 from eikek/fix-multi-tag-edit
Fix multi tag edit
This commit is contained in:
		@@ -8,7 +8,7 @@ This release contains many bug fixes, thank you all so much for
 | 
				
			|||||||
helping out! There is also a new feature and some more scripts in
 | 
					helping out! There is also a new feature and some more scripts in
 | 
				
			||||||
tools.
 | 
					tools.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Edit/delete multiple items at once (#253)
 | 
					- Edit/delete multiple items at once (#253, #412)
 | 
				
			||||||
- Show/hide side menus via ui settings (#351)
 | 
					- Show/hide side menus via ui settings (#351)
 | 
				
			||||||
- Adds two more scripts to the `tools/` section (thanks to
 | 
					- Adds two more scripts to the `tools/` section (thanks to
 | 
				
			||||||
  @totti4ever):
 | 
					  @totti4ever):
 | 
				
			||||||
@@ -36,6 +36,7 @@ tools.
 | 
				
			|||||||
- Routes for managing multiple items:
 | 
					- Routes for managing multiple items:
 | 
				
			||||||
  - `/sec/items/deleteAll`
 | 
					  - `/sec/items/deleteAll`
 | 
				
			||||||
  - `/sec/items/tags`
 | 
					  - `/sec/items/tags`
 | 
				
			||||||
 | 
					  - `/sec/items/tagsremove`
 | 
				
			||||||
  - `/sec/items/name`
 | 
					  - `/sec/items/name`
 | 
				
			||||||
  - `/sec/items/folder`
 | 
					  - `/sec/items/folder`
 | 
				
			||||||
  - `/sec/items/direction`
 | 
					  - `/sec/items/direction`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,12 @@ trait OItem[F[_]] {
 | 
				
			|||||||
      collective: Ident
 | 
					      collective: Ident
 | 
				
			||||||
  ): F[UpdateResult]
 | 
					  ): F[UpdateResult]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def removeTagsMultipleItems(
 | 
				
			||||||
 | 
					      items: NonEmptyList[Ident],
 | 
				
			||||||
 | 
					      tags: List[String],
 | 
				
			||||||
 | 
					      collective: Ident
 | 
				
			||||||
 | 
					  ): F[UpdateResult]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Toggles tags of the given item. Tags must exist, but can be IDs or names. */
 | 
					  /** Toggles tags of the given item. Tags must exist, but can be IDs or names. */
 | 
				
			||||||
  def toggleTags(item: Ident, tags: List[String], collective: Ident): F[UpdateResult]
 | 
					  def toggleTags(item: Ident, tags: List[String], collective: Ident): F[UpdateResult]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -225,6 +231,29 @@ object OItem {
 | 
				
			|||||||
              }
 | 
					              }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def removeTagsMultipleItems(
 | 
				
			||||||
 | 
					            items: NonEmptyList[Ident],
 | 
				
			||||||
 | 
					            tags: List[String],
 | 
				
			||||||
 | 
					            collective: Ident
 | 
				
			||||||
 | 
					        ): F[UpdateResult] =
 | 
				
			||||||
 | 
					          tags.distinct match {
 | 
				
			||||||
 | 
					            case Nil => UpdateResult.success.pure[F]
 | 
				
			||||||
 | 
					            case ws =>
 | 
				
			||||||
 | 
					              store.transact {
 | 
				
			||||||
 | 
					                (for {
 | 
				
			||||||
 | 
					                  itemIds <- OptionT
 | 
				
			||||||
 | 
					                    .liftF(RItem.filterItems(items, collective))
 | 
				
			||||||
 | 
					                    .filter(_.nonEmpty)
 | 
				
			||||||
 | 
					                  given <- OptionT.liftF(RTag.findAllByNameOrId(ws, collective))
 | 
				
			||||||
 | 
					                  _ <- OptionT.liftF(
 | 
				
			||||||
 | 
					                    itemIds.traverse(item =>
 | 
				
			||||||
 | 
					                      RTagItem.removeAllTags(item, given.map(_.tagId).toList)
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                } yield UpdateResult.success).getOrElse(UpdateResult.notFound)
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def toggleTags(
 | 
					        def toggleTags(
 | 
				
			||||||
            item: Ident,
 | 
					            item: Ident,
 | 
				
			||||||
            tags: List[String],
 | 
					            tags: List[String],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1945,6 +1945,29 @@ paths:
 | 
				
			|||||||
            application/json:
 | 
					            application/json:
 | 
				
			||||||
              schema:
 | 
					              schema:
 | 
				
			||||||
                $ref: "#/components/schemas/BasicResult"
 | 
					                $ref: "#/components/schemas/BasicResult"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /sec/items/tagsremove:
 | 
				
			||||||
 | 
					    post:
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - Item (Multi Edit)
 | 
				
			||||||
 | 
					      summary: Remove tags from multiple items
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Remove the given tags from all given items.
 | 
				
			||||||
 | 
					      security:
 | 
				
			||||||
 | 
					        - authTokenHeader: []
 | 
				
			||||||
 | 
					      requestBody:
 | 
				
			||||||
 | 
					        content:
 | 
				
			||||||
 | 
					          application/json:
 | 
				
			||||||
 | 
					            schema:
 | 
				
			||||||
 | 
					              $ref: "#/components/schemas/ItemsAndRefs"
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        200:
 | 
				
			||||||
 | 
					          description: Ok
 | 
				
			||||||
 | 
					          content:
 | 
				
			||||||
 | 
					            application/json:
 | 
				
			||||||
 | 
					              schema:
 | 
				
			||||||
 | 
					                $ref: "#/components/schemas/BasicResult"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    put:
 | 
					    put:
 | 
				
			||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - Item (Multi Edit)
 | 
					        - Item (Multi Edit)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,6 +73,18 @@ object ItemMultiRoutes {
 | 
				
			|||||||
          resp <- Ok(Conversions.basicResult(res, "Tags added."))
 | 
					          resp <- Ok(Conversions.basicResult(res, "Tags added."))
 | 
				
			||||||
        } yield resp
 | 
					        } yield resp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case req @ POST -> Root / "tagsremove" =>
 | 
				
			||||||
 | 
					        for {
 | 
				
			||||||
 | 
					          json  <- req.as[ItemsAndRefs]
 | 
				
			||||||
 | 
					          items <- readIds[F](json.items)
 | 
				
			||||||
 | 
					          res <- backend.item.removeTagsMultipleItems(
 | 
				
			||||||
 | 
					            items,
 | 
				
			||||||
 | 
					            json.refs,
 | 
				
			||||||
 | 
					            user.account.collective
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          resp <- Ok(Conversions.basicResult(res, "Tags removed"))
 | 
				
			||||||
 | 
					        } yield resp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case req @ PUT -> Root / "name" =>
 | 
					      case req @ PUT -> Root / "name" =>
 | 
				
			||||||
        for {
 | 
					        for {
 | 
				
			||||||
          json  <- req.as[ItemsAndName]
 | 
					          json  <- req.as[ItemsAndName]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,7 @@ module Api exposing
 | 
				
			|||||||
    , refreshSession
 | 
					    , refreshSession
 | 
				
			||||||
    , register
 | 
					    , register
 | 
				
			||||||
    , removeMember
 | 
					    , removeMember
 | 
				
			||||||
 | 
					    , removeTagsMultiple
 | 
				
			||||||
    , sendMail
 | 
					    , sendMail
 | 
				
			||||||
    , setAttachmentName
 | 
					    , setAttachmentName
 | 
				
			||||||
    , setCollectiveSettings
 | 
					    , setCollectiveSettings
 | 
				
			||||||
@@ -1342,6 +1343,20 @@ addTagsMultiple flags data receive =
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					removeTagsMultiple :
 | 
				
			||||||
 | 
					    Flags
 | 
				
			||||||
 | 
					    -> ItemsAndRefs
 | 
				
			||||||
 | 
					    -> (Result Http.Error BasicResult -> msg)
 | 
				
			||||||
 | 
					    -> Cmd msg
 | 
				
			||||||
 | 
					removeTagsMultiple flags data receive =
 | 
				
			||||||
 | 
					    Http2.authPost
 | 
				
			||||||
 | 
					        { url = flags.config.baseUrl ++ "/api/v1/sec/items/tagsremove"
 | 
				
			||||||
 | 
					        , account = getAccount flags
 | 
				
			||||||
 | 
					        , body = Http.jsonBody (Api.Model.ItemsAndRefs.encode data)
 | 
				
			||||||
 | 
					        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
setNameMultiple :
 | 
					setNameMultiple :
 | 
				
			||||||
    Flags
 | 
					    Flags
 | 
				
			||||||
    -> ItemsAndName
 | 
					    -> ItemsAndName
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,12 @@ type SaveNameState
 | 
				
			|||||||
    | SaveFailed
 | 
					    | SaveFailed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TagEditMode
 | 
				
			||||||
 | 
					    = AddTags
 | 
				
			||||||
 | 
					    | RemoveTags
 | 
				
			||||||
 | 
					    | ReplaceTags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type alias Model =
 | 
					type alias Model =
 | 
				
			||||||
    { tagModel : Comp.Dropdown.Model Tag
 | 
					    { tagModel : Comp.Dropdown.Model Tag
 | 
				
			||||||
    , nameModel : String
 | 
					    , nameModel : String
 | 
				
			||||||
@@ -70,6 +76,7 @@ type alias Model =
 | 
				
			|||||||
    , concPersonModel : Comp.Dropdown.Model IdName
 | 
					    , concPersonModel : Comp.Dropdown.Model IdName
 | 
				
			||||||
    , concEquipModel : Comp.Dropdown.Model IdName
 | 
					    , concEquipModel : Comp.Dropdown.Model IdName
 | 
				
			||||||
    , modalEdit : Maybe Comp.DetailEdit.Model
 | 
					    , modalEdit : Maybe Comp.DetailEdit.Model
 | 
				
			||||||
 | 
					    , tagEditMode : TagEditMode
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,6 +89,7 @@ type Msg
 | 
				
			|||||||
    | RemoveDueDate
 | 
					    | RemoveDueDate
 | 
				
			||||||
    | RemoveDate
 | 
					    | RemoveDate
 | 
				
			||||||
    | ConfirmMsg Bool
 | 
					    | ConfirmMsg Bool
 | 
				
			||||||
 | 
					    | ToggleTagEditMode
 | 
				
			||||||
    | FolderDropdownMsg (Comp.Dropdown.Msg IdName)
 | 
					    | FolderDropdownMsg (Comp.Dropdown.Msg IdName)
 | 
				
			||||||
    | TagDropdownMsg (Comp.Dropdown.Msg Tag)
 | 
					    | TagDropdownMsg (Comp.Dropdown.Msg Tag)
 | 
				
			||||||
    | DirDropdownMsg (Comp.Dropdown.Msg Direction)
 | 
					    | DirDropdownMsg (Comp.Dropdown.Msg Direction)
 | 
				
			||||||
@@ -146,6 +154,7 @@ init =
 | 
				
			|||||||
    , dueDate = Nothing
 | 
					    , dueDate = Nothing
 | 
				
			||||||
    , dueDatePicker = Comp.DatePicker.emptyModel
 | 
					    , dueDatePicker = Comp.DatePicker.emptyModel
 | 
				
			||||||
    , modalEdit = Nothing
 | 
					    , modalEdit = Nothing
 | 
				
			||||||
 | 
					    , tagEditMode = AddTags
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -213,19 +222,48 @@ update flags msg model =
 | 
				
			|||||||
                newModel =
 | 
					                newModel =
 | 
				
			||||||
                    { model | tagModel = m2 }
 | 
					                    { model | tagModel = m2 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                mkChange list =
 | 
				
			||||||
 | 
					                    case model.tagEditMode of
 | 
				
			||||||
 | 
					                        AddTags ->
 | 
				
			||||||
 | 
					                            AddTagChange list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        RemoveTags ->
 | 
				
			||||||
 | 
					                            RemoveTagChange list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        ReplaceTags ->
 | 
				
			||||||
 | 
					                            ReplaceTagChange list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                change =
 | 
					                change =
 | 
				
			||||||
                    if isDropdownChangeMsg m then
 | 
					                    if isDropdownChangeMsg m then
 | 
				
			||||||
                        Comp.Dropdown.getSelected newModel.tagModel
 | 
					                        Comp.Dropdown.getSelected newModel.tagModel
 | 
				
			||||||
                            |> Util.List.distinct
 | 
					                            |> Util.List.distinct
 | 
				
			||||||
                            |> List.map (\t -> IdName t.id t.name)
 | 
					                            |> List.map (\t -> IdName t.id t.name)
 | 
				
			||||||
                            |> ReferenceList
 | 
					                            |> ReferenceList
 | 
				
			||||||
                            |> TagChange
 | 
					                            |> mkChange
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                        NoFormChange
 | 
					                        NoFormChange
 | 
				
			||||||
            in
 | 
					            in
 | 
				
			||||||
            resultNoCmd change newModel
 | 
					            resultNoCmd change newModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ToggleTagEditMode ->
 | 
				
			||||||
 | 
					            let
 | 
				
			||||||
 | 
					                ( m2, _ ) =
 | 
				
			||||||
 | 
					                    Comp.Dropdown.update (Comp.Dropdown.SetSelection []) model.tagModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                newModel =
 | 
				
			||||||
 | 
					                    { model | tagModel = m2 }
 | 
				
			||||||
 | 
					            in
 | 
				
			||||||
 | 
					            case model.tagEditMode of
 | 
				
			||||||
 | 
					                AddTags ->
 | 
				
			||||||
 | 
					                    resultNone { newModel | tagEditMode = RemoveTags }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                RemoveTags ->
 | 
				
			||||||
 | 
					                    resultNone { newModel | tagEditMode = ReplaceTags }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ReplaceTags ->
 | 
				
			||||||
 | 
					                    resultNone { newModel | tagEditMode = AddTags }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        GetTagsResp (Ok tags) ->
 | 
					        GetTagsResp (Ok tags) ->
 | 
				
			||||||
            let
 | 
					            let
 | 
				
			||||||
                tagList =
 | 
					                tagList =
 | 
				
			||||||
@@ -554,6 +592,28 @@ renderEditForm cfg settings model =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                span [ class "invisible hidden" ] []
 | 
					                span [ class "invisible hidden" ] []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tagModeIcon =
 | 
				
			||||||
 | 
					            case model.tagEditMode of
 | 
				
			||||||
 | 
					                AddTags ->
 | 
				
			||||||
 | 
					                    i [ class "grey plus link icon" ] []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                RemoveTags ->
 | 
				
			||||||
 | 
					                    i [ class "grey eraser link icon" ] []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ReplaceTags ->
 | 
				
			||||||
 | 
					                    i [ class "grey redo alternate link icon" ] []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tagModeMsg =
 | 
				
			||||||
 | 
					            case model.tagEditMode of
 | 
				
			||||||
 | 
					                AddTags ->
 | 
				
			||||||
 | 
					                    "Tags chosen here are *added* to all selected items."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                RemoveTags ->
 | 
				
			||||||
 | 
					                    "Tags chosen here are *removed* from all selected items."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ReplaceTags ->
 | 
				
			||||||
 | 
					                    "Tags chosen here *replace* those on selected items."
 | 
				
			||||||
    in
 | 
					    in
 | 
				
			||||||
    div [ class cfg.menuClass ]
 | 
					    div [ class cfg.menuClass ]
 | 
				
			||||||
        [ div [ class "ui form warning" ]
 | 
					        [ div [ class "ui form warning" ]
 | 
				
			||||||
@@ -581,8 +641,17 @@ renderEditForm cfg settings model =
 | 
				
			|||||||
                    [ label []
 | 
					                    [ label []
 | 
				
			||||||
                        [ Icons.tagsIcon "grey"
 | 
					                        [ Icons.tagsIcon "grey"
 | 
				
			||||||
                        , text "Tags"
 | 
					                        , text "Tags"
 | 
				
			||||||
 | 
					                        , a
 | 
				
			||||||
 | 
					                            [ class "right-float"
 | 
				
			||||||
 | 
					                            , href "#"
 | 
				
			||||||
 | 
					                            , title "Change tag edit mode"
 | 
				
			||||||
 | 
					                            , onClick ToggleTagEditMode
 | 
				
			||||||
 | 
					                            ]
 | 
				
			||||||
 | 
					                            [ tagModeIcon
 | 
				
			||||||
 | 
					                            ]
 | 
				
			||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    , Html.map TagDropdownMsg (Comp.Dropdown.view settings model.tagModel)
 | 
					                    , Html.map TagDropdownMsg (Comp.Dropdown.view settings model.tagModel)
 | 
				
			||||||
 | 
					                    , Markdown.toHtml [ class "small-info" ] tagModeMsg
 | 
				
			||||||
                    ]
 | 
					                    ]
 | 
				
			||||||
            , div [ class " field" ]
 | 
					            , div [ class " field" ]
 | 
				
			||||||
                [ label [] [ text "Name" ]
 | 
					                [ label [] [ text "Name" ]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,9 @@ import Set exposing (Set)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type FormChange
 | 
					type FormChange
 | 
				
			||||||
    = NoFormChange
 | 
					    = NoFormChange
 | 
				
			||||||
    | TagChange ReferenceList
 | 
					    | AddTagChange ReferenceList
 | 
				
			||||||
 | 
					    | ReplaceTagChange ReferenceList
 | 
				
			||||||
 | 
					    | RemoveTagChange ReferenceList
 | 
				
			||||||
    | FolderChange (Maybe IdName)
 | 
					    | FolderChange (Maybe IdName)
 | 
				
			||||||
    | DirectionChange Direction
 | 
					    | DirectionChange Direction
 | 
				
			||||||
    | OrgChange (Maybe IdName)
 | 
					    | OrgChange (Maybe IdName)
 | 
				
			||||||
@@ -45,13 +47,27 @@ multiUpdate flags ids change receive =
 | 
				
			|||||||
            Set.toList ids
 | 
					            Set.toList ids
 | 
				
			||||||
    in
 | 
					    in
 | 
				
			||||||
    case change of
 | 
					    case change of
 | 
				
			||||||
        TagChange tags ->
 | 
					        ReplaceTagChange tags ->
 | 
				
			||||||
            let
 | 
					            let
 | 
				
			||||||
                data =
 | 
					                data =
 | 
				
			||||||
                    ItemsAndRefs items (List.map .id tags.items)
 | 
					                    ItemsAndRefs items (List.map .id tags.items)
 | 
				
			||||||
            in
 | 
					            in
 | 
				
			||||||
            Api.setTagsMultiple flags data receive
 | 
					            Api.setTagsMultiple flags data receive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AddTagChange tags ->
 | 
				
			||||||
 | 
					            let
 | 
				
			||||||
 | 
					                data =
 | 
				
			||||||
 | 
					                    ItemsAndRefs items (List.map .id tags.items)
 | 
				
			||||||
 | 
					            in
 | 
				
			||||||
 | 
					            Api.addTagsMultiple flags data receive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        RemoveTagChange tags ->
 | 
				
			||||||
 | 
					            let
 | 
				
			||||||
 | 
					                data =
 | 
				
			||||||
 | 
					                    ItemsAndRefs items (List.map .id tags.items)
 | 
				
			||||||
 | 
					            in
 | 
				
			||||||
 | 
					            Api.removeTagsMultiple flags data receive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        NameChange name ->
 | 
					        NameChange name ->
 | 
				
			||||||
            let
 | 
					            let
 | 
				
			||||||
                data =
 | 
					                data =
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user