mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-30 21:05:08 +00:00
Merge pull request #148 from eikek/rename-attachments
Rename attachments
This commit is contained in:
commit
151e831b1c
@ -94,6 +94,12 @@ trait OItem[F[_]] {
|
||||
def deleteAttachment(id: Ident, collective: Ident): F[Int]
|
||||
|
||||
def moveAttachmentBefore(itemId: Ident, source: Ident, target: Ident): F[AddResult]
|
||||
|
||||
def setAttachmentName(
|
||||
attachId: Ident,
|
||||
name: Option[String],
|
||||
collective: Ident
|
||||
): F[AddResult]
|
||||
}
|
||||
|
||||
object OItem {
|
||||
@ -472,6 +478,16 @@ object OItem {
|
||||
|
||||
def deleteAttachment(id: Ident, collective: Ident): F[Int] =
|
||||
QAttachment.deleteSingleAttachment(store)(id, collective)
|
||||
|
||||
def setAttachmentName(
|
||||
attachId: Ident,
|
||||
name: Option[String],
|
||||
collective: Ident
|
||||
): F[AddResult] =
|
||||
store
|
||||
.transact(RAttachment.updateName(attachId, collective, name))
|
||||
.attempt
|
||||
.map(AddResult.fromUpdate)
|
||||
})
|
||||
} yield oitem
|
||||
}
|
||||
|
@ -1683,6 +1683,31 @@ paths:
|
||||
description: See Other
|
||||
200:
|
||||
description: Ok
|
||||
/sec/attachment/{id}/name:
|
||||
post:
|
||||
tags: [ Attachment ]
|
||||
summary: Changes the name of an attachment
|
||||
description: |
|
||||
Change the name of the attachment with the given id. The
|
||||
attachment must be part of an item that belongs to the
|
||||
collective of the current user.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/id"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/OptionalText"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
/sec/queue/state:
|
||||
get:
|
||||
tags: [ Job Queue ]
|
||||
|
@ -9,6 +9,7 @@ import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.headers._
|
||||
import org.http4s.headers.ETag.EntityTag
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.circe.CirceEntityDecoder._
|
||||
import docspell.backend.BackendApp
|
||||
import docspell.backend.auth.AuthToken
|
||||
import docspell.backend.ops.OItem
|
||||
@ -126,6 +127,13 @@ object AttachmentRoutes {
|
||||
resp <- md.map(Ok(_)).getOrElse(NotFound(BasicResult(false, "Not found.")))
|
||||
} yield resp
|
||||
|
||||
case req @ POST -> Root / Ident(id) / "name" =>
|
||||
for {
|
||||
nn <- req.as[OptionalText]
|
||||
res <- backend.item.setAttachmentName(id, nn.text, user.account.collective)
|
||||
resp <- Ok(Conversions.basicResult(res, "Name updated."))
|
||||
} yield resp
|
||||
|
||||
case DELETE -> Root / Ident(id) =>
|
||||
for {
|
||||
n <- backend.item.deleteAttachment(id, user.account.collective)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package docspell.store.records
|
||||
|
||||
import bitpeace.FileMeta
|
||||
import cats.implicits._
|
||||
import doobie._
|
||||
import doobie.implicits._
|
||||
import docspell.common._
|
||||
@ -89,6 +90,18 @@ object RAttachment {
|
||||
selectSimple(cols, from, cond).query[FileMeta].option
|
||||
}
|
||||
|
||||
def updateName(
|
||||
attachId: Ident,
|
||||
collective: Ident,
|
||||
aname: Option[String]
|
||||
): ConnectionIO[Int] = {
|
||||
val update = updateRow(table, id.is(attachId), name.setTo(aname)).update.run
|
||||
for {
|
||||
exists <- existsByIdAndCollective(attachId, collective)
|
||||
n <- if (exists) update else 0.pure[ConnectionIO]
|
||||
} yield n
|
||||
}
|
||||
|
||||
def findByIdAndCollective(
|
||||
attachId: Ident,
|
||||
collective: Ident
|
||||
@ -106,6 +119,20 @@ object RAttachment {
|
||||
def findByItem(id: Ident): ConnectionIO[Vector[RAttachment]] =
|
||||
selectSimple(all, table, itemId.is(id)).query[RAttachment].to[Vector]
|
||||
|
||||
def existsByIdAndCollective(
|
||||
attachId: Ident,
|
||||
collective: Ident
|
||||
): ConnectionIO[Boolean] = {
|
||||
val aId = id.prefix("a")
|
||||
val aItem = itemId.prefix("a")
|
||||
val iId = RItem.Columns.id.prefix("i")
|
||||
val iColl = RItem.Columns.cid.prefix("i")
|
||||
val from =
|
||||
table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId)
|
||||
val cond = and(iColl.is(collective), aId.is(attachId))
|
||||
selectCount(id, from, cond).query[Int].unique.map(_ > 0)
|
||||
}
|
||||
|
||||
def findByItemAndCollective(
|
||||
id: Ident,
|
||||
coll: Ident
|
||||
|
@ -61,6 +61,7 @@ module Api exposing
|
||||
, refreshSession
|
||||
, register
|
||||
, sendMail
|
||||
, setAttachmentName
|
||||
, setCollectiveSettings
|
||||
, setConcEquip
|
||||
, setConcPerson
|
||||
@ -1061,6 +1062,21 @@ getJobQueueStateTask flags =
|
||||
--- Item
|
||||
|
||||
|
||||
setAttachmentName :
|
||||
Flags
|
||||
-> String
|
||||
-> Maybe String
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
setAttachmentName flags attachId newName receive =
|
||||
Http2.authPost
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/attachment/" ++ attachId ++ "/name"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.OptionalText.encode (OptionalText newName))
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
}
|
||||
|
||||
|
||||
moveAttachmentBefore :
|
||||
Flags
|
||||
-> String
|
||||
|
@ -98,6 +98,7 @@ type alias Model =
|
||||
, loading : Set String
|
||||
, attachDD : DD.Model String String
|
||||
, modalEdit : Maybe Comp.DetailEdit.Model
|
||||
, attachRename : Maybe AttachmentRename
|
||||
}
|
||||
|
||||
|
||||
@ -107,6 +108,12 @@ type NotesField
|
||||
| HideNotes
|
||||
|
||||
|
||||
type alias AttachmentRename =
|
||||
{ id : String
|
||||
, newName : String
|
||||
}
|
||||
|
||||
|
||||
isEditNotes : NotesField -> Bool
|
||||
isEditNotes field =
|
||||
case field of
|
||||
@ -185,6 +192,7 @@ emptyModel =
|
||||
, loading = Set.empty
|
||||
, attachDD = DD.init
|
||||
, modalEdit = Nothing
|
||||
, attachRename = Nothing
|
||||
}
|
||||
|
||||
|
||||
@ -255,6 +263,11 @@ type Msg
|
||||
| StartConcPersonModal
|
||||
| StartEquipModal
|
||||
| CloseModal
|
||||
| EditAttachNameStart String
|
||||
| EditAttachNameCancel
|
||||
| EditAttachNameSet String
|
||||
| EditAttachNameSubmit
|
||||
| EditAttachNameResp (Result Http.Error BasicResult)
|
||||
|
||||
|
||||
|
||||
@ -543,7 +556,14 @@ update key flags next msg model =
|
||||
)
|
||||
|
||||
SetActiveAttachment pos ->
|
||||
noSub ( { model | visibleAttach = pos, sentMailsOpen = False }, Cmd.none )
|
||||
noSub
|
||||
( { model
|
||||
| visibleAttach = pos
|
||||
, sentMailsOpen = False
|
||||
, attachRename = Nothing
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleMenu ->
|
||||
noSub ( { model | menuOpen = not model.menuOpen }, Cmd.none )
|
||||
@ -1303,6 +1323,92 @@ update key flags next msg model =
|
||||
CloseModal ->
|
||||
noSub ( { model | modalEdit = Nothing }, Cmd.none )
|
||||
|
||||
EditAttachNameStart id ->
|
||||
case model.attachRename of
|
||||
Nothing ->
|
||||
let
|
||||
name =
|
||||
Util.List.find (\el -> el.id == id) model.item.attachments
|
||||
|> Maybe.map (\el -> Maybe.withDefault "" el.name)
|
||||
in
|
||||
case name of
|
||||
Just n ->
|
||||
noSub ( { model | attachRename = Just (AttachmentRename id n) }, Cmd.none )
|
||||
|
||||
Nothing ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
Just _ ->
|
||||
noSub ( { model | attachRename = Nothing }, Cmd.none )
|
||||
|
||||
EditAttachNameCancel ->
|
||||
noSub ( { model | attachRename = Nothing }, Cmd.none )
|
||||
|
||||
EditAttachNameSet str ->
|
||||
case model.attachRename of
|
||||
Just m ->
|
||||
noSub
|
||||
( { model | attachRename = Just { m | newName = str } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
EditAttachNameSubmit ->
|
||||
let
|
||||
editId =
|
||||
Maybe.map .id model.attachRename
|
||||
|
||||
name =
|
||||
Util.List.find (\el -> Just el.id == editId) model.item.attachments
|
||||
|> Maybe.map (\el -> Maybe.withDefault "" el.name)
|
||||
|
||||
ma =
|
||||
Util.Maybe.filter (\m -> Just m.newName /= name) model.attachRename
|
||||
in
|
||||
case ma of
|
||||
Just m ->
|
||||
noSub
|
||||
( model
|
||||
, Api.setAttachmentName
|
||||
flags
|
||||
m.id
|
||||
(Util.Maybe.fromString m.newName)
|
||||
EditAttachNameResp
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
noSub ( { model | attachRename = Nothing }, Cmd.none )
|
||||
|
||||
EditAttachNameResp (Ok res) ->
|
||||
case model.attachRename of
|
||||
Just m ->
|
||||
let
|
||||
changeName a =
|
||||
if a.id == m.id then
|
||||
{ a | name = Util.Maybe.fromString m.newName }
|
||||
|
||||
else
|
||||
a
|
||||
|
||||
changeItem i =
|
||||
{ i | attachments = List.map changeName i.attachments }
|
||||
in
|
||||
noSub
|
||||
( { model
|
||||
| attachRename = Nothing
|
||||
, item = changeItem model.item
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
EditAttachNameResp (Err _) ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
|
||||
|
||||
--- View
|
||||
@ -1568,23 +1674,47 @@ renderAttachmentsTabMenu model =
|
||||
(List.indexedMap
|
||||
(\pos ->
|
||||
\el ->
|
||||
a
|
||||
([ classList <|
|
||||
[ ( "item", True )
|
||||
, ( "active", attachmentVisible model pos )
|
||||
if attachmentVisible model pos then
|
||||
a
|
||||
([ classList <|
|
||||
[ ( "active item", True )
|
||||
]
|
||||
++ highlight el
|
||||
, title (Maybe.withDefault "No Name" el.name)
|
||||
, href ""
|
||||
]
|
||||
++ DD.draggable AttachDDMsg el.id
|
||||
++ DD.droppable AttachDDMsg el.id
|
||||
)
|
||||
[ Maybe.map (Util.String.ellipsis 30) el.name
|
||||
|> Maybe.withDefault "No Name"
|
||||
|> text
|
||||
, a
|
||||
[ class "right-tab-icon-link"
|
||||
, href "#"
|
||||
, onClick (EditAttachNameStart el.id)
|
||||
]
|
||||
[ i [ class "grey edit link icon" ] []
|
||||
]
|
||||
]
|
||||
|
||||
else
|
||||
a
|
||||
([ classList <|
|
||||
[ ( "item", True )
|
||||
]
|
||||
++ highlight el
|
||||
, title (Maybe.withDefault "No Name" el.name)
|
||||
, href ""
|
||||
, onClick (SetActiveAttachment pos)
|
||||
]
|
||||
++ DD.draggable AttachDDMsg el.id
|
||||
++ DD.droppable AttachDDMsg el.id
|
||||
)
|
||||
[ Maybe.map (Util.String.ellipsis 20) el.name
|
||||
|> Maybe.withDefault "No Name"
|
||||
|> text
|
||||
]
|
||||
++ highlight el
|
||||
, title (Maybe.withDefault "No Name" el.name)
|
||||
, href ""
|
||||
, onClick (SetActiveAttachment pos)
|
||||
]
|
||||
++ DD.draggable AttachDDMsg el.id
|
||||
++ DD.droppable AttachDDMsg el.id
|
||||
)
|
||||
[ Maybe.map (Util.String.ellipsis 20) el.name
|
||||
|> Maybe.withDefault "No Name"
|
||||
|> text
|
||||
]
|
||||
)
|
||||
model.item.attachments
|
||||
++ mailTab
|
||||
@ -1611,6 +1741,7 @@ renderAttachmentView settings model pos attach =
|
||||
]
|
||||
]
|
||||
[ Html.map (DeleteAttachConfirm attach.id) (Comp.YesNoDimmer.view model.deleteAttachConfirm)
|
||||
, renderEditAttachmentName model attach
|
||||
, div [ class "ui small secondary menu" ]
|
||||
[ div [ class "horizontally fitted item" ]
|
||||
[ i [ class "file outline icon" ] []
|
||||
@ -2243,3 +2374,36 @@ renderFileItem model file =
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
renderEditAttachmentName : Model -> Attachment -> Html Msg
|
||||
renderEditAttachmentName model attach =
|
||||
let
|
||||
am =
|
||||
Util.Maybe.filter (\m -> m.id == attach.id) model.attachRename
|
||||
in
|
||||
case am of
|
||||
Just m ->
|
||||
div [ class "ui fluid action input" ]
|
||||
[ input
|
||||
[ type_ "text"
|
||||
, value m.newName
|
||||
, onInput EditAttachNameSet
|
||||
]
|
||||
[]
|
||||
, button
|
||||
[ class "ui primary icon button"
|
||||
, onClick EditAttachNameSubmit
|
||||
]
|
||||
[ i [ class "check icon" ] []
|
||||
]
|
||||
, button
|
||||
[ class "ui secondary icon button"
|
||||
, onClick EditAttachNameCancel
|
||||
]
|
||||
[ i [ class "delete icon" ] []
|
||||
]
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
span [ class "invisible hidden" ] []
|
||||
|
@ -1,5 +1,6 @@
|
||||
module Util.Maybe exposing
|
||||
( fromString
|
||||
( filter
|
||||
, fromString
|
||||
, isEmpty
|
||||
, nonEmpty
|
||||
, or
|
||||
@ -52,3 +53,17 @@ fromString str =
|
||||
|
||||
else
|
||||
Just str
|
||||
|
||||
|
||||
filter : (a -> Bool) -> Maybe a -> Maybe a
|
||||
filter predicate ma =
|
||||
case ma of
|
||||
Just v ->
|
||||
if predicate v then
|
||||
Just v
|
||||
|
||||
else
|
||||
Nothing
|
||||
|
||||
Nothing ->
|
||||
Nothing
|
||||
|
@ -83,6 +83,11 @@
|
||||
padding: 0.8em;
|
||||
}
|
||||
|
||||
.default-layout .menu .item.active a.right-tab-icon-link {
|
||||
position: relative;
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
.markdown-preview {
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
|
Loading…
x
Reference in New Issue
Block a user