mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-06 07:05:59 +00:00
Allow to change position of attachments
This commit is contained in:
parent
6807ce4d2f
commit
3cb738568f
@ -75,6 +75,8 @@ trait OItem[F[_]] {
|
|||||||
def findByFileSource(checksum: String, sourceId: Ident): F[Vector[RItem]]
|
def findByFileSource(checksum: String, sourceId: Ident): F[Vector[RItem]]
|
||||||
|
|
||||||
def deleteAttachment(id: Ident, collective: Ident): F[Int]
|
def deleteAttachment(id: Ident, collective: Ident): F[Int]
|
||||||
|
|
||||||
|
def moveAttachmentBefore(itemId: Ident, source: Ident, target: Ident): F[AddResult]
|
||||||
}
|
}
|
||||||
|
|
||||||
object OItem {
|
object OItem {
|
||||||
@ -121,6 +123,16 @@ object OItem {
|
|||||||
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
|
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
|
||||||
Resource.pure[F, OItem[F]](new OItem[F] {
|
Resource.pure[F, OItem[F]](new OItem[F] {
|
||||||
|
|
||||||
|
def moveAttachmentBefore(
|
||||||
|
itemId: Ident,
|
||||||
|
source: Ident,
|
||||||
|
target: Ident
|
||||||
|
): F[AddResult] =
|
||||||
|
store
|
||||||
|
.transact(QItem.moveAttachmentBefore(itemId, source, target))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def findItem(id: Ident, collective: Ident): F[Option[ItemData]] =
|
def findItem(id: Ident, collective: Ident): F[Option[ItemData]] =
|
||||||
store
|
store
|
||||||
.transact(QItem.findItem(id))
|
.transact(QItem.findItem(id))
|
||||||
|
@ -1239,6 +1239,30 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ItemProposals"
|
$ref: "#/components/schemas/ItemProposals"
|
||||||
|
/sec/item/{itemId}/attachment/movebefore:
|
||||||
|
post:
|
||||||
|
tags: [ Item ]
|
||||||
|
summary: Reorder attachments within an item
|
||||||
|
description: |
|
||||||
|
Moves the `source` attachment before the `target` attachment,
|
||||||
|
such that `source` becomes the immediate neighbor of `target`
|
||||||
|
with a lower position.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/itemId"
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/MoveAttachment"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
|
||||||
/sec/attachment/{id}:
|
/sec/attachment/{id}:
|
||||||
delete:
|
delete:
|
||||||
@ -1945,6 +1969,19 @@ paths:
|
|||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
|
MoveAttachment:
|
||||||
|
description: |
|
||||||
|
Data to move an attachment to another position.
|
||||||
|
required:
|
||||||
|
- source
|
||||||
|
- target
|
||||||
|
properties:
|
||||||
|
source:
|
||||||
|
type: string
|
||||||
|
format: ident
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
format: ident
|
||||||
ScanMailboxSettingsList:
|
ScanMailboxSettingsList:
|
||||||
description: |
|
description: |
|
||||||
A list of scan-mailbox tasks.
|
A list of scan-mailbox tasks.
|
||||||
|
@ -137,6 +137,14 @@ object ItemRoutes {
|
|||||||
resp <- Ok(ip)
|
resp <- Ok(ip)
|
||||||
} yield resp
|
} yield resp
|
||||||
|
|
||||||
|
case req @ POST -> Root / Ident(id) / "attachment" / "movebefore" =>
|
||||||
|
for {
|
||||||
|
data <- req.as[MoveAttachment]
|
||||||
|
_ <- logger.fdebug(s"Move item (${id.id}) attachment $data")
|
||||||
|
res <- backend.item.moveAttachmentBefore(id, data.source, data.target)
|
||||||
|
resp <- Ok(Conversions.basicResult(res, "Attachment moved."))
|
||||||
|
} yield resp
|
||||||
|
|
||||||
case DELETE -> Root / Ident(id) =>
|
case DELETE -> Root / Ident(id) =>
|
||||||
for {
|
for {
|
||||||
n <- backend.item.deleteItem(id, user.account.collective)
|
n <- backend.item.deleteItem(id, user.account.collective)
|
||||||
|
@ -72,12 +72,18 @@ case class Column(name: String, ns: String = "", alias: String = "") {
|
|||||||
def isGt[A: Put](a: A): Fragment =
|
def isGt[A: Put](a: A): Fragment =
|
||||||
f ++ fr"> $a"
|
f ++ fr"> $a"
|
||||||
|
|
||||||
|
def isGte[A: Put](a: A): Fragment =
|
||||||
|
f ++ fr">= $a"
|
||||||
|
|
||||||
def isGt(c: Column): Fragment =
|
def isGt(c: Column): Fragment =
|
||||||
f ++ fr">" ++ c.f
|
f ++ fr">" ++ c.f
|
||||||
|
|
||||||
def isLt[A: Put](a: A): Fragment =
|
def isLt[A: Put](a: A): Fragment =
|
||||||
f ++ fr"< $a"
|
f ++ fr"< $a"
|
||||||
|
|
||||||
|
def isLte[A: Put](a: A): Fragment =
|
||||||
|
f ++ fr"<= $a"
|
||||||
|
|
||||||
def isLt(c: Column): Fragment =
|
def isLt(c: Column): Fragment =
|
||||||
f ++ fr"<" ++ c.f
|
f ++ fr"<" ++ c.f
|
||||||
|
|
||||||
@ -103,4 +109,10 @@ case class Column(name: String, ns: String = "", alias: String = "") {
|
|||||||
|
|
||||||
def max: Fragment =
|
def max: Fragment =
|
||||||
fr"MAX(" ++ f ++ fr")"
|
fr"MAX(" ++ f ++ fr")"
|
||||||
|
|
||||||
|
def increment[A: Put](a: A): Fragment =
|
||||||
|
f ++ fr"=" ++ f ++ fr"+ $a"
|
||||||
|
|
||||||
|
def decrement[A: Put](a: A): Fragment =
|
||||||
|
f ++ fr"=" ++ f ++ fr"- $a"
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package docspell.store.queries
|
package docspell.store.queries
|
||||||
|
|
||||||
import bitpeace.FileMeta
|
import bitpeace.FileMeta
|
||||||
import cats.implicits._
|
|
||||||
import cats.effect.Sync
|
import cats.effect.Sync
|
||||||
|
import cats.data.OptionT
|
||||||
|
import cats.implicits._
|
||||||
import fs2.Stream
|
import fs2.Stream
|
||||||
import doobie._
|
import doobie._
|
||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
@ -16,6 +17,44 @@ import org.log4s._
|
|||||||
object QItem {
|
object QItem {
|
||||||
private[this] val logger = getLogger
|
private[this] val logger = getLogger
|
||||||
|
|
||||||
|
def moveAttachmentBefore(
|
||||||
|
itemId: Ident,
|
||||||
|
source: Ident,
|
||||||
|
target: Ident
|
||||||
|
): ConnectionIO[Int] = {
|
||||||
|
|
||||||
|
// rs < rt
|
||||||
|
def moveBack(rs: RAttachment, rt: RAttachment): ConnectionIO[Int] =
|
||||||
|
for {
|
||||||
|
n <- RAttachment.decPositions(itemId, rs.position, rt.position)
|
||||||
|
k <- RAttachment.updatePosition(rs.id, rt.position)
|
||||||
|
} yield n + k
|
||||||
|
|
||||||
|
// rs > rt
|
||||||
|
def moveForward(rs: RAttachment, rt: RAttachment): ConnectionIO[Int] =
|
||||||
|
for {
|
||||||
|
n <- RAttachment.incPositions(itemId, rt.position, rs.position)
|
||||||
|
k <- RAttachment.updatePosition(rs.id, rt.position)
|
||||||
|
} yield n + k
|
||||||
|
|
||||||
|
(for {
|
||||||
|
_ <- OptionT.liftF(
|
||||||
|
if (source == target)
|
||||||
|
Sync[ConnectionIO].raiseError(new Exception("Attachments are the same!"))
|
||||||
|
else ().pure[ConnectionIO]
|
||||||
|
)
|
||||||
|
rs <- OptionT(RAttachment.findById(source)).filter(_.itemId == itemId)
|
||||||
|
rt <- OptionT(RAttachment.findById(target)).filter(_.itemId == itemId)
|
||||||
|
n <- OptionT.liftF(
|
||||||
|
if (rs.position == rt.position || rs.position + 1 == rt.position)
|
||||||
|
0.pure[ConnectionIO]
|
||||||
|
else if (rs.position < rt.position) moveBack(rs, rt)
|
||||||
|
else moveForward(rs, rt)
|
||||||
|
)
|
||||||
|
} yield n).getOrElse(0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
case class ItemData(
|
case class ItemData(
|
||||||
item: RItem,
|
item: RItem,
|
||||||
corrOrg: Option[ROrganization],
|
corrOrg: Option[ROrganization],
|
||||||
|
@ -38,6 +38,20 @@ object RAttachment {
|
|||||||
fr"${v.id},${v.itemId},${v.fileId.id},${v.position},${v.created},${v.name}"
|
fr"${v.id},${v.itemId},${v.fileId.id},${v.position},${v.created},${v.name}"
|
||||||
).update.run
|
).update.run
|
||||||
|
|
||||||
|
def decPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] =
|
||||||
|
updateRow(
|
||||||
|
table,
|
||||||
|
and(itemId.is(iId), position.isGte(lowerBound), position.isLte(upperBound)),
|
||||||
|
position.decrement(1)
|
||||||
|
).update.run
|
||||||
|
|
||||||
|
def incPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] =
|
||||||
|
updateRow(
|
||||||
|
table,
|
||||||
|
and(itemId.is(iId), position.isGte(lowerBound), position.isLte(upperBound)),
|
||||||
|
position.increment(1)
|
||||||
|
).update.run
|
||||||
|
|
||||||
def nextPosition(id: Ident): ConnectionIO[Int] =
|
def nextPosition(id: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
max <- selectSimple(position.max, table, itemId.is(id)).query[Option[Int]].unique
|
max <- selectSimple(position.max, table, itemId.is(id)).query[Option[Int]].unique
|
||||||
|
Loading…
x
Reference in New Issue
Block a user