Merge pull request #148 from eikek/rename-attachments

Rename attachments
This commit is contained in:
mergify[bot] 2020-06-14 15:08:40 +00:00 committed by GitHub
commit 151e831b1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 294 additions and 18 deletions

View File

@ -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
}

View File

@ -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 ]

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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" ] []

View File

@ -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

View File

@ -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;